Skip to content

context-graph: serve recall to Claude Code and Codex over MCP - #426

Merged
antejavor merged 1 commit into
mainfrom
feat/418-mcp
Oct 5, 2026
Merged

antejavor merged 1 commit into
mainfrom
feat/418-mcp

Conversation

@antejavor

Copy link
Copy Markdown
Contributor

Closes #418. Part of #390; implements #394 points 1, 2, 4 and 9.

What

  • Tool surface in agent-context-graph: components register tools under a new agent_context_graph.tools entry-point group (tools.py).
    • agent-context-graph mcp serves them over stdio MCP, behind a new mcp extra.
    • agent-context-graph recall "<question>" [--json] runs recall from a shell.
  • recall(question) in sessions-graph (sessions_graph/tool.py): the user and the [recall] lane and width overrides come from config.toml, never from the call, so a model can only read its own user's memory.
  • SessionStart: one line of context saying the tool exists, added only when sessions-graph is enabled. No retrieved content is pushed, and if a tool fails to load the hook still succeeds.
  • Plugins: Claude Code and Codex bundle the server through .mcp.json, and both bootstrap.sh scripts install agent-context-graph[mcp].
  • Config and doctor: config set and bootstrap now keep hand-edited [recall] keys when they rewrite the file, and doctor checks that the server can start and serves recall.
  • Docs: a Recall section in the agent-context-graph README (with the overrides table), Codex specifics in its plugin README, a CONTEXT.md glossary entry, and AGENTS.md.

Change from #394 point 4: results are text only

#394 chose "text for the model, JSON beside it". Checked end to end, that choice backfires: when a result has both, Claude Code and Codex both show the model the structuredContent JSON instead of the text. Codex's source does this explicitly (as_function_call_output_payload prefers structured_content), and Claude Code was observed sending the model the JSON. Sending both meant the model never saw the benchmarked text. The MCP result now carries only the text; the JSON is printed by recall --json.

Checked and written down (#418 "done when")

Claude Code 2.1.x Codex 0.158
Text + structuredContent Model sees the JSON only Model sees the JSON only (text dropped)
Plugin bundles an MCP server .mcp.json at the plugin root; tool appears as mcp__plugin_context-graph_context-graph__recall "mcpServers": "./.mcp.json" in .codex-plugin/plugin.json
SessionStart additionalContext Injected ("provided additionalContext (203 chars)") Injected; the model called recall without being told about memory

Codex specifics, now in the plugin README:

  • Plugin hooks run only after the user trusts them (startup review or /hooks); codex exec never prompts.
  • Each recall call asks for approval unless default_tools_approval_mode = "approve" is set.
  • MCP servers don't inherit the environment, so the Codex .mcp.json forwards CONTEXT_GRAPH_CONFIG via env_vars.

End-to-end runs

Both runs used a fresh MAGE Memgraph and the worktree plugins.

  • Claude Code: session 1 told the model a fact and was recorded by the hooks; the session-end embed ran (embedding_status = completed). In session 2 the model called recall and answered correctly from the text result.
  • Codex: with hooks and the tool allowed, the model called recall without being prompted and answered from the Claude Code session's memory.

Tests

  • agent-context-graph: 122 passed. New test_tools.py covers the MCP server through an in-memory client, the CLI, the SessionStart hint, and [recall] surviving rewrites.
  • sessions-graph: 93 passed, 1 skipped. New test_e2e_recall_tool.py runs against a real Memgraph: own user only, config widths, refusals, and registration.
  • ruff and ty are clean.

Not in this PR

agent-context-graph gains a tool surface: components register tools under
the new `agent_context_graph.tools` entry point, `agent-context-graph mcp`
serves them over stdio MCP (new `mcp` extra), and `agent-context-graph
recall [--json]` runs recall from a shell. sessions-graph registers
`recall(question)`; the user and `[recall]` overrides come from the config
file only, never from the call.

At SessionStart the hook adds one line telling the model the tool exists.
Both plugins bundle the server via `.mcp.json`, and bootstrap installs the
`mcp` extra. `config set`/bootstrap now keep `[recall]` widths when they
rewrite the config, and `doctor` checks the server.

Results carry text only: verified end to end that Claude Code and Codex
both show the model structuredContent instead of the text when both are
sent, and the text is the benchmarked form. JSON stays in `--json`.

Closes #418
@antejavor
antejavor merged commit dd273e1 into main Oct 5, 2026
25 checks passed
antejavor added a commit that referenced this pull request Oct 5, 2026
Resolve the claude_code/codex adapter conflicts with #427 by keeping this
branch's declarative adapters and carrying #427's reply rules into the
shared turn_end rule: a blank reply records nothing, and the reply's text
is not duplicated into its metadata. #427's tests now expect a turn end,
not a session end, after the reply.

main's sessions-graph spawns the no-LLM `embed` step on SessionEnd (#422),
which Codex recall (#426, #427) relied on via the per-turn Stop. Since a
Stop is now a TurnEnd, sessions-graph spawns `embed` on TurnEnd too, so
every runtime's turns are embedded as they end, including runtimes with no
session-end hook. LLM reconciliation stays on SessionEnd only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

agent-context-graph: MCP server with the recall tool

1 participant