feat(mcp): session-scoped notification delivery and wire-format negotiation#273
feat(mcp): session-scoped notification delivery and wire-format negotiation#273vitaliyslion wants to merge 1 commit into
Conversation
|
On the Once this architecture is approved, the follow-up is to flip the model around: have each of the currently-allowlisted clients declare Pi's own bridge (next PR) will be the first client built against this from day one — self-declaring instead of getting a server-side entry — so it'll double as the reference implementation for migrating the rest. |
|
Hey @vitaliyslion,
The capability placement is hard to change later, and it's a one-door decision. According to the change, the client declares Also, such a change requires a documentation actualisation for a public protocol extension. docs/mcp.md:70 documents the client-name allowlist, but nothing anywhere mentions |
Summary
Daemon-socket MCP sessions (the transport used by
gortex mcpproxying and bridge-style clients like Pi) were never registered with mcp-go's internal client-session tracking, so any server-initiated notification —tools/list_changedafter atools_searchpromotion,resources/updated, etc. — was silently dropped for them. This PR wires those sessions in so pushes actually reach the client, and adds a capability-based mechanism for clients to self-declare their compact wire-format support instead of relying solely on a server-side client-name allowlist.Changes
SessionStartedHook(internal/daemon/server.go): an optional hookMCPDispatcherimplementations can satisfy to receive awrite func([]byte) errorcallback per connection, invoked after the handshake ack and before the first frame is dispatched. Server-initiated frames now share the connection under one write lock so they never interleave with reply frames.ConnectSession/DisconnectSession/WithClientSession(internal/mcp/conn_session.go, new): registers a daemon-socket connection as a live mcp-go client session soSendNotificationToAllClients(which mcp-go gates on session-initialized state) can actually deliver to it.cmd/gortex/daemon_mcp.go:mcpDispatchernow implementsSessionStarted/SessionEndedto connect/disconnect the session, and attaches the client session to the request context viaWithClientSessionbefore dispatch.gortex/wireMCP capability: a client can declare its supported compact formats duringinitialize(capabilities["gortex/wire"]: ["gcx", "toon"], in preference order).maybeSnoopInitializeparses it and calls the newServer.NoteSessionWireFormats;resolveSessionFormatprefers this declaration over the hardcodeddefaultFormatForClientname allowlist, so new clients get compact output without a server-side code change.internal/mcp/server.go: addedsessionState.wireFormats+recordWireFormats/snapshotWireFormats,NoteSessionWireFormats, andformatFromWireCapability(maps the declared list to the first format this server can emit, skipping unknown/future format names).Testing
Checklist