feat: support mcp 2.x (#92) - #93
Open
sleeyax wants to merge 2 commits into
Open
Conversation
mcp 2.0.0 replaced the low-level Server's @list_tools() / @call_tool() decorators with constructor-based handler registration, so build_app() raised AttributeError at import and the server never served a request. Handlers are now plain (ctx, params) coroutines passed as on_list_tools / on_call_tool, returning ListToolsResult / CallToolResult. The 2.x low-level server drops two things its 1.x decorators did on a handler's behalf, both of which the client-visible contract depends on, so call_tool now does them itself: a failed tool call is returned as an is_error result carrying the message (a raised exception would reach the client scrubbed to "Internal server error"), and arguments are validated against the tool's schema. The resulting error strings are unchanged from 1.8.0. Tool descriptions move to the snake_case field spelling (input_schema) that 2.x exposes for attribute access; the camelCase wire form is unaffected. Adds an end-to-end test that drives a real ClientSession against the real Server over in-memory streams, covering initialize, tools/list and tools/call — the wiring this issue broke, which no test previously touched.
update_page in replace mode minted fresh uuids for every block it wrote. Any ((uuid)) reference elsewhere in the graph was left dangling, and Logseq rewrote those refs as plain text in the referring files — content loss well outside the page being written. Two paths dropped the uuids. insertBatchBlock was called without keepUUID, so Logseq ignored the id:: it was handed. The first block went through appendBlockInPage instead, which cannot carry a uuid at all; it now writes an empty anchor and routes every block through the batch. keepUUID is only requested when every id in the batch parses as an RFC 4122 uuid. Logseq answers a malformed one by discarding the entire batch and still reporting success, so a single bad id would empty the page. On file graphs the anchor is kept when the page has properties, since Logseq stores those as `key:: value` lines in the first block. Deleting it merged the page properties into the first block of real content.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #92.
mcp 2.0.0 removed the low-level
Server's@list_tools()/@call_tool()decorators in favour of constructor-based handler registration, sobuild_app()raisedAttributeErrorat import and the server never served a request. Sincemcp>=1.27had no upper bound, every fresh install hit this.This ports the server to the 2.x API rather than capping the dependency, and requires
mcp>=2.0.What changed
Handler registration.
list_tools/call_toolare now plain(ctx, params)coroutines passed ason_list_tools/on_call_tool, returningListToolsResult/CallToolResult.Error reporting, now done by us. In 2.x an exception out of a handler becomes a JSON-RPC error scrubbed to a generic
"Internal server error", so the model would no longer see why a call failed — an access denial, a missing page, an unreachable Logseq.call_toolcatches and returnsis_errorresults carrying the message instead. The strings clients see are unchanged from 1.8.0 (Error: …,Unknown tool: …,Input validation error: …).Argument validation, now done by us. 1.x validated arguments against
inputSchemaby default; 2.x moved validation client-side.call_toolvalidates viajsonschema, which is added as an explicit dependency since it is now used directly rather than transitively.Field spelling.
Toolfields are snake_case for attribute access in 2.x, so tool descriptions and the test assertions that read.inputSchemamove to.input_schema. The wire format is unaffected — the camelCase alias still works for construction and serialization.Testing
tests/integration/test_mcp_protocol.pydrives a realClientSessionagainst the realServerover in-memory streams —initialize,tools/list,tools/call, error results, schema validation, and read-only tool filtering. This layer had no coverage previously, which is why the break only surfaced at a user's first connection.mcp-logseqbinary over real stdio completes the handshake, lists 17 tools, and returns a tool-call failure as an in-band error result with the message intact.Notes
mcp>=2.0floor is a breaking dependency change, so this probably wants a minor version bump rather than a patch.uv.lock.