Shipping an MCP server that works in every agent
Source: docs/guide.md · v0.1.2
An MCP server is one program that many agents start. Each agent reads the tool list a little differently. A schema that one agent accepts can make another drop the tool, with no message to the user.
yokoten mcp starts your server over stdio and reads its tool list. It reports each
trap with a what, a why and a fix. Run it before a release, and in CI beside your tests.
yokoten mcp
yokoten mcp --json
yokoten mcp --command "node src/server.ts"
It reads .mcp.json, .vscode/mcp.json and mcp.json from the project root. It starts
each stdio server, calls initialize, then tools/list. It stops each server when it is
done. A server over HTTP is named and skipped: this check starts only stdio servers.
What it checks
| Trap | What the check looks for | Fix |
|---|---|---|
| A schema Claude rejects | A root anyOf, oneOf or allOf |
Put the branches under one property of a root object |
| A schema Gemini rejects | A $ref or $defs, a list of types, or an array with no items |
Inline the schema, use one type, and add items |
| OpenAI strict mode | A property that is not in required |
Add every property to required |
| A dropped property | A property name outside [A-Za-z0-9_.-], or longer than 64 |
Rename it |
| A dropped tool | A tool name longer than 64 | Shorten it |
| A tool the client never shows | 40 tools or more | Merge tools, or keep 39 or fewer |
| A tool the client cannot gate | A missing readOnlyHint, destructiveHint, idempotentHint or openWorldHint |
Set all four, each as true or false |
| An empty answer in Cursor | A tool with outputSchema that returns only structuredContent |
Return a text content item too |
| A broken session | A stdout line that is not JSON-RPC, is not UTF-8, or ends with CRLF | Write logs to stderr, write UTF-8, and end each line with LF |
| A server that never starts on Windows | A config command such as npx or bar.cmd |
Start it through cmd /c |
| A server that does not answer | No reply to initialize or tools/list |
Run it by hand, and fix what stderr says |
The check reports the tool count, the total size of the tool definitions, and the longest description. A large tool list costs every client context on every turn. Keep each tool small, and keep its description short.
The outputSchema rule
A tool may declare outputSchema. Clients that understand it read structuredContent.
Other clients read only the text content. A result with just one of the two looks empty
somewhere.
Return both:
{
"structuredContent": { "reply": "ok" },
"content": [{ "type": "text", "text": "ok" }]
}
yokoten mcp calls a tool only when it is safe to try: the tool sets
readOnlyHint: true, declares an outputSchema, and requires no argument. Other tools
are left alone. The check says how many it read.
Windows
Windows cannot start npx or a .cmd file the way a Unix shell can. A config entry
that names one needs cmd:
{
"mcpServers": {
"demo": {
"command": "cmd",
"args": ["/c", "npx", "-y", "demo-mcp"]
}
}
}
The check warns about such an entry. A warning never fails the check: the same project may run on a machine that is not Windows.
stdout is for JSON-RPC only
The client reads stdout as a stream of JSON-RPC messages. One log line, one progress bar, or one carriage return breaks the session for every client.
- Write logs to stderr.
- Write one JSON-RPC message per line.
- End each line with a line feed, not CRLF.
- Write UTF-8 only. A Windows code page such as cp1252 corrupts a name.
In CI
Add the check after your tests:
yokoten mcp --json
It exits 1 when a tool breaks a rule. It exits 0 when the server is clean or the project has no MCP config. A Windows warning is reported and does not change the exit code.