Commit ead56f2
authored
feat!: migrate published packages to MCP SDK v2 (#327)
## What kind of change does this PR introduce?
Moves the three published packages off the v1 monolithic
`@modelcontextprotocol/sdk` and onto the split v2 packages,
`@modelcontextprotocol/server` + `@modelcontextprotocol/client`, on the
released `2.0.0` line.
Part of
[AI-1044](https://linear.app/supabase/issue/AI-1044/migrate-mcp-server-supabase-to-mcp-sdk-v2),
PR 1 of 3 in the [MCP SDK v2 migration
plan](https://linear.app/supabase/document/mcp-sdk-v2-migration-plan-d19bb064d673).
Rebased off @raulb 's original beta.3 work onto current `main`.
## What is the current behavior?
`@supabase/mcp-utils`, `@supabase/mcp-server-supabase`, and
`@supabase/mcp-server-postgrest` depend on `@modelcontextprotocol/sdk:
^1.25.2`, which predates the 2026-07-28 spec.
## What is the new behavior?
Same wire behavior apart from one malformed-input error code, covered
under **Wire delta** below. Nothing about serving changed:
- `mcp-utils` registers its five handlers by method string
(`tools/call`, `tools/list`, `resources/list`,
`resources/templates/list`, `resources/read`) instead of by Zod request
schema. v2 still validates and narrows each request through its codec,
so this is a signature change, not a validation change.
- `createMcpServer` returns a bare `Server`. v2's `Server` takes no type
parameters, so the `ExtractRequest` / `ExtractNotification` /
`ExtractResult` machinery that existed only to widen intellisense had
nothing left to widen.
- `mcp-server-postgrest` is compile-only, as the plan requires: imports
moved, stdio entry and tool surface untouched.
- No `serveStdio`, no HTTP handler, no era negotiation. Those are PR 2.
## Additional context
**Catalog range.** The catalog entries are `^2.0.0` rather than an exact
`2.0.0`. `peerDependencies` in all three packages is `"catalog:"`, and
`pnpm publish` substitutes the catalog value verbatim, so an exact pin
would ship consumers a peer they cannot satisfy alongside any other 2.x
of `@modelcontextprotocol/server`. The old `^1.25.2` entry was a range
for the same reason. The lockfile still resolves to exactly `2.0.0`. The
plan's Task 1 Step 3 has been updated to record the caret range as the
accepted value, so this is no longer a deviation from it.
**Wire delta: malformed `tools/call` params return `-32602`, not
`-32603`.** This is the one observable change between the two builds,
and it is accepted as a spec correction rather than reverted. Full risk
analysis is in the PR comments and recorded in the plan's PR 1
acceptance gate. In short: the 2025-11-25 spec classifies a request
failing `CallToolRequest` schema validation as a Protocol Error with a
worked `-32602` example, and the v2 SDK reserves `-32603` for a handler
throwing something that is not a `ProtocolError`. v1's `-32603` came
from an uncaught `ZodError` falling back to `InternalError`, so it was a
side effect rather than a deliberate choice. `error instanceof McpError`
still holds, the class and `name` are unchanged, and the failure is
still a JSON-RPC error rather than an `isError` result. Only `.code` and
the message prefix move. Type-checked callers cannot produce it, but
untyped ones can: v1's `Client.callTool(params)` types `params` without
runtime-validating `name` before it sends
(`dist/esm/client/index.js:488-493`), so plain JavaScript, or TypeScript
that lost the type through `any`, reaches this path through the ordinary
public API. Note the separate case that did **not** change: arguments
failing a tool's own zod schema still surface as an `isError` text
result.
**Verification.** Two independent checks, because the in-repo tests
upgraded their own client alongside the server and so cannot prove
backward compatibility on their own.
1. Wire goldens, committed before the pin flip and green on both sides
of it: the full sorted 29-name `tools/list` set, server identity and
declared capabilities, and one ordinary `list_projects` call against a
loopback management API stub on port 0. Fully offline.
2. A throwaway probe holding the **client** fixed at v1
(`@modelcontextprotocol/sdk@1.30.0`, what a user on `^1.25.2` installs
today) and varying only the server, driving `dist/transports/stdio.js`
built from `main` and from this branch:
```
OLD serverInfo: {"name":"supabase","title":"Supabase","version":"0.10.0"}
NEW serverInfo: {"name":"supabase","title":"Supabase","version":"0.10.0"}
OLD capabilities: {"tools":{}} NEW capabilities: {"tools":{}}
OLD tool count: 29 NEW tool count: 29
OLD list_projects result == NEW list_projects result (byte-for-byte)
VERDICT: IDENTICAL
```
The missing-access-token path is identical too.
`test/stdio.integration.ts` now asserts `Connection closed` where it
used to assert `MCP error -32000`, and that is the test's own client
upgrade rather than a server change: v1's error class prefixed the
message with `MCP error ${code}: `, v2's passes it through bare. Held
against the fixed v1 client, both builds return `MCP error -32000:
Connection closed`.
**Test results on `2.0.0`.** `mcp-utils` 12/12, `mcp-server-supabase`
215/215 across unit + integration, `format:check` clean, all three
packages typecheck and build. Two suites need external services and fail
identically before and after the pin flip:
`mcp-server-postgrest/src/server.test.ts` wants a local Supabase stack
on `127.0.0.1:54321` (7 failures, all `fetch failed`), and `test/e2e/*`
wants `ANTHROPIC_API_KEY`. Worth noting `tests.yml` runs
`test:coverage`, which filters to `@supabase/mcp-server-supabase`, so
`mcp-utils` and `mcp-server-postgrest` only run locally.
**Merging is not shipping.** `release.yml` runs release-please on push
to `main`, which only opens a release PR. Hold that release PR until PR
3 is approved and merge-ready, per the plan's release gate. Platform
develops against a `publish-preview` build in the meantime.
BREAKING CHANGE: the peer dependency is now
`@modelcontextprotocol/server` instead of `@modelcontextprotocol/sdk`,
so consumers must install the new package. `@supabase/mcp-utils` also
drops the exported types `ExtractRequest`, `ExtractNotification`,
`ExtractResult` and `ExpandRecursively`, and `createMcpServer` returns a
bare `Server` rather than `Server<Request, Notification, Result>`,
because v2's `Server` class takes no type parameters. Because that
returned value is now a v2 `Server`, a consumer who registered extra
handlers on it must rewrite `server.setRequestHandler(SomeRequestSchema,
...)` as `server.setRequestHandler('some/method', ...)`; the v1
Zod-schema overload no longer exists. `InitData.clientCapabilities` now
follows v2's `ClientCapabilities`, which is narrower than v1's and not
assignable from it. Finally, a `tools/call` request whose `params` are
malformed, meaning no `name` key or a non-string `name`, now returns
JSON-RPC `-32602` with the message prefix `Invalid tools/call request: `
where v1 returned `-32603` with a bare stringified ZodError.
---------
Co-authored-by: Barry Roodt <barry.roodt@supabase.io>1 parent 8512069 commit ead56f2
19 files changed
Lines changed: 295 additions & 744 deletions
File tree
- packages
- mcp-server-postgrest
- src
- mcp-server-supabase
- src
- management-api
- tools
- transports
- test
- mcp-utils
- src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
92 | 92 | | |
93 | 93 | | |
94 | 94 | | |
95 | | - | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
96 | 100 | | |
97 | 101 | | |
98 | | - | |
| 102 | + | |
99 | 103 | | |
100 | 104 | | |
101 | 105 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
| 40 | + | |
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
44 | | - | |
| 44 | + | |
| 45 | + | |
45 | 46 | | |
46 | 47 | | |
47 | 48 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | | - | |
4 | 2 | | |
| 3 | + | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
63 | | - | |
| 63 | + | |
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
70 | | - | |
| 70 | + | |
| 71 | + | |
71 | 72 | | |
72 | 73 | | |
73 | 74 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10082 | 10082 | | |
10083 | 10083 | | |
10084 | 10084 | | |
| 10085 | + | |
| 10086 | + | |
| 10087 | + | |
| 10088 | + | |
| 10089 | + | |
| 10090 | + | |
| 10091 | + | |
10085 | 10092 | | |
10086 | 10093 | | |
10087 | 10094 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
3 | | - | |
4 | | - | |
5 | | - | |
| 1 | + | |
| 2 | + | |
6 | 3 | | |
7 | 4 | | |
8 | 5 | | |
9 | 6 | | |
10 | 7 | | |
11 | 8 | | |
12 | 9 | | |
| 10 | + | |
13 | 11 | | |
14 | 12 | | |
15 | 13 | | |
| |||
102 | 100 | | |
103 | 101 | | |
104 | 102 | | |
105 | | - | |
| 103 | + | |
106 | 104 | | |
107 | | - | |
| 105 | + | |
108 | 106 | | |
109 | 107 | | |
110 | 108 | | |
| |||
3893 | 3891 | | |
3894 | 3892 | | |
3895 | 3893 | | |
3896 | | - | |
| 3894 | + | |
3897 | 3895 | | |
3898 | 3896 | | |
3899 | 3897 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
2 | | - | |
3 | | - | |
4 | | - | |
5 | | - | |
| 1 | + | |
| 2 | + | |
6 | 3 | | |
7 | 4 | | |
8 | 5 | | |
| |||
38 | 35 | | |
39 | 36 | | |
40 | 37 | | |
41 | | - | |
| 38 | + | |
42 | 39 | | |
43 | | - | |
| 40 | + | |
44 | 41 | | |
45 | 42 | | |
46 | 43 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
3 | | - | |
4 | 2 | | |
| 3 | + | |
| 4 | + | |
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
| |||
0 commit comments