New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@ata-project/mcp

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ata-project/mcp

JSON Schema validation for the MCP TypeScript SDK that works where code generation is blocked.

latest
Source
npmnpm
Version
0.1.4
Version published
Weekly downloads
192
300%
Maintainers
1
Weekly downloads
 
Created
Source

@ata-project/mcp

A JSON Schema validation provider for the Model Context Protocol TypeScript SDK, for runtimes that do not allow code generation.

Why this exists

The MCP SDK validates tool call arguments against each tool's inputSchema, and exposes jsonSchemaValidator as the extension point for how that is done. It ships two providers: one on ajv, and one on @cfworker/json-schema. The second one is there because ajv compiles a schema with new Function, and Cloudflare Workers, Vercel Edge and anything under a strict CSP do not allow that.

This is a third provider for the same slot. It needs no code generation either, and it is faster in that environment.

Use

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
import { AtaJsonSchemaValidator } from '@ata-project/mcp'

const server = new McpServer(
  { name: 'my-server', version: '1.0.0' },
  { validator: new AtaJsonSchemaValidator() }
)

Nothing here imports the SDK. The interface is structural, so the only dependency is the validator itself.

On an edge runtime, compile first

The provider above compiles each tool's schema on first use. That is the right trade on a server and the wrong one on a Worker, where an isolate cold starts often and the per-call microseconds are invisible next to a model round trip.

Five tool schemas, minified and gzipped, and cold start measured as module evaluation plus the first validation in a fresh process:

bundlecold start
compiled ahead of time6.6 kB0.74 ms
cfworker provider6.4 kB1.36 ms
this provider at runtime85 kB12.07 ms

So compile the tools at build time and serve them:

// build step
import { compileTools } from '@ata-project/mcp/build'

await compileTools({
  tools: { search: searchSchema, send_email: sendEmailSchema },
  outDir: './compiled',
})
// worker
import { AtaAotJsonSchemaValidator } from '@ata-project/mcp/aot'
import tools from './compiled/index.mjs'

const server = new McpServer(info, { validator: new AtaAotJsonSchemaValidator(tools) })

Import it from @ata-project/mcp/aot on a Worker, not from the main entry. The main entry holds the runtime provider too and so requires ata-validator, and a bundler brings the whole engine along with it: on a Worker made with the SDK and Hono that was 1.2 MB of source, more than the SDK itself. The /aot entry requires nothing but the key and the message renderers. Under onMissing: 'compile' it needs options.fallback, a provider to hand a missing schema to; the main entry's class supplies the runtime provider for that by itself.

A whole server in this shape, with Hono and the SDK on Workers, tests and the numbers measured, is at ata-core/mcp-ata-workers-starter.

The lookup is by a hash of the schema rather than by name or by object identity. A name has to be kept in step by hand and identity does not survive a bundler, while the hash comes from the schema itself: if the server declares a schema that was never compiled, getValidator says so instead of validating against the wrong one. Pass onMissing: 'compile' to fall back to compiling at run time, though that needs code generation and will not work in the runtime this exists for.

A constructor you registered yourself in withKeywords.CONSTRUCTORS cannot be compiled, because a standalone module imports nothing and there is nowhere to put your class. Those schemas are refused rather than silently compiled without the check.

Verified inside the SDK

e2e-sdk.mjs runs a real Server and Client from @modelcontextprotocol/sdk (1.30.0) over an in-memory transport, with a tool declaring an outputSchema. With code generation allowed, the SDK's default provider, the cfworker provider and this one accept the same valid result and reject the same invalid one with the same MCP error. With code generation blocked (node --disallow-code-generation-from-strings, the restriction Workers and strict-CSP pages enforce), the default provider fails the tool call even when the result is valid; cfworker and ata keep answering.

Measured

Node 25, ata-validator 1.36.0, an eight-field tool schema with a nested array of objects, 20000 calls per round, medians of 7 interleaved rounds.

With code generation blocked, which is the case this provider is for:

providervalidinvalid
ata181 ns509 ns
cfworker2906 ns869 ns
ajvthrows EvalErrorthrows EvalError

With code generation allowed:

providervalidinvalid
ata19 ns192 ns
ajv54 ns34 ns
cfworker2845 ns860 ns

ajv is the one to beat on the failing path when it can compile, and it is not beaten there. Rejecting is where it stops early and reports almost nothing; the numbers above include building the message string, which is the work a provider actually does.

Error messages

A tool call that fails validation usually goes back to the model to be retried, so the message is not a log line, it is part of the next prompt.

The default wording matches the providers the SDK already ships, so swapping does not change the strings anything downstream is reading:

/limit: must be <= 100

detailed says what was expected and what arrived, which is what a model needs in order to fix the call rather than guess at it:

new AtaJsonSchemaValidator({ detailed: true })
/mode: expected one of ["read", "write", "append"], found "delete"
/limit: expected ≤100, found 250

I have not measured that this reduces retries. It is a mechanism, not a result.

Options

optiondefaultmeaning
allErrorsfalsereport every failure instead of stopping at the first
detailedfalsemessages name the expectation and the value that arrived
formatsnonecustom format checkers, passed through to ata

Drop-in

npm test compares this provider's verdicts against both of the SDK's own providers over 280 tool calls each, and checks that the verdicts are identical with code generation switched off. A provider that quietly accepted a call the others rejected would be worse than no provider at all.

Keywords

mcp

FAQs

Package last updated on 15 Oct 2026

Related posts