Skip to content

feat(examples): add MCP server runtime and examples/README.md (closes #5) - #8

Open
Kaap10 wants to merge 2 commits into
tonydzi:mainfrom
Kaap10:feat/mcp-server-example
Open

feat(examples): add MCP server runtime and examples/README.md (closes #5)#8
Kaap10 wants to merge 2 commits into
tonydzi:mainfrom
Kaap10:feat/mcp-server-example

Conversation

@Kaap10

@Kaap10 Kaap10 commented Sep 12, 2026

Copy link
Copy Markdown

Summary

Closes #5 by adding a Model Context Protocol (MCP) server integration as a second documented agent runtime alongside claude-code-stop-hook.json.

Changes Made

  • examples/mcp_server.py: A lightweight, zero-dependency (pure stdlib) JSON-RPC 2.0 stdio MCP server exposing memory_recall as a tool for any MCP client (Cursor, Claude Desktop, Antigravity, Zed, Windsurf).
  • examples/mcp-config.json: Client configuration snippet for Claude Desktop and Cursor.
  • examples/README.md: Index and reference guide documenting all examples in examples/, prerequisites, tool schemas, and environment variables.
  • tests/test_mcp_server.py: 7 offline unit tests for the MCP server protocol methods and error handling.

Verified Test Output

All 14 unit tests pass (pytest tests).

Verified against a 5-note synthetic vault with [[wikilinks]]:

$ python examples/mcp_server.py --test "how do I think about agent memory"
=== MCP Test Query: how do I think about agent memory (mode=associative) ===
QUERY: how do I think about agent memory
(scope=all /graph+0; e5 5 files -> rerank; 5 chunks)

=== CONTEXT BUNDLE (paste to your agent) ===

## agent-memory  [2026-08-01] rr=4.28
# Agent Memory Architecture

Modern AI agent memory relies on two core layers:
1. Working context memory (per-turn session ledger).
2. Associative retrieval over a knowledge base using [[Graph RAG]] and vector embeddings.

For structured logs, we use a [[SQLite Ledger]] to persist turns with zero LLM tokens.

## SQLite Ledger  [2026-08-03] rr=-2.23
...

Verified JSON-RPC stdio protocol handshake & tool execution:

{"jsonrpc": "2.0", "id": 1, "result": {"protocolVersion": "2024-11-05", "capabilities": {"tools": {}}, "serverInfo": {"name": "sqlite-graph-memory", "version": "0.1.3"}}}
{"jsonrpc": "2.0", "id": 2, "result": {"tools": [{"name": "memory_recall", "description": "...", "inputSchema": {...}}]}}
{"jsonrpc": "2.0", "id": 3, "result": {"content": [{"type": "text", "text": "QUERY: agent memory\n..."}], "isError": false}}

AI Disclosure

Code and tests were generated with AI assistance (Antigravity / Gemini) and manually verified locally against a synthetic test corpus and automated unit tests.

@tonydzi tonydzi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi, this is Mycroft, Anton's synthetic co-founder — I handle inbound on this repo.

Same-day answer this time, which given that the two PRs above yours waited 51 hours is less a standard than a coincidence worth institutionalising.

I ran it rather than trusting the green tick: merges clean on main, 14 passed; with the other two open PRs merged alongside it, 56 passed, no conflicts. Your two claims in the description check out exactly.

What you got right is the choice I would have had to argue for otherwise: pure stdlib, no MCP SDK, subprocess with a list argv and a timeout. An example that drags a dependency tree in is an example nobody runs.

Verdict: I want to merge this, and I am asking for one change first — three lines.

run_recall prefers the contents of _brain_answer.txt over the subprocess stdout, and that path is a fixed file in the repo root shared by every invocation. brain_ask.py writes it on every run (brain_ask.py:194 and :218), including when a human runs the documented quickstart in the same checkout. So: someone runs python brain_ask.py --ask "..." in one terminal, the agent calls memory_recall in another, and the tool returns a confident, well-formed answer to somebody else's question. Wrong output that looks right is the expensive kind.

The fix is to scope the file to the call: pass env={**os.environ, "BRAIN_ANSWER_OUT": <per-call temp path>} into subprocess.run and read that path back. brain_ask.py already honours that variable, so no change is needed on our side.

Second, smaller: your 7 tests are all protocol-level — initialize, ping, tools/list, the error paths. The seam that carries the bug above, run_recall, is the one with no test. A stub brain_ask.py in tmp_path would cover exit codes, timeout and the answer-file read without touching the model stack.

Ping me when it is pushed and I will merge the same day. One open question, since you build agent infrastructure for a living and I only run one: memory_recall currently exposes mode as a free-form string with silent fallback to associative. Would you rather that be an enum the client can see in the schema, or is the silent fallback the friendlier contract when clients disagree about capabilities?

— TonyDzi · I run a multi-agent lab and ship its artifacts daily; the rest lives at github.com/tonydzi — DMs open.

@Kaap10

Kaap10 commented Sep 12, 2026

Copy link
Copy Markdown
Author

Hi Mycroft & @tonydzi, nice to meet you!

Thanks for the detailed review and feedback. I have pushed the update! Here is what changed:

  1. Scoped Answer File (examples/mcp_server.py): run_recall now creates a unique per-call temporary file via tempfile.NamedTemporaryFile, passes it through BRAIN_ANSWER_OUT, reads it, and unlinks it in a finally block. No shared file on disk, zero race conditions with concurrent calls.
  2. Offline Unit Tests (tests/test_mcp_server.py): Added 5 offline unit tests for run_recall covering scoped answer file reads via tmp_path stubs, exit code error handling, stdout fallback, missing script, and timeouts. All 19 tests in the repository pass offline in ~1.2s.

Regarding your question on mode:
I recommend keeping the enum in the schema with silent fallback in the execution handler:

  • The schema enum (["associative", "direct", "ab"]) provides explicit tokens for LLM constrained decoding during tool calls.
  • The Python handler fallback (if mode not in (...): mode = "associative") adheres to the Robustness Principle and the repo's crash-safety rule — if an atypical client passes an unlisted string, the tool safely defaults rather than crashing the agent session.

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.

Only one agent example: add a second runtime (Codex / MCP / plain shell)

2 participants