A mistyped argument name should not get a confident answer to the wrong question. ironmcp makes an MCP tool refuse the undeclared argument with a recoverable error and enforce exactly the arguments it advertises — one guarantee, proven by a single conformance corpus, in Python, TypeScript, PHP, and Dart.
Most MCP SDKs validate a tool call against its declared parameters and silently drop any
argument that was not declared. One added letter (project → projects) yields a confident
answer to a question nobody asked, with no way for the caller to learn their constraint was
ignored. ironmcp makes the server refuse the unknown argument with a bounded, recoverable
message, and advertise exactly what it enforces on every tool.
The contract lives in spec/ and is executable as the language-neutral corpus in
conformance/. Each language kit implements that one contract natively on its
own MCP SDK and passes that one corpus — the way POSIX is a single specification with a
conformance suite and many native libraries, not one library ported everywhere.
# Without ironmcp: search({"query": "x", "projet": "typo"}) drops `projet`, runs search("x"),
# and returns a confident wrong answer.
from ironmcp import strict_server
app = strict_server(name="search", version="1.0.0")
# With ironmcp the same call is refused:
# unknown argument(s): projet. Tool 'search' accepts: query. Nothing was executed ...The guard is the core, but every kit ships the same substrate from one dependency: a self-discovery
registry (enumerate every live ironmcp server; the registry file is byte-identical across all four
languages, so a Python server and a Dart server appear in the same list), a structured
readiness/health contract, hardened serving (bearer + open /healthz + a DNS-rebinding host
guard on by default), and content + clean-quit helpers. ironmcp is a layer that works with
whatever MCP framework you already use — it does not replace it.
- AGENTS.md — the fast path for an AI agent: harden, serve, and prove conformance in copy-paste blocks.
- ROADMAP.md — the kits, what has landed, what is next.
- CONTRIBUTING.md — how to contribute, including the recipe for adding a language kit.
- spec/strict-args.md — the rule and the refusal message.
- conformance/ — the corpus that makes "same guarantee, every language" provable.
| Kit | Package | Registry | Location |
|---|---|---|---|
| Python | ironmcp |
PyPI | kits/python/ |
| TypeScript | ironmcp |
npm | kits/typescript/ |
| PHP | ironmcp/core |
Packagist | kits/php/ |
| Dart | ironmcp |
pub.dev | kits/dart/ |
A kit conforms when a server built with its strict layer passes every case in
conformance/cases/ — and every kit also proves the bare server is refused,
because a corpus never watched to FAIL is theatre. ironmcp runs in a production desktop application's
live MCP server on both Linux and Windows, hardening 60+ tools with zero behaviour change. See
ROADMAP.md for what is next.
from ironmcp import strict_server, serve_http, aassert_enforces_v2
app = strict_server(name="search", version="1.0.0") # refuse unknown args, advertise closed
serve_http(app, token="<secret>", port=8080) # bearer-guarded /mcp + open /healthz
await aassert_enforces_v2(app) # prove every tool: advertise == runtimeThe TypeScript kit mirrors this (strictServer / serveHttp / assertEnforces); see
kits/typescript/README.md.
spec/ the contract (language-agnostic)
conformance/cases/ the corpus (one, owned by no language)
kits/<language>/ one native kit per language, each passing the corpus