Skip to content

feat: add dual-era package serving entries - #358

Merged
barryroodt merged 6 commits into
mainfrom
barryroodt/ai-1044-dual-era-serving
Aug 20, 2026
Merged

feat: add dual-era package serving entries#358
barryroodt merged 6 commits into
mainfrom
barryroodt/ai-1044-dual-era-serving

Conversation

@barryroodt

@barryroodt barryroodt commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

This is a stacked PR. Its parent is #327, please review that one first.

The diff here is only this branch's own commits. The base is raulb/spike-mcp-v2 rather than main on purpose, because #327 is still in review; once it lands, this retargets to main and the diff against main shrinks accordingly.

The test check runs on this page and passes at 851f01e. An earlier version of this description said it would be absent; that note is out of date. The test output under Verification came from a local run that predates the check.

At merge time: retarget to main, let tests.yml run green there, then merge.

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 /mcp endpoint to mount.

PR 2 of 3 for AI-1044 (plan).

What is the current behavior?

src/transports/stdio.ts connects a raw StdioServerTransport, so only 2025-era clients are served, and there's no HTTP entry for hosted to consume.

What is the new behavior?

serveStdio owns the transport and picks the era per connection. createSupabaseMcpHandler wraps the SDK's createMcpHandler with legacy: 'reject', so hosted keeps authentication, ABAC, era dispatch, and logging.

tools/list is unchanged, so the frozen ChatGPT contract is unaffected. mcp-server-postgrest keeps its single-era entry, and nothing here adds Elicitation machinery, tool policies, or telemetry.

Verification

  • mcp-server-supabase unit + integration: 222 passed. Both eras run against the real built dist/transports/stdio.js, spawned as a child process.
  • mcp-utils: 12 passed. pnpm build and pnpm format:check clean.
  • pnpm test:packed-platform-consumer (new): 3/3 on packed 0.10.0. Installs the tarballs with plain npm outside the repo on Platform's exact zod pin, then checks the CJS entry, a tsc typecheck of the packed declarations, and one modern call that imports the ESM entry.
  • CLI behavior unchanged: --version exits 0, a missing access token exits 1.

@barryroodt
barryroodt force-pushed the barryroodt/ai-1044-dual-era-serving branch from 8a775c8 to e464513 Compare August 11, 2026 14:10
@barryroodt
barryroodt marked this pull request as ready for review August 12, 2026 09:56
@barryroodt
barryroodt requested a review from a team as a code owner August 12, 2026 09:56
@barryroodt barryroodt added the publish-preview Runs `publish-preview` workflow to publish preview packages via https://pkg.pr.new/ label Aug 12, 2026
@pkg-pr-new

pkg-pr-new Bot commented Aug 12, 2026

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/@supabase/mcp-server-postgrest@ec9ebed
pnpm add https://pkg.pr.new/@supabase/mcp-server-supabase@ec9ebed
pnpm add https://pkg.pr.new/@supabase/mcp-utils@ec9ebed

commit: ec9ebed

@barryroodt barryroodt self-assigned this Aug 12, 2026

@raulb raulb left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@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.

Comment thread package.json
Comment thread packages/mcp-server-supabase/src/transports/http.ts
Comment thread packages/mcp-server-supabase/src/transports/http.test.ts

@barryroodt barryroodt left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

barryroodt added a commit that referenced this pull request Aug 13, 2026
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.
@coveralls

coveralls commented Aug 13, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 32367104134

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage increased (+0.008%) to 96.52%

Details

  • Coverage increased (+0.008%) from the base build.
  • Patch coverage: 6 of 6 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 3036
Covered Lines: 2951
Line Coverage: 97.2%
Relevant Branches: 384
Covered Branches: 350
Branch Coverage: 91.15%
Branches in Coverage %: Yes
Coverage Strength: 50.92 hits per line

💛 - Coveralls

barryroodt added a commit that referenced this pull request Aug 20, 2026
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.
@barryroodt
barryroodt force-pushed the barryroodt/ai-1044-dual-era-serving branch from 851f01e to ed3212c Compare August 20, 2026 11:04
barryroodt added a commit that referenced this pull request Aug 20, 2026
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.
@barryroodt
barryroodt force-pushed the barryroodt/ai-1044-dual-era-serving branch from ed3212c to 6a006fa Compare August 20, 2026 11:07
Base automatically changed from raulb/spike-mcp-v2 to main August 20, 2026 12:06
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.
@barryroodt
barryroodt force-pushed the barryroodt/ai-1044-dual-era-serving branch from 6a006fa to ec9ebed Compare August 20, 2026 12:06
@barryroodt
barryroodt merged commit 989eb4e into main Aug 20, 2026
7 checks passed
@supabase-releaser supabase-releaser Bot mentioned this pull request Aug 20, 2026
barryroodt pushed a commit that referenced this pull request Aug 20, 2026
🤖 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

publish-preview Runs `publish-preview` workflow to publish preview packages via https://pkg.pr.new/

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants