openapi-agentify
This MCP server exposes three read-only, idempotent tools for an orders API.
List orders via
GET /v1/orders, with optional page size (limit1–100), paginationcursor, andstatusfilter.Retrieve a single order by ID via
GET /v1/orders/{id}.Generate a sales report for paid orders, including order count, total amount in cents, and per-SKU order counts.
All tools are marked non-destructive and closed-world; none modify data.
Adds Express middleware that serves the MCP endpoint and discovery documents, dispatching tool calls in-process through the existing Express app and its routes.
Registers as a Fastify plugin to expose MCP and discovery endpoints, dispatching tool calls via Fastify's app.inject() through existing routes.
Adds Hono middleware to expose MCP and discovery endpoints, dispatching tool calls through app.fetch() with the app's bindings.
Integrates with Next.js App Router through a catch-all route and wrap(), exposing MCP and discovery endpoints and dispatching tool calls through route module GET/POST handlers.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@openapi-agentifylist the tools from my OpenAPI spec"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
openapi-agentify
Turn the API you already run into an MCP server: one middleware, from your OpenAPI spec, in-process.
Add one middleware to your Express, Hono, Fastify or Next.js app. It reads
your OpenAPI spec (or drafts one from your routes) and serves /mcp with one
tool per operation. A tool call becomes the HTTP request the operation
describes and runs in-process through your own routes: your auth
middleware, your validation and your handlers, the ones you already test.
There is no second server, no proxy and no extra port.
It also serves what agents read before they call: llms.txt, an MCP server
card, the AI Catalog, your spec with agent hints, and /agent, an index of
all of them. And it scores how well a model will understand your spec, then
suggests the fixes.
Quick start (30 seconds)
cd your-api
npx openapi-agentify init --write
npm install
npm run devinit detects the framework and the file that creates your app, finds your
spec (or drafts one from your routes), scores it, writes agentify.config.ts
and agentify.tools.json (reads on, writes off), and adds two lines to your
entry file. Then point any MCP client at http://localhost:3000/mcp:
npx @modelcontextprotocol/inspector # Streamable HTTP, http://localhost:3000/mcpOr by hand, on Express:
import express from "express";
import { agentify } from "openapi-agentify/express";
import { defineConfig } from "openapi-agentify";
const app = express();
app.use(agentify(defineConfig({ name: "Acme Orders API", spec: "./openapi.yaml" }))); // first, before body parsers and auth
app.use(express.json());
app.get("/v1/orders/:id", requireApiKey, getOrder); // unchangedRelated MCP server: Graft
Frameworks
Framework | Versions | Mount | Tool calls reach your app through |
Express | 4 and 5 |
| an in-memory |
Hono | 4 |
|
|
Fastify | 5 |
|
|
Next.js (App Router) | 15 and 16 |
| the route module's exported |
Need a socket (TLS client certificates) or another language? dispatch: { mode: "http", url } sends tool calls over loopback HTTP instead.
How it fits
npx openapi-agentify init reads package.json, finds or drafts the spec,
scores agent readiness, writes agentify.config.ts
│
▼
your app (Express · Hono · Fastify · Next.js)
┌───────────────────────────────────────────────────────────────────────┐
│ app.use(agentify(config)) │
│ POST /mcp MCP (2026-07-28 and 2025-xx) │
│ GET /openapi.json, /openapi.yaml your spec, with agent hints │
│ GET /llms.txt, /llms-full.txt for models reading docs │
│ GET /mcp/server-card MCP server card (SEP-2127 draft) │
│ GET /.well-known/ai-catalog.json where the card is │
│ GET /agent one index of all of the above │
│ on your routes: agent detection · Idempotency-Key · your hooks │
│ │
│ tool call ──in-process──▶ your existing route handler + auth │
└───────────────────────────────────────────────────────────────────────┘What's included
MCP endpoint: Streamable HTTP, JSON responses, stateless; both protocol eras (2026-07-28 with
server/discoverand theMcp-Method/Mcp-Nameheader rules, and 2025-11-25/06-18/03-26 withinitialize). Tested against the official MCP TypeScript client in both.In-process dispatch: path, query, header and JSON body parameters mapped from the spec; only declared parameters reach your app; credentials and
X-Agentify-*headers can't be forged by the model; non-2xx answers become tool errors the model can read; timeouts and response caps.Tools from your spec: OpenAPI 3.0 and 3.1 (JSON or YAML, a file, a URL or read from your running app), validated with JSON Schema 2020-12 in linear time, plus code tools (
defineTool()with a Zod schema) for work that isn't a route.agentify.tools.jsonis the reviewable list: names, descriptions, which writes are enabled.Discovery documents:
llms.txtandllms-full.txt(llmstxt.org), the MCP server card (SEP-2127), the AI Catalog,/openapi.jsonand/openapi.yamlwithx-agentifyhints,/agent,Link:headers on/. Cached with ETags; each one can be switched off.Agent readiness check:
npx openapi-agentify checkscores what a model will get wrong about your API, 0–100, over 13 weighted rules (operationIds, descriptions, parameter docs, response schemas, auth, examples, errors, pagination, idempotency, enums, tool count).improvewrites rule-based fixes intoagentify.tools.json(never your spec);--acceptserves them.A spec drafter: no spec?
draftreads your Express, Hono (includingzValidatorand@hono/zod-openapi), Fastify or Next.js source with TypeScript's parser (your code is never run) and writes OpenAPI 3.1.On your routes: forged
X-Agentify-*headers stripped, agents detected (a dated User-Agent list and Web Bot Auth),Idempotency-Keyhonoured on agents' writes, paths a router could resolve differently refused.Hooks for everything else:
authenticate,beforeToolCall,afterToolCall,guardRoute,describe, extrasurfaces. Bring your own API keys, OAuth, rate limits, metering or payments. See docs/extending.md.
The CLI
npx openapi-agentify init [dir] # the whole setup (--yes, --write, --spec, --url, --include-writes…)
npx openapi-agentify check [spec|url] # the readiness report (--json, --fail-under 80)
npx openapi-agentify improve [--accept] # suggested fixes, into agentify.tools.json
npx openapi-agentify draft [dir] # a draft OpenAPI 3.1 from your routes
npx openapi-agentify sync # the spec changed: update tools, keep your editsnpx openapi-agentify check --fail-under 80 in CI keeps the score honest.
Configuration
// agentify.config.ts
import { defineConfig } from "openapi-agentify";
export default defineConfig({
name: "Acme Orders API",
description: "Look up, create and refund orders.",
baseUrl: process.env.PUBLIC_URL, // your canonical origin: set it in production
spec: "./openapi.yaml", // or a URL, or "app:/docs/json" (read from your app)
tools: "./agentify.tools.json",
auth: { mode: "passthrough" }, // agents send the credentials your API accepts
anonymous: "allow", // | "deny"
detect: { include: ["/v1/*"] },
dispatch: { mode: "in-process" }, // | { mode: "http", url }
idempotency: true, // | { ttlSeconds, store }
trustProxy: false, // true behind one proxy you run (Vercel: true)
hooks: [], // see docs/extending.md
});Every option is typed and commented in src/config.ts.
Security notes
Your auth stays in charge. By default the MCP endpoint forwards the caller's own credentials and your API decides. Agentify refuses to start if
dispatch: httpwould send them to another origin.Set
baseUrlin production. It names your origin in every document, and it's the only origin whose pages may call/mcpfrom a browser (plusallowedOrigins). Without it, browser requests are accepted only from localhost pages on a localhost Host, which stops DNS rebinding.Mount it first, before body parsers and auth, so it can strip forged
X-Agentify-*headers. It never parses a body for your routes.Give id parameters a
pattern(or anenum, aformat): a route outside the spec that matches a template is then not taken for the operation.
Examples
examples/orders-api is the same small Orders API in Express, Hono, Fastify
and Next.js, with its own API-key auth and a deliberately thin spec (it scores
54; improve --accept brings it to 96):
git clone https://github.com/jorgeortega/openapi-agentify && cd openapi-agentify
npm install
npm run demo:hono # or demo:express, demo:fastify, demo:next
curl -s localhost:3000/llms.txtFor MCP clients that start a process instead of calling a URL, npm run demo:stdio serves the Express version over stdio: each message is relayed to
its /mcp, through the same middleware, with the tool definitions
improve --accept wrote (examples/orders-api/stdio.tools.json, readiness
96). The repo's Dockerfile runs it (docker build -t openapi-agentify-demo . && docker run -i --rm openapi-agentify-demo); it is how the demo is listed on
Glama, and it is not part of the npm package.
Docs
Extending: the hooks, with a worked example
Dispatch: how a tool call reaches each framework
Surfaces: every discovery document and its standard
Readiness: the rules,
improve,syncanddraft
Hosted and paid
openapi-agentify is the open core, MIT-licensed, and stays that way. If you'd rather not run it yourself, or you need the operational parts:
Hosted MCP at latitude10.tech/platform/mcp: your OpenAPI document served as an MCP server, with sealed upstream credentials, analytics and sign-in through your identity provider.
The Latitude 10 Agentify kit (paid) builds on this package with its own agent keys, OAuth, per-agent rate limits, usage metering and x402 pay-per-call, all attached through the hooks in docs/extending.md.
Contributing
Issues and pull requests are welcome: see CONTRIBUTING.md. Security reports: SECURITY.md.
License
MIT © 2026 October Moon LLC
Available Tools
3 toolsgetOrderGet an orderARead-onlyIdempotent
Gets one order by its id: customer email, items (sku, qty, priceCents), totalCents, status (open, paid or refunded) and createdAt. Returns the API's 404 when there's no such order; use listOrders to find ids. Calls need the Orders API key (x-api-key header; the demo's is demo-customer-key-0001); without it the tool returns the API's 401.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | The order's id, such as ord_1002, as listOrders returns it. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations only declare the read-only/idempotent profile, so the description adds real value: required auth (x-api-key header), the concrete demo key, the exact 401 failure mode without it, and the 404 behavior for unknown ids. That is more than an agent could infer from annotations alone.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three dense sentences ordered by importance: what it returns, then failure modes, then auth. The demo API key is mildly incidental detail, but it is tied directly to the 401 sentence and earns its place for a sandbox environment.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No output schema exists, yet the description enumerates the return payload fields and status enum values, and covers both the 404 and 401 paths plus the auth prerequisite. An agent has everything needed to call this correctly and interpret failures.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% and the schema itself documents the id format (ord_1002, as listOrders returns it), so the description's mention of id adds no new syntax or constraints. Baseline 3 applies since the schema carries the parameter burden.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Names a specific verb and resource ('Gets one order by its id') and enumerates the returned fields (customer email, items, totalCents, status, createdAt), which makes it unmistakably distinct from the sibling listOrders that returns many orders.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly routes the agent to listOrders when it needs to discover ids, and states the 404 outcome for missing orders. It stops short of a full when-to-use/when-not matrix, but the boundary with listOrders is clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
getSalesReportSales reportARead-onlyIdempotent
Totals for paid orders only (open and refunded orders are left out): the number of orders, their sum in cents (currency USD), and per SKU how many paid orders include it. Takes no arguments. Returns { currency, orders, totalCents, bySku }. Calls need the Orders API key (x-api-key header; the demo's is demo-customer-key-0001); without it the tool returns the API's 401.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnlyHint, idempotentHint, destructiveHint=false, and openWorldHint=false. The description adds meaningful context beyond that: authentication via x-api-key, the demo key value, and the 401 failure mode without a key.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
It is a single tight paragraph that front-loads the scope and output fields, then adds the auth caveat. Every sentence carries useful information with no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a no-argument report tool with no output schema, the description supplies the filter rules, exact return keys, and auth/error behavior. Nothing an agent needs in order to call this correctly appears to be missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
There are zero input parameters, and the description confirms 'Takes no arguments.' With no parameters to document, the baseline is 4; the description appropriately spends its space on return shape instead.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening clause states a specific computation over a specific resource — paid orders — and enumerates the three outputs. This clearly separates it from listOrders and getOrder, which retrieve individual orders rather than aggregate sales.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It specifies the inclusion scope explicitly: paid orders only, with open and refunded orders excluded. That gives clear context for when the result is appropriate, though it does not explicitly route the agent to or away from sibling tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
listOrdersList ordersARead-onlyIdempotent
Lists orders, oldest first, optionally filtered by status, 20 per page unless limit says otherwise. Use it to find an order's id before calling getOrder. Returns { orders, nextCursor }; pass nextCursor back as cursor for the next page (null on the last one). Calls need the Orders API key (x-api-key header; the demo's is demo-customer-key-0001); without it the tool returns the API's 401.
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | How many orders to return, 1 to 100 (default 20). | |
| cursor | No | Where to continue from: the cursor the previous page returned (nextCursor). Leave it out for the first page. | |
| status | No | Only orders with this status. One of: open, paid, refunded. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile (readOnly, idempotent, non-destructive, closed-world), yet the description still adds real value: default ordering, default page size of 20, the exact return shape, pagination termination (null cursor), and the auth requirement including 401 behavior. The only omission is what happens with an invalid cursor or status, which is minor.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, front-loaded with purpose and scope, then pagination mechanics, then auth. No restatement of the name or title and no filler; even the demo key serves a concrete calling need.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description compensates by spelling out the returned { orders, nextCursor } shape and how to consume it. Auth, defaults, and ordering are all covered, so an agent can call this correctly on the first attempt.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds cross-parameter meaning by tying cursor to the nextCursor return value and restating the 20-per-page default that limit overrides. It stops short of describing the status enum values, which the schema already covers.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource (lists orders) plus the sort order and filter capability. It also explicitly distinguishes itself from the sibling getOrder by framing itself as the id-lookup step that precedes it.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Gives a concrete when-to-use rule: 'Use it to find an order's id before calling getOrder,' which tells the agent the prerequisite relationship with the sibling. Pagination usage is also prescribed (pass nextCursor back as cursor), leaving nothing to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
3 tool updates
v0.1.1- Removed
get2 - Added
getOrder - Changed
listOrders3 fields changed- added
Input schema / properties / cursor / descriptionAdded value: +"Where to continue from: the cursor the previous page returned (nextCursor). Leave it out for the first page." - changed
Input schema / properties / limit / descriptionPrevious value: -"Page size."New value: +"How many orders to return, 1 to 100 (default 20)." - added
Input schema / properties / status / descriptionAdded value: +"Only orders with this status. One of: open, paid, refunded."
3 tool updates
v0.1.0- First observed
get2 - First observed
getSalesReport - First observed
listOrders
TDQS
Scored across 3 tools
The three tools have clearly distinct purposes: listOrders discovers and paginates orders, getOrder retrieves one order's details, and getSalesReport aggregates paid-order totals. The descriptions explicitly guide when to use listOrders before getOrder, leaving no meaningful overlap.
All tools follow a predictable camelCase verb+noun pattern: listOrders, getOrder, getSalesReport. The singular/plural noun choices match the operation semantics.
Three tools are a reasonable, well-scoped set for a minimal read-only orders/reporting interface. It is slightly thin, but each tool earns its place and there is no bloat.
The surface covers listing, single-order retrieval, and sales aggregation, but it lacks order creation, status updates, refunds, and deletion. For an Orders API domain, these are notable gaps that would leave agents unable to perform common lifecycle operations.
Maintenance
Related MCP Connectors
Host any HTTP API as a standard MCP server; auto-distribute to 12+ mainstream agent platforms.
MCP server for your apps' tools and custom tools, plus hosted AI agents and approval-gated workflows
The OpenRouter for tools. One MCP connection gives any AI agent 254 hosted tools, pay per call.
Model Context Protocol server for the Apideck Unified API. Connect any MCP-compatible agent framework to 100+ accounting systems, HRIS platforms, file storage providers, and more through one integration. More information https://www.apideck.com/mcp-server
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceExposes Express.js API endpoints as MCP tools, preserving existing schemas and authentication behavior. Supports streaming responses and can be mounted directly to existing Express apps or run as a standalone gateway.11 npm1MIT
- AlicenseNot gradedqualityCmaintenanceEnables building agent-ready APIs that expose tools as both HTTP and MCP endpoints from a single server definition, with automatic OpenAPI, discovery docs, and interactive API reference.5Apache 2.0
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to call existing Node.js API routes as MCP tools across NestJS, Express, Fastify, Koa, Hono, and AdonisJS, preserving existing auth, validation, and middleware.11 npm1MIT
- AlicenseNot gradedqualityAmaintenanceTurns existing FastAPI routes (or any OpenAPI spec) into owner-approved MCP tools served from one governed endpoint, keeping tools hidden until an owner reviews a diff and enables them. Agents get incremental discovery via meta-tools, response field projection, per-caller API-key policy, PII redaction, and a JSON-lines audit log.160 PyPI1MIT