Skip to content

Security hardening: remove eval() from MCP plugin#537

Open
corinagum wants to merge 2 commits intomainfrom
cg/mcp-eval-removal
Open

Security hardening: remove eval() from MCP plugin#537
corinagum wants to merge 2 commits intomainfrom
cg/mcp-eval-removal

Conversation

@corinagum
Copy link
Copy Markdown
Collaborator

Summary

Removes eval() from McpPlugin.use(prompt) by replacing the external json-schema-to-zod library (which emits JavaScript source that must be evaluated) with a local converter that builds Zod schemas directly.

Addresses security finding #13 (RCE via eval() in MCP plugin). TypeScript-only; Python and C# MCP plugins are not affected.

Approach

The MCP TypeScript SDK requires Zod schemas for server.tool() registration — we can't skip conversion the way the PY/C# SDKs do. Rather than hardening the eval call (e.g. scoping with new Function), we eliminate code generation entirely:

  • New external/mcp/src/json-schema-to-zod.ts walks a Schema object and returns a live z.ZodTypeAny. No string emission, no eval, no new Function.
  • json-schema-to-zod removed from external/mcp/package.json dependencies.
  • McpPlugin.use(prompt) now calls the local converter and defensively checks instanceof z.ZodObject before accessing .shape.

Coverage scope is bounded by the Schema type in @microsoft/teams.ai (a closed, typed subset of JSON Schema). Constructs not expressible in that type — anyOf, oneOf, allOf, const, patternProperties, not — are not supported and not needed. \$ref is in the type but unused in practice; the converter throws a clear error if it appears.

Context and alternatives considered: design/mcp-eval-removal-options.md.

Behavior change to be aware of

The prior json-schema-to-zod library silently ignored min, max, and multipleOf on NumberSchema (those are non-standard JSON Schema keywords; the spec uses minimum/maximum). The new converter honors them, matching the Schema type's stated contract.

Customers whose schemas already used these constraints will see:

  • If the LLM produces an in-range value: no change.
  • If the LLM produces an out-of-range value: the tool call now returns a structured InvalidParams error (e.g. "Number must be less than or equal to 85") instead of silently passing invalid data to the handler. Modern MCP clients surface this error back to the LLM, which typically retries with a corrected value.

Example — a thermostat tool with temperature: { type: 'number', min: 50, max: 85 }:

  • Before: LLM emits 95, handler receives 95, thermostat may or may not clamp.
  • After: LLM emits 95, MCP returns an InvalidParams error, LLM typically retries with 85, handler runs with a valid value.

For the narrow case where a schema has typo'd bounds (e.g. min: 10, max: 5), every call will now fail — previously silently ignored.

Copilot AI review requested due to automatic review settings April 21, 2026 23:14
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR removes dynamic code evaluation from the TypeScript MCP plugin by replacing the json-schema-to-zod dependency (string-emitting) with a local converter that builds Zod schemas directly, mitigating an RCE vector via eval().

Changes:

  • Replaced eval(jsonSchemaToZod(...)) usage in McpPlugin.use() with a local jsonSchemaToZod() implementation that returns live Zod types.
  • Removed json-schema-to-zod from external/mcp dependencies and updated the root lockfile accordingly.
  • Added unit tests for the new JSON-Schema-to-Zod converter.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
package-lock.json Updates lockfile to drop json-schema-to-zod and reflect workspace dependency changes.
external/mcp/package.json Removes the json-schema-to-zod dependency from the MCP package.
external/mcp/src/plugin.ts Stops using eval() and switches MCP tool registration to the local converter output.
external/mcp/src/json-schema-to-zod.ts New local Schema→Zod converter implementation (no codegen/eval).
external/mcp/src/json-schema-to-zod.spec.ts Adds Jest coverage for converter behavior and edge cases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread external/mcp/src/json-schema-to-zod.ts
Comment thread external/mcp/src/json-schema-to-zod.ts
Comment thread external/mcp/src/plugin.ts
Comment thread external/mcp/src/json-schema-to-zod.ts Outdated
@corinagum corinagum force-pushed the cg/mcp-eval-removal branch from 008ddbf to e56ba3c Compare April 23, 2026 22:57
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