feat: add dual-era package serving entries - #358
Open
barryroodt wants to merge 5 commits into
Open
Conversation
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).
barryroodt
force-pushed
the
barryroodt/ai-1044-dual-era-serving
branch
from
August 11, 2026 14:10
8a775c8 to
e464513
Compare
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.
barryroodt
marked this pull request as ready for review
August 12, 2026 09:56
commit: |
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.
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.