feat(mcp): add @gemstack/mcp — agent-agnostic MCP server framework (Phase 1)#22
Merged
Conversation
…se 1) Graduates the framework-agnostic core of @rudderjs/mcp into a standalone, dependency-light package (runtime deps: @modelcontextprotocol/sdk, zod, reflect-metadata; zero @rudderjs/*). Implements Phase 1 of gemstack #18. - Strip Rudder framework wiring from the core: drop provider.ts (McpProvider), the make-* CLI scaffolders, doctor.ts, and the inspector (these stay with the Rudder binding in Phase 2). - DI resolver seam (Decision 2): replace the globalThis container read with an instance-scoped McpResolver passed at construction. @handle resolves at call time off the server; built-in createResolver().register for the no-container case; a requested dependency with no resolver, or one that yields undefined, fails loudly naming the member and token. No global setter. - Framework-neutral HTTP (Decision 3): createMcpHttpHandler returns a plain node:http (req, res) handler; createWebRequestHandler returns a Web Standard (request) => Response engine for Hono/Vike/edge. - Generic OAuth2 (Decision 4): oauth2McpMiddleware takes a Connect (req,res,next) signature + a user-supplied verifyToken; no passport / @rudderjs/core coupling. - Drop @rudderjs/json-schema (Decision 5): inline Zod 4 native z.toJSONSchema with the date-time override + open-object fallback. - Fresh 0.0.0 -> 0.1.0 via changeset (Decision 7). Build + typecheck + 100 tests green standalone, including a raw node:http acceptance test serving a server with no framework present. Closes #19.
This was referenced Jun 26, 2026
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.
Phase 1 of the
@gemstack/mcpgraduation. Design + locked decisions: #18. Build issue: #19. Family epic: #1.Stands up
@gemstack/mcpas a standalone, agent-agnostic MCP server framework — the server-authoring axis of the MCP taxonomy, peer of@gemstack/ai-mcp(the agent bridge). It depends on no AI runtime, andai-sdkmust never depend on it.What landed
Lifted the framework-agnostic core out of
@rudderjs/mcpand applied the locked decisions:provider.ts(McpProvider), themake-*CLI scaffolders,doctor.ts, and the inspector from the core — these stay with the@rudderjs/mcpbinding in Phase 2 (Build: @gemstack/mcp Phase 2 — repoint @rudderjs/mcp onto the core (thin binding) #20). KeptMcp/McpServer/McpTool/McpResource/McpPrompt/McpResponse, the decorators, observers, runtime, testing.globalThis.__rudderjs_*container read with an instance-scopedMcpResolverpassed at construction —new MyServer({ resolver }).@Handleresolves at call time off the server instance; built-increateResolver().register(token, instance)for the no-container case. A@Handledependency with no resolver, or a resolver that yieldsundefined, throws a loud error naming the member + token — never injectsundefined. No global setter.createMcpHttpHandler(server)returns a plainnode:http(req, res)handler (also fits Express/Connect);createWebRequestHandler(server)returns a Web Standard(request) => Promise<Response>engine for Hono/Vike/edge runtimes.oauth2McpMiddlewarere-typed to a Connect(req, res, next)signature taking a user-suppliedverifyToken; dropped the@rudderjs/coreMiddlewareHandlerimport and the passport lazy-load (passport is wired by the binding, not here).@rudderjs/json-schema(Decision 5): inline Zod 4 nativez.toJSONSchemawith thez.date()->string/date-timeoverride + open-object fallback. Zero behaviour change.0.0.0->0.1.0via aminorchangeset (matches the family convention).Runtime deps collapse to
@modelcontextprotocol/sdk+zod+reflect-metadata. Zero@rudderjs/*, and nothing imported from@gemstack/ai-*.Acceptance
pnpm build+pnpm typecheck+ full suite green standalone (100 tests,zod+node:test).@gemstack/mcpserves overcreateMcpHttpHandleron rawnode:http, no framework present (new integration test using the SDK's realStreamableHTTPClientTransport).@Handleworks three ways: via a passedresolver, viacreateResolver().register, and a no-@Handletool needs no resolver; a missing resolver / undefined resolution throws a clear named error.@gemstack/ai-*; deps = SDK + zod + reflect-metadata.@gemstack/mcp@0.1.0— on merge, via the release workflow (no pending changeset blocks it).Notes
boost/Claude-skill docs were not carried over: they document the Rudder-flavored authoring (CLI scaffolders, provider,globalThis-container DI) which no longer matches the agnostic core (resolver seam, neutral handler, no CLI). Agnostic-core skill docs belong to Phase 3 (Build: @gemstack/mcp Phase 3 — framework-neutral quickstart/docs (optional) #21); the existing docs stay with the binding.@rudderjs/mcponto this core as the thin Rudder binding (provider, container resolver, CLI, doctor, inspector).Closes #19.