muse-and-mcp.md markdown
340 lines 10.7 KB
Raw
sha256:e1ceec85262dce5ebb3d317013964d640c04718f0757da58dd391c80ff1c6a93 Move Episode 16 script into place Sonnet 5 3 hours ago

Episode 16 --- Muse + MCP

Working YouTube title:
The Onboarding Instructions Lied To The Agent That Received Them

Thumbnail thought:
2141 lines of SQL. one real answer.

Target runtime: ~9:00


[0:00--0:20] COLD OPEN

[CAMERA --- Episode 15's closing line, on screen: "what happens when the thing talking to Muse isn't a terminal at all."]

GABRIEL:

Every episode so far, I've been the terminal. From here on, imagine the thing typing these commands is a language model, talking over a real protocol, not a human copying and pasting. That protocol is MCP.

[TITLE CARD --- fast]

MUSE + MCP

[Music enters.]


[0:20--1:10] A REAL SERVER, NOT A TOY

[TERMINAL]

$ head -20 musehub/docs/reference/mcp.md
MuseHub MCP Reference
Protocol version: 2025-11-25 | pure-Python async, no external MCP SDK
Tools — 40 total | Resources — 29 total | Prompts — 10 total

GABRIEL VO:

Full JSON-RPC 2.0. Streamable HTTP with SSE push. A stdio transport for local dev. Elicitation --- the server can pause mid-tool-call and ask a human a question. This isn't a wrapper around the REST API. It's real infrastructure. Let's drive it for real.


[1:10--2:40] THE DOCUMENTED PATH, BROKEN

[TERMINAL --- exactly the Cursor IDE example from the docs]

$ echo '{"jsonrpc":"2.0","id":1,"method":"initialize",...}' \
  | docker exec -i musehub python3 -m musehub.mcp.stdio_server
2026-09-12 20:15:44,321 INFO sqlalchemy.engine.Engine select pg_catalog.version()
2026-09-12 20:15:44,321 INFO sqlalchemy.engine.Engine [raw sql] ()
2026-09-12 20:15:44,322 INFO sqlalchemy.engine.Engine select current_schema()
... (2138 more lines like this)

GABRIEL:

That's stdout. The docs say --- word for word --- "writes JSON-RPC 2.0 responses to stdout... logs diagnostic messages to stderr." The real response is buried somewhere in twenty-one hundred lines of raw SQL debug text. Any actual MCP client parsing this line by line fails almost immediately.


[2:40--3:40] FINDING THE TRIGGER

[CAMERA]

$ docker exec musehub python3 -c "from musehub.config import settings; print(settings.debug)"
True

GABRIEL VO:

This container's own running config --- the actual local dev setup this project uses --- has debug mode on. musehub/db/database.py does create_async_engine(..., echo=settings.debug). stdio_server.py itself correctly points its own logging at stderr. Somewhere between that correct intention and the SQL engine's own logging, debug mode wins and lands on stdout instead.

$ echo '...' | docker exec -i -e DEBUG=false musehub python3 -m musehub.mcp.stdio_server
{"jsonrpc": "2.0", "id": 1, "result": {"protocolVersion": "2025-11-25", ...}}

GABRIEL:

One line. Exactly one. DEBUG=false and the transport is perfectly clean. That's the confirmed trigger --- filed as staging#208 with everything I found, including what I couldn't pin down: the exact mechanism by which the echo lands on stdout instead of SQLAlchemy's normal stderr default. That part's still open.


[3:40--4:30] THE INSTRUCTIONS THE SERVER GIVES ITSELF

[TERMINAL]

$ ... | docker exec -i -e DEBUG=false musehub python3 -m musehub.mcp.stdio_server
{ "result": { "instructions": "MuseHub MCP — Agent Quick Start\n\nSTEP 1 — FOCUS YOUR SESSION:\n  musehub_set_context(owner, slug)\n  All repo-scoped tools then inherit owner/slug automatically...\n\nSTEP 4 — MULTI-AGENT COORDINATION:\n  ..." } }

GABRIEL VO:

This is what an agent gets the moment it connects --- a full onboarding guide, generated by the server itself, telling it exactly how to use Muse's coordination primitives from Episode 14 through this exact protocol. Genuinely well thought out. Let's follow step one.


[4:30--5:50] STEP ONE, AS WRITTEN, FAILS

[TERMINAL]

$ musehub_set_context(owner="gabriel", slug="build-with-muse")
{ "focused": true, "message": "Session is now focused on gabriel/build-with-muse. Subsequent tool calls... will use this repo automatically." }
$ musehub_read_context()
{ "error_code": "repo_not_found", "error_message": "Repository '' not found." }

[beat]

GABRIEL:

The exact next call the server itself told the agent to make. Empty string where the focused repo should be. The session focus write succeeded; the very next read never sees it.

$ musehub_read_context(owner="gabriel", slug="build-with-muse")
{ "context": { "repo": { "name": "build-with-muse", ... }, "branches": [...] } }

GABRIEL VO:

Pass it explicitly and it works perfectly --- full repo context, branches, recent commits, everything. So the read path is fine. Only the "remember what I focused on" part is broken. Filed as staging#210.


[5:50--6:30] THE COUNT THAT'S JUST WRONG

[TERMINAL]

$ tools/list, resources/list, prompts/list
tools: 125       (docs claim 40)
resources: 9      (docs claim 29)
prompts: 11       (docs claim 10)

GABRIEL:

The docs undercount the tool surface by more than three times. Not a functional bug --- the server has more capability than advertised, if anything --- but it's real drift, filed as staging#209, and it's the kind of thing that erodes trust in every OTHER number in a reference doc once you catch one this far off.


[6:30--7:50] AND THEN IT ACTUALLY WORKS

[CAMERA]

Three findings in one episode. Let's end on what this protocol actually delivers when you use the documented workaround.

[TERMINAL]

$ musehub_create_issue(owner="gabriel", slug="mcp-episode16-demo",
    title="Real issue filed by an LLM over MCP",
    body="Created live during Episode 16 recording...")
{ "issue_id": "sha256:c0fea7...", "number": 1, "author": "stdio-user", "state": "open" }
$ musehub_list_issues(owner="gabriel", slug="mcp-episode16-demo")
{ "total": 1, "issues": [{ "title": "Real issue filed by an LLM over MCP", "author": "stdio-user" }] }

GABRIEL VO:

A real write, through a real JSON-RPC tool call, landing in the same database every other episode's CLI commands have been writing to all season. author: "stdio-user" --- exactly what the docs promise for the trusted local process. This part is exactly as solid as it looks.


[7:50--8:30] THE PATTERN ACROSS THIS EPISODE

[CAMERA]

Three issues, three different shapes: a transport-corrupting misconfiguration interaction, a session-state bug in the exact workflow the server recommends to itself, and a documentation count that's stale by 3x. None of them touch the core write path, which works exactly as advertised. That's oddly the most useful thing this episode found --- the actual read/write mechanics are sound; the seams around them --- transport hygiene, session lifecycle, docs --- are where the real risk concentrates.


[8:30--9:00] OUT

[TERMINAL --- fading to black]

GABRIEL VO:

Everything this season has assumed HTTP or stdio, request and response. Next: what actually goes over the wire during a push or a fetch, at the byte level --- the layer underneath all of it.

[beat]

That's next.

[CUT TO BLACK]

musehub.ai


Production Notes

Episode 16 has three findings of genuinely different character, and the risk is the audience flattening them into "MCP is broken." Keep the closing beat (8:30) doing real work: transport hygiene, session lifecycle, and doc staleness are three different failure classes, and the actual tool-calling mechanics --- the thing MCP exists to do --- work correctly. Say that distinction plainly.

Show The Onboarding Instructions Verbatim

The initialize response's instructions field is real, substantial, and worth reading almost in full on screen --- it's the single best piece of evidence this episode has for "Muse takes agents seriously as a client," which makes the Step-1 failure land harder immediately after, not softer.

DEBUG=true Is Not A Contrived Edge Case

Emphasize that DEBUG=true is this project's OWN actual running local dev container's setting, not a deliberately misconfigured demo. The exact audience the stdio docs target (local dev, Cursor IDE) is the audience most likely to be running with debug on by default.

Ticket Discipline, Three Different Severities

staging#208 (transport corruption) is the most severe of this episode's three and should read that way. staging#210 (session context) is real but has a full working fallback. staging#209 (doc counts) is the lowest stakes --- explicitly filed as a documentation ticket, not a bug, per the standing "fix stale docs" discipline, scoped down to "track the fuller rewrite" rather than attempting to hand-fix 125 tools' worth of tables in this session.

Everything Here Is Real

Every JSON-RPC exchange in this episode was run against the actual running local musehub + musehub_worker containers via docker exec, using the real stdio transport, not a mock. The mcp-episode16-demo repo is a real, disposable local-hub repo created and cleaned up idempotently by the demo script. Re-run make-mcp-episode16-demo.sh at record time; if any of #208/#209/ #210 have been fixed, the corresponding part needs to show the fix landing rather than silently keep narrating a resolved bug.

The Seed

The viewer arrives thinking:

40 tools, JSON-RPC, elicitation --- solid MCP integration, standard stuff for a modern dev platform.

They should leave thinking:

The actual tool-calling core is more capable than documented and works exactly as promised. Everything AROUND it --- how it starts up, how it remembers what it's focused on, how accurately it describes itself --- has real seams. Protocol compliance and protocol reliability turned out to be two different audits.

That's the exact posture the wire-protocol episode needs next --- zooming in one more layer, from "does the tool call work" to "what actually crosses the wire when it does."

File History 1 commit
sha256:e1ceec85262dce5ebb3d317013964d640c04718f0757da58dd391c80ff1c6a93 Move Episode 16 script into place Sonnet 5 3 hours ago