feat(mcp-server): curated tool registry & input validation (MCP Prompt 10)#4011
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces a curated tool registry (ToolRegistry) and validation layer (validateToolInput) using Zod schemas for strict input validation, along with corresponding unit tests. The feedback suggests replacing the non-standard z.toJSONSchema method with the explicitly imported zodToJsonSchema function from zod-to-json-schema to ensure runtime compatibility and type safety.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
743418f to
9080929
Compare
cc1631c to
94a27f0
Compare
9080929 to
c280fe9
Compare
There was a problem hiding this comment.
Pull request overview
Adds the curated tool registry contracts and strict input validation layer for the new @accounter/mcp-server package, along with supporting MCP server scaffolding (HTTP transport skeleton, OAuth discovery helpers, auth primitives) and a comprehensive unit-test suite to keep behavior deterministic and hermetic.
Changes:
- Introduces
ToolRegistry+ToolDefinitioncontracts andvalidateToolInput()with strict unknown-field rejection and deterministic validation error payloads. - Adds MCP server foundations (request context/logging, OAuth protected-resource metadata + 401 challenge helper, JSON-RPC parsing/dispatch, Auth0 token verification + identity mapping).
- Updates docs/spec + implementation blueprint and wires workspace-local TS/Vitest config for isolated testing.
Reviewed changes
Copilot reviewed 33 out of 34 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| yarn.lock | Adds the new workspace package’s dependency set to the lockfile. |
| packages/mcp-server/vitest.config.ts | Package-scoped Vitest config for isolated test runs. |
| packages/mcp-server/tsconfig.json | Package TS build config (ESM, strict, dist output). |
| packages/mcp-server/src/version.ts | Runtime service name/version helpers for health/logging. |
| packages/mcp-server/src/tools/registry.ts | Tool registry contracts, JSON-Schema description, strict input validation. |
| packages/mcp-server/src/tools/tests/registry.test.ts | Unit tests for registry behavior and validation payloads. |
| packages/mcp-server/src/server.ts | Minimal node:http server with routing, health, and shutdown. |
| packages/mcp-server/src/oauth/metadata.ts | RFC 9728 protected-resource metadata document builder + URL helper. |
| packages/mcp-server/src/oauth/challenge.ts | Standard 401 WWW-Authenticate Bearer challenge builder/sender. |
| packages/mcp-server/src/oauth/tests/metadata.test.ts | Contract tests for protected-resource metadata shape and URL joining. |
| packages/mcp-server/src/oauth/tests/challenge.test.ts | Contract tests for 401 challenge header escaping/body behavior. |
| packages/mcp-server/src/mcp/tools.ts | Placeholder “smoke tool” surface for the early MCP skeleton. |
| packages/mcp-server/src/mcp/jsonrpc.ts | Minimal JSON-RPC request/response primitives + request-shape validation. |
| packages/mcp-server/src/mcp/handler.ts | MCP HTTP handler (auth boundary + body read + JSON-RPC dispatch). |
| packages/mcp-server/src/mcp/tests/jsonrpc.test.ts | Tests for JSON-RPC helpers and request-shape narrowing. |
| packages/mcp-server/src/mcp/tests/handler.test.ts | End-to-end-ish tests for MCP handler dispatch, auth, and size limits. |
| packages/mcp-server/src/logger.ts | Minimal structured JSON logger + request-bound logger helpers. |
| packages/mcp-server/src/index.ts | Entrypoint wiring: start server + process-level error handlers. |
| packages/mcp-server/src/context.ts | Per-request requestId/correlationId creation and storage. |
| packages/mcp-server/src/config/env.ts | Strict env parsing/normalization with fail-fast error reporting. |
| packages/mcp-server/src/config/tests/env.test.ts | Tests for env parsing defaults and invalid configurations. |
| packages/mcp-server/src/auth/verifier.ts | Auth0 JWKS-backed access-token verifier (cached by JWKS URL). |
| packages/mcp-server/src/auth/token.ts | Bearer extraction, jose verification wrapper, and principal shape. |
| packages/mcp-server/src/auth/identity.ts | Identity→memberships mapping + read-scope narrowing helpers. |
| packages/mcp-server/src/auth/tests/token.test.ts | Tests for token extraction and verification error classification. |
| packages/mcp-server/src/auth/tests/identity.test.ts | Tests for membership parsing, dedupe, and scope narrowing semantics. |
| packages/mcp-server/src/tests/server.test.ts | Server routing/shutdown/health tests (log silencing included). |
| packages/mcp-server/src/tests/logger.test.ts | Tests for logger output safety and context field merging. |
| packages/mcp-server/src/tests/index.test.ts | Ensures entrypoint exports are wired. |
| packages/mcp-server/src/tests/context.test.ts | Tests for correlation id inheritance and route parsing behavior. |
| packages/mcp-server/README.md | Package documentation for local running, OAuth discovery, and config. |
| packages/mcp-server/package.json | Declares the new workspace package, scripts, and dependencies. |
| docs/mcp/spec.md | Updates/introduces spec content referenced by the prompt pack. |
| docs/mcp/implementation-blueprint.md | Updates the step-by-step prompt pack/blueprint for incremental delivery. |
Comments suppressed due to low confidence (1)
packages/mcp-server/src/config/env.ts:64
- Same issue for AUTH0_JWKS_URL:
{ error: ... }likely doesn’t set a message in Zod. This makes validation errors less informative than intended.
AUTH0_JWKS_URL: emptyStringAsUndefined(
zod.url({ error: 'AUTH0_JWKS_URL must be a valid URL' }).optional(),
),
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
94a27f0 to
e2773cd
Compare
Add the production-ready tool registry abstraction (docs/mcp/spec.md §8.3). - src/tools/registry.ts: ToolDefinition (name, description, strict zod input schema, auth policy, pure handler); ToolRegistry with register/get/has/list and duplicate-name rejection; describe() renders input schemas to JSON Schema (additionalProperties:false) for tools/list - validateToolInput(): strict parsing with unknown-field rejection and a deterministic VALIDATION_ERROR payload (code, message, field-path issues) - ToolAuthPolicy / DataClassification / ToolExecutionContext contracts for the policy evaluator and handlers in later steps - unit tests for registration, duplicates, ordering, describe(), and schema validation (valid, missing required, unknown field, type mismatch) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SPMszz9gdZFhNjobRm6Ndo
…hema strictness
Address PR review feedback on Prompt 10:
- MCP_ENABLED=0 now actually disables the connector: the MCP transport (`/mcp`)
and its OAuth protected-resource-metadata discovery route return 404 (health
only), instead of being served while the logs claim "disabled mode".
- `tools/call` returns a clear InvalidParams ("params must be an object") when
`params` is an array or other non-object, instead of a misleading
"Unknown tool: undefined".
- `registry.describe()` renders JSON Schema from the same `.strict()` schema that
`validateToolInput` enforces, so the advertised `additionalProperties: false`
provably matches the runtime unknown-field rejection.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SPMszz9gdZFhNjobRm6Ndo
e2773cd to
49992ce
Compare
Summary
Implements Prompt 10 – Tool registry contracts and input validation (see
docs/mcp/spec.md§8.3). Adds the curated toolregistry abstraction that later production tools plug into.
Changes
src/tools/registry.tsToolDefinition<Schema>—name,description, a strictzodinputSchema, aToolAuthPolicy, and a purehandler(input, context).ToolAuthPolicy(requiredRoles,requiresBusinessScope,dataClassification) andToolExecutionContext(auth,readScope,correlationId) — contracts the policy evaluator (Prompt 11) and handlers (Prompt 13+) consume.ToolRegistry—register/has/get/list(registration order preserved) withDuplicateToolErroron name collisions;describe()renders each input schema to JSON Schema (additionalProperties: false) fortools/list.validateToolInput()— strict parse (unknown fields rejected) returning either the typed data or a deterministicVALIDATION_ERRORpayload (code,message, field-pathissues).describe()JSON Schema, and validation (valid, missing-required, unknown-field, type-mismatch). 120 tests total.Validation
yarn workspace @accounter/mcp-server test→ 120 passedyarn workspace @accounter/mcp-server lint/typecheck/build→ passNotes
POST /mcp— the smoke tool still answerstools/list/tools/calldirectly. The registry becomes the source of truth once the first real tool + the policy evaluator land (Prompts 11/13); the smoke stub is removed at final wiring (Prompt 24).dataClassification/requiredRolesare defined here but enforced in Prompt 11.🤖 Generated with Claude Code
Generated by Claude Code