Type MCP registerNeonTools against the matching McpServer - #567
Open
andrelandgraf wants to merge 3 commits into
Open
andrelandgraf wants to merge 3 commits into
andrelandgraf wants to merge 3 commits into
Conversation
andrelandgraf
requested review from
atilafassina and
rodneyshibu
as code owners
September 6, 2026 17:16
Without the peer and with skipLibCheck, an MCP 1 server still type-checks against the MCP 2 adapter.
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.
Problem
@neon/toolsships two MCP entry points that publish tool schemas in different formats:@neon/tools/mcpconverts each tool's Zod input schema to JSON Schema for MCP 2.x and@neon/tools/mcp-v1passes the Zod schema through for MCP 1.x. Both typedserveras{ registerTool: object }, so any object with aregisterToolproperty satisfied either entry. Registering an MCP 1McpServerthrough@neon/tools/mcpcompiled clean and the mistake only surfaced at runtime, when the server received schemas in the other version's format.What changed
Each entry now types
serveragainst theMcpServerof its own SDK:@modelcontextprotocol/server(^2.0.0) and@modelcontextprotocol/sdk(^1.0.0) are optional peer dependencies. A consumer installs the one their server uses.The correct pairing is unchanged:
The wrong pairing now fails to compile:
This is a breaking type change.
McpToolServerwas one shared export with shape{ registerTool: object }; each entry now exports its ownPickover the matching SDK, and hand-rolled stubs that satisfied the old shape need a cast. The changeset marks@neon/toolsmajor. Runtime behavior is unchanged:registerNeonToolsWithSchemastill treatsregisterToolasunknowninternally, so JavaScript consumers see no difference.Residual gap
The guard needs the matching peer installed. When it is missing and
skipLibCheckis on, theMcpServerimport fails to resolve, the type degrades and an MCP 1 server type-checks against@neon/tools/mcpagain. Both peers are optional, so package managers do not warn about the missing one. The README documents this next to the MCP example.Also in here
lib/mcp-register.tsis renamed toMcpToolRegistrar(registerTool: unknown) since the publicMcpToolServerexports moved into the entry points.@modelcontextprotocol/sdk1.30.0 is added as a devDependency so the repo type-checks the v1 entry against the real SDK;pnpm-lock.yamlupdated.src/mcp.test.tsstubs are consolidated intocaptureHandler(), which returns the stub cast to each entry'sMcpToolServer.Verification
pnpm --filter @neon/tools exec tsc --noEmitpasses. It includes the newsrc/mcp.test-d.ts, which asserts both real servers type-check against their entry and a bare{ registerTool: {} }object fails both.McpServerto the MCP 2 entry and the MCP 2McpServerto the MCP 1 entry fails both directions with TS2345.pnpm --filter @neon/tools exec vitest run src/mcp.test.ts: 13 tests pass.skipLibCheckon. The README note is the only mitigation in this PR.