Skip to content

feat(mcp-server): curated tool registry & input validation (MCP Prompt 10)#4011

Open
gilgardosh wants to merge 1 commit into
claude/mcp-prompt-09-identity-mappingfrom
claude/mcp-prompt-10-tool-registry
Open

feat(mcp-server): curated tool registry & input validation (MCP Prompt 10)#4011
gilgardosh wants to merge 1 commit into
claude/mcp-prompt-09-identity-mappingfrom
claude/mcp-prompt-10-tool-registry

Conversation

@gilgardosh

Copy link
Copy Markdown
Collaborator

Summary

Implements Prompt 10 – Tool registry contracts and input validation (see
docs/mcp/spec.md §8.3). Adds the curated tool
registry abstraction that later production tools plug into.

Stacked PR: targets claude/mcp-prompt-09-identity-mapping (Prompt 09, #4010).
Review/merge Prompts 05→09 first; this diff shows only the Prompt 10 changes.

Changes

  • src/tools/registry.ts
    • ToolDefinition<Schema>name, description, a strict zod inputSchema, a ToolAuthPolicy, and a pure handler(input, context).
    • ToolAuthPolicy (requiredRoles, requiresBusinessScope, dataClassification) and ToolExecutionContext (auth, readScope, correlationId) — contracts the policy evaluator (Prompt 11) and handlers (Prompt 13+) consume.
    • ToolRegistryregister / has / get / list (registration order preserved) with DuplicateToolError on name collisions; describe() renders each input schema to JSON Schema (additionalProperties: false) for tools/list.
    • validateToolInput() — strict parse (unknown fields rejected) returning either the typed data or a deterministic VALIDATION_ERROR payload (code, message, field-path issues).
  • Tests — registration, duplicate rejection, ordering, describe() JSON Schema, and validation (valid, missing-required, unknown-field, type-mismatch). 120 tests total.

Validation

  • yarn workspace @accounter/mcp-server test → 120 passed
  • yarn workspace @accounter/mcp-server lint / typecheck / build → pass

Notes

  • Registry and validation are not yet wired into POST /mcp — the smoke tool still answers tools/list/tools/call directly. 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 / requiredRoles are defined here but enforced in Prompt 11.

🤖 Generated with Claude Code


Generated by Claude Code

@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 21, 2026 15:18 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 21, 2026 15:18 — with GitHub Actions Inactive

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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.

return this.list().map(tool => ({
name: tool.name,
description: tool.description,
inputSchema: z.toJSONSchema(tool.inputSchema) as Record<string, unknown>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

Use the explicitly imported zodToJsonSchema function instead of the non-standard z.toJSONSchema to ensure compatibility, type safety, and avoid runtime errors.

Suggested change
inputSchema: z.toJSONSchema(tool.inputSchema) as Record<string, unknown>,
inputSchema: zodToJsonSchema(tool.inputSchema) as Record<string, unknown>,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Declining: z.toJSONSchema() is a built-in zod v4 API (the repo pins zod@4.4.3), not global monkey-patching. Verified against the installed version — it returns a valid JSON Schema with additionalProperties: false. Pulling in the separate zod-to-json-schema package (the zod v3 approach) would add an unnecessary dependency. Keeping the built-in.


Generated by Claude Code

@@ -0,0 +1,169 @@
import { z } from 'zod';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

To support converting Zod schemas to JSON Schema without relying on non-standard global monkey-patching, import zodToJsonSchema explicitly.

Suggested change
import { z } from 'zod';
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';

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
@gilgardosh
gilgardosh force-pushed the claude/mcp-prompt-10-tool-registry branch from cc1631c to 94a27f0 Compare July 21, 2026 16:12
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 21, 2026 16:13 — with GitHub Actions Inactive
@gilgardosh
gilgardosh temporarily deployed to accounter-fullstack July 21, 2026 16:13 — with GitHub Actions Inactive
@gilgardosh gilgardosh self-assigned this Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants