Add production-grade MCP 2026 server with sessions, OAuth, and resources - #15
Merged
Merged
Conversation
Replace the stubbed ToolProxy.execute with a production-grade MCP 2026 compliant server and transport layer. Key additions: - src/mcp/server.ts: Full JSON-RPC 2.0 server with SSE streaming, /.well-known/mcp.json discovery, session management, OAuth 2.1, rate limiting, audit logging, and progress notifications - src/mcp/transport.ts: Multi-transport bridge (MCP, REST, local, gRPC) with three-tier execution (inference → stream → single-shot) - src/mcp/session-store.ts: SQLite-backed session persistence that survives server restarts with pruning and TTL support - src/mcp/oauth.ts: OAuth 2.1 client credentials flow with scoped tokens mapped from permissions.json - src/mcp/resources.ts: Resource list/read/subscribe with change notification broadcasting to SSE clients - src/mcp/prompts.ts: Prompt list/get/render with template variable substitution and static prompt registration Updated ToolProxy to route through real transports with executeStream and executeInference support. Rewrote serve command to use MCPServer. Added MCP compliance checker to doctor command (--mcp flag). All 190 tests pass including 53 new MCP module tests. https://claude.ai/code/session_01Kpot412pjCGBQcDMtcw4Pb
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
johnnyclem
added a commit
that referenced
this pull request
Aug 16, 2026
* chore: remove orphaned nested lockfiles in shorthand/ and packages/examples/ Both were flagged as a deferred finding in the prior audit pass (AUDIT.md #15) pending confirmation they weren't serving some standalone-publish purpose that would make removing them unsafe. Checked both possibilities directly: - @shorthand/core is not published to npm (registry 404) despite shorthand/'s lockfile implying an independently-tracked dependency tree. - @smallchat/examples is published, but publishing doesn't consume a package-lock.json, so that's not a reason to keep one either. More importantly, `npm install` run directly inside either directory does not update its local lockfile at all — npm detects the ancestor workspace root and defers to the root package-lock.json entirely, so these files were not being maintained by any normal workflow. This had already caused real drift: shorthand/package-lock.json still resolved vitest@3.2.4, the exact version with the critical CVE fixed at the root in the prior audit pass (#85) — invisible to root-level `npm audit`/CI, the same class of blind spot as the packages/docs lockfile found in that same pass. A lockfile nobody can update and that silently reintroduces fixed CVEs is worse than no lockfile. Verified: fresh `npm install` from a clean node_modules, `npm test` (1153/1153), `npm run build`, and `npm run build:packages` all pass using only the root lockfile. * fix(docs): coordinated Docusaurus 3.6.3 -> 3.10.2 upgrade Second half of the deferred packages/docs dependency work: bumping just @docusaurus/preset-classic (via npm audit fix --force) while @docusaurus/core stayed pinned at 3.6.3 produced a mismatched, out-of-range pair that wasn't safe to ship. Bumped all four @docusaurus/* packages (core, preset-classic, module-type-aliases, types) together to the current latest, 3.10.2. - npm audit: 44 -> 24 remaining findings. All 24 are serialize-javascript/uuid/sockjs/webpack-dev-server, pulled in transitively via @docusaurus/bundler's own webpack toolchain, with "No fix available" per npm audit's own output even at Docusaurus's current latest release -- nothing further to do here until Docusaurus updates that dependency chain upstream. These are build/dev-time tooling deps, not shipped in the static site output. - Fixed the onBrokenMarkdownLinks config deprecation warning the bump surfaced (moved to markdown.hooks.onBrokenMarkdownLinks per the new Docusaurus config shape). - Aligned engines.node from >=18.0 to >=20.0 -- Docusaurus 3.10 itself now requires Node >=20, so the old floor was both already inconsistent with the rest of the monorepo and no longer accurate. Verified: `npm run build` (docusaurus build) succeeds cleanly with no warnings; root `npm test` (1153/1153) and `npm run build` unaffected (packages/docs is outside the root workspace/tsconfig). * refactor(core): decouple ToolProxy from concrete MCP transport Deferred item #13 from the security audit: ToolClass/ToolProxy are re-exported from `@smallchat/core/inference`, documented as the durable, transport-agnostic engine -- but ToolProxy statically imported MCPTransport/getTransport from src/mcp/transport.ts, ~550 lines of HTTP/JSON-RPC/SSE/gRPC wire-protocol code. Anyone importing `@smallchat/core/inference` expecting pure selection logic got concrete networking code pulled in transitively, contradicting the entry point's own stated contract in ARCHITECTURE.md. - src/core/types.ts: added ToolTransport (execute/executeStream/ executeInference), ToolTransportConnectionOptions, and a ToolTransportFactory type -- type-only, zero runtime import cost. - src/core/tool-class.ts: ToolProxy no longer imports mcp/transport.js at all. Its constructor takes an optional transportFactory; getTransport() is now a private method that uses the injected factory (lazily, same caching behavior as before) or returns null. execute()/ executeStream() return a clear "no transport configured" ToolResult error when no factory was injected, rather than silently reaching into a hardcoded implementation; executeInference() returns nothing, matching its existing "unsupported transport" no-op pattern. - src/compiler/compiler.ts (createIMP) and src/mcp/artifact.ts (hydrateRuntime) -- the only two places that construct ToolProxy -- now explicitly pass getTransport from mcp/transport.ts, preserving identical existing behavior at both call sites. - src/inference.ts and src/index.ts: export the three new types alongside the existing core type exports. Verified the fix is real, not just moved: grepped the built dist/inference.js, dist/core/*.js, and dist/runtime/*.js for any reference to mcp/transport -- none. Added src/inference.test.ts, a source-scan regression test that fails if any core/runtime file statically imports mcp/transport.ts again. Added constructor-injection tests to core/tool-class.test.ts (no-factory error path, and routing through an injected factory). Verified: npm run lint (tsc --noEmit), npm test (1156/1156), npm run build, and npm run build:packages all clean. * fix(importance): reconcile src/importance/ with @shorthand/core/importance Deferred item #21, tracked as a "Phase 0, do first" fix in docs/ecosystem/engineering-guide.md: PR #58 titled itself "Extract @shorthand/core package from compaction, CRDT, and importance modules," but only compaction and CRDT actually got extracted -- src/compaction/ and src/crdt/ don't exist as local directories, only as re-exports from @shorthand/core in src/index.ts. src/importance/ was left as a full duplicate copy of shorthand/src/importance/ instead, and it had already drifted: its types.ts defined a narrower local ConversationMessage (missing the 'tool' role option, timestamp: string | number, and normalizeTimestamp) instead of importing the canonical shared type shorthand/src/importance/types.ts already does. `diff -rq` on the two directories confirmed every other file was byte-identical -- this was purely a types.ts fork. Replaced src/importance/ with a thin re-export of @shorthand/core/importance, matching how compaction/CRDT already work, and deleted the five duplicated implementation files plus their tests (the underlying logic is exercised by @shorthand/core's own test suite). The public @smallchat/core/importance subpath is unchanged -- same exported names, same behavior -- so this is non-breaking for anyone consuming it. Making this change surfaced a real CI gap: shorthand/'s own ~260-spec test suite (compaction, CRDT, importance) was never run by root `npm test` or CI -- root vitest.config.ts only globs the root src/, and nothing wired shorthand's tests into either. This was true even before this change; deleting the local duplicate just made the gap visible rather than causing it. Added `npm test --workspace=shorthand` to CI's test job to close it. Adds src/importance/index.test.ts: a smoke test verifying the re-export barrel resolves and works end-to-end (not re-testing the underlying logic, which shorthand's own suite already covers). Verified: npm run lint (tsc --noEmit) clean, npm test (1106/1106), npm run build, npm run build:packages, and `npm test --workspace=shorthand` (260/260) all pass. * docs: update README test counts after the importance/docs follow-up work Root spec count dropped from ~1,150 to ~1,106 (removing the duplicated src/importance/ implementation files also removed their tests -- the underlying logic is now exercised by @shorthand/core's own suite instead). Also documents `npm test --workspace=shorthand`, which the audit follow-up wired into CI but wasn't mentioned anywhere for anyone running it locally. * feat(deps)!: raise Node floor to >=22, bump commander and better-sqlite3 Last deferred item from the security audit. commander@15 and better-sqlite3@13 (two majors ahead of what was pinned) both now require Node >=22 upstream -- a real breaking-change decision that wasn't this audit's to make unilaterally, so it was put to the maintainer directly: raise the floor, or hold at Node >=20 and skip the bumps. Decision: raise the floor. BREAKING CHANGE: engines.node is now >=22.0.0 (was >=20.0.0). Node 20 is no longer supported. - package.json: commander ^13.0.0 -> ^15.0.0, better-sqlite3 ^11.0.0 -> ^13.0.3, @types/better-sqlite3 ^7.6.12 -> ^9.6.0, engines.node -> >=22.0.0. - shorthand/package.json: better-sqlite3 bumped to match (it depends on the same package directly -- now deduped to one copy across the workspace instead of two divergent majors), plus vitest ^3.0.0 -> ^3.2.7 to match the critical-CVE fix applied to root earlier in this audit but missed here, and engines.node -> >=22.0.0. - packages/examples/package.json, packages/docs/package.json: engines.node -> >=22.0.0 for consistency. - src/cli/commands/init.ts: the package.json template `smallchat init` scaffolds for new projects also bumped to >=22.0.0 -- it depends on @smallchat/core, which now requires that floor anyway. - .github/workflows/ci.yml: test matrix ['20','22'] -> ['22','24']; the audit and docs jobs' single Node version '20' -> '22'. - README.md: "Requires Node.js >= 20" -> ">= 22". Verified beyond typecheck/tests (which a native-binding major bump like better-sqlite3 wouldn't necessarily catch): built and ran the actual CLI against the new versions -- `smallchat --help` renders correctly and `smallchat doctor` confirms "better-sqlite3 + sqlite-vec: working". Verified: npm run lint, npm test (1106/1106), npm run build, npm run build:packages, and npm test --workspace=shorthand (260/260) all pass on Node 22. * docs: note in AUDIT.md that all five deferred items are now resolved --------- Co-authored-by: Claude <noreply@anthropic.com>
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.
Summary
This PR introduces a complete, production-ready MCP (Model Context Protocol) 2026 compliant server implementation with enterprise features including session management, OAuth 2.1 authentication, resource/prompt registries, rate limiting, and audit logging.
Key Changes
New MCP Server Implementation
src/mcp/server.ts(1133 lines): Core MCPServer class implementing the full MCP protocol/.well-known/mcp.jsondiscovery endpoint for MCP complianceSession Management
src/mcp/session-store.ts(198 lines): SQLite-backed session persistencesession-store.test.tsOAuth 2.1 Authentication
src/mcp/oauth.ts(343 lines): Complete OAuth 2.1 implementationoauth.test.tsResource Management
src/mcp/resources.ts(240 lines): MCP resources protocolresources.test.tsPrompt Management
src/mcp/prompts.ts(187 lines): MCP prompts protocolprompts.test.tsTransport Layer
src/mcp/transport.ts(548 lines): Multi-transport tool executiontransport.test.tsCLI Integration
src/cli/commands/serve.ts(refactored): Simplified to use MCPServer--db-path,--auth,--rate-limit,--audit,--session-ttlsrc/cli/commands/doctor.ts(enhanced): Added MCP compliance checkerCore Type Updates
src/core/tool-class.ts: Added MCPTransport integration to ToolProxysrc/index.ts: Exported new MCP server and transport classesNotable Implementation Details
Protocol Compliance: Full MCP 2024-11-05 protocol support with proper JSON-RPC 2.0 error codes and response formats
Streaming Architecture: Three-tier streaming model:
Security:
https://claude.ai/code/session_01Kpot412pjCGBQcDMtcw4Pb