feat: add dual-era package serving entries - #358
Conversation
8a775c8 to
e464513
Compare
commit: |
raulb
left a comment
There was a problem hiding this comment.
@barryroodt left some comments, but my main question is the following one is re: the new scripts directory added.
I believe that's because it's meant to be shared across @supabase/mcp-server-supabase and @supabase/mcp-utils. Is that right?
If so, I think it'd be important to state that in the CONTRIBUTING.md file since we're introducing a new pattern for these.
barryroodt
left a comment
There was a problem hiding this comment.
Yeah, that's right, it packs both packages and installs them together, so neither owns it. Root package.json already holds the cross-package commands (build, test). The check works because the consumer is built under tmpdir(), where workspace:, catalog: and symlinked node_modules can't reach it.
The new top-level scripts/ dir does arrive unexplained though. I'll document it in CONTRIBUTING.md, slightly wider than you asked: there's no testing section at all today, so pnpm test, test:coverage and this gate are all undiscoverable.
Address review feedback on #358: - Comment createSupabaseMcpHandler as modern-protocol only. - Link the 2026-07-28 release-candidate post beside both copies of the protocol revision constant. - Correct README's handler lifecycle guidance. A shared long-lived handler is supported when the platform is deliberately shared; the per-request rule applies when platform carries a per-request credential. close() ends the handler it is called on, so its timing follows that handler's lifetime rather than always being per response. - Add a CONTRIBUTING testing section covering the suites and the scripts/ packaging-gate pattern.
Coverage Report for CI Build 32367104134Warning Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes. Coverage increased (+0.008%) to 96.52%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
Address review feedback on #358: - Comment createSupabaseMcpHandler as modern-protocol only. - Link the 2026-07-28 release-candidate post beside both copies of the protocol revision constant. - Correct README's handler lifecycle guidance. A shared long-lived handler is supported when the platform is deliberately shared; the per-request rule applies when platform carries a per-request credential. close() ends the handler it is called on, so its timing follows that handler's lifetime rather than always being per response. - Add a CONTRIBUTING testing section covering the suites and the scripts/ packaging-gate pattern.
851f01e to
ed3212c
Compare
Address review feedback on #358: - Comment createSupabaseMcpHandler as modern-protocol only. - Link the 2026-07-28 release-candidate post beside both copies of the protocol revision constant. - Correct README's handler lifecycle guidance. A shared long-lived handler is supported when the platform is deliberately shared; the per-request rule applies when platform carries a per-request credential. close() ends the handler it is called on, so its timing follows that handler's lifetime rather than always being per response. - Add a CONTRIBUTING testing section covering the suites and the scripts/ packaging-gate pattern.
ed3212c to
6a006fa
Compare
Move the CLI's stdio entry from a one-shot server plus raw StdioServerTransport onto the SDK's serveStdio, which owns transport startup, connection-pinned era selection, and teardown. The default legacy: 'serve' is the dual-era behavior we want, so no option is passed. CLI parsing, token handling, platform construction, error output, and exit codes are unchanged. Export createSupabaseMcpHandler, a thin wrapper over the SDK's createMcpHandler with legacy: 'reject'. Hosted owns authentication, ABAC, era dispatch, logging, and request lifetime, so the package interface carries only the strict modern entry and server construction. The stdio wire goldens now run as an era matrix. Both eras assert the same 29-tool set, the same server identity and capabilities, and the same list_projects content. Modern additionally carries the protocol's mandatory _meta['io.modelcontextprotocol/serverInfo'] stamp, which legacy asserts is absent. Add test:packed-platform-consumer, which packs mcp-server-supabase and mcp-utils, installs both with plain npm into a throwaway project pinned to the exact zod version Platform's catalog carries, then asserts the ESM entry, the CJS entry, the type declarations, and one modern 2026-07-28 call. Registering the account tool group keeps the zod-built tool surface under test, which an empty catalog would skip.
serveStdio routes transport startup and out-of-band wire errors only through options.onerror, swallowing them otherwise, so without it a startup failure was silent. The previous awaited server.connect() surfaced it through main().catch(console.error).
Restore eager --features validation. Moving server construction into serveStdio's lazy factory deferred parseFeatureGroups until the first valid opening message, so an invalid --features value no longer reported and terminated at startup. Validate the explicitly provided list before serving, leaving platform-dependent default resolution inside createSupabaseMcpServer. Covered by a new startup-failure test. Make the packed-consumer gate prove what it claimed. Its only schema check was a truthiness test on list_projects, a zero-argument tool whose healthy schema already carries no properties, so a zod regression emitting property-less schemas passed. Assert a parameterised witness instead, and drop skipLibCheck so the packed declarations are really type-checked. Pin the fixture's dev dependencies exactly and install without lifecycle scripts. Move the fixture's source out of JS string constants into real files, collapse the per-check pass-throughs into one runner plus a table, and drop the esm-entry check that modern-call.mjs already covers by importing and calling the package ESM entry. The script goes from 429 lines to 240. Share the msw lifecycle between the two suites that had copied it, assert status and error code rather than the SDK's exact envelope wording, guard the integration suite against a stale dist build, and document createSupabaseMcpHandler with the required per-request mounting pattern.
The modern validation test asserted the SDK's exact envelope message, so an
upstream rewording would fail a test whose contract held. Assert the status,
the JSON-RPC error code, and the structured envelope data instead. The
message is upstream text this package does not own; {key, problem} is
machine-readable and does not churn on rewording.
handler.close() returns a Promise that aborts in-flight exchanges, and the example discarded it inside an event callback while also making the createServer callback async, so a cleanup or serving failure surfaced as an unhandled rejection. Attach a catch to both, and keep the close on res finishing rather than on the handler resolving, since the latter would cut streaming responses short.
Address review feedback on #358: - Comment createSupabaseMcpHandler as modern-protocol only. - Link the 2026-07-28 release-candidate post beside both copies of the protocol revision constant. - Correct README's handler lifecycle guidance. A shared long-lived handler is supported when the platform is deliberately shared; the per-request rule applies when platform carries a per-request credential. close() ends the handler it is called on, so its timing follows that handler's lifetime rather than always being per response. - Add a CONTRIBUTING testing section covering the suites and the scripts/ packaging-gate pattern.
6a006fa to
ec9ebed
Compare
🤖 I have created a release *beep* *boop* --- <details><summary>mcp-utils: 0.7.0</summary> ## [0.7.0](mcp-utils-v0.6.0...mcp-utils-v0.7.0) (2026-08-20) ### ⚠ BREAKING CHANGES * 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. ### Features * migrate published packages to MCP SDK v2 ([#327](#327)) ([ead56f2](ead56f2)) </details> <details><summary>mcp-server-supabase: 0.11.0</summary> ## [0.11.0](mcp-server-supabase-v0.10.0...mcp-server-supabase-v0.11.0) (2026-08-20) ### ⚠ BREAKING CHANGES * 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. ### Features * add dual-era package serving entries ([#358](#358)) ([989eb4e](989eb4e)) * migrate published packages to MCP SDK v2 ([#327](#327)) ([ead56f2](ead56f2)) </details> <details><summary>mcp-server-postgrest: 0.2.0</summary> ## [0.2.0](mcp-server-postgrest-v0.1.1...mcp-server-postgrest-v0.2.0) (2026-08-20) ### ⚠ BREAKING CHANGES * 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. ### Features * migrate published packages to MCP SDK v2 ([#327](#327)) ([ead56f2](ead56f2)) </details> --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: supabase-releaser[bot] <223506987+supabase-releaser[bot]@users.noreply.github.com>
What kind of change does this PR introduce?
The CLI serves both protocol eras from one tool definition, and the package exports one modern HTTP handler for the hosted
/mcpendpoint to mount.PR 2 of 3 for AI-1044 (plan).
What is the current behavior?
src/transports/stdio.tsconnects a rawStdioServerTransport, so only 2025-era clients are served, and there's no HTTP entry for hosted to consume.What is the new behavior?
serveStdioowns the transport and picks the era per connection.createSupabaseMcpHandlerwraps the SDK'screateMcpHandlerwithlegacy: 'reject', so hosted keeps authentication, ABAC, era dispatch, and logging.tools/listis unchanged, so the frozen ChatGPT contract is unaffected.mcp-server-postgrestkeeps its single-era entry, and nothing here adds Elicitation machinery, tool policies, or telemetry.Verification
mcp-server-supabaseunit + integration: 222 passed. Both eras run against the real builtdist/transports/stdio.js, spawned as a child process.mcp-utils: 12 passed.pnpm buildandpnpm format:checkclean.pnpm test:packed-platform-consumer(new): 3/3 on packed0.10.0. Installs the tarballs with plainnpmoutside the repo on Platform's exact zod pin, then checks the CJS entry, atsctypecheck of the packed declarations, and one modern call that imports the ESM entry.--versionexits 0, a missing access token exits 1.