Agents & MCP
MCP Server Authoring
Use when building a Model Context Protocol server that exposes tools and resources to Claude or another MCP client. Covers designing a tool schema the model will call correctly, returning errors the model can act on, and testing locally over stdio. Also use when a tool is registered but the model never calls it, or a client reports the server as failed. If you are writing an MCP server, use this skill.
1 file
Description
Use when building a Model Context Protocol server that exposes tools and resources to Claude or another MCP client. Covers designing a tool schema the model will call correctly, returning errors the model can act on, and testing locally over stdio. Also use when a tool is registered but the model never calls it, or a client reports the server as failed. If you are writing an MCP server, use this skill.
name: mcp-server-authoring description: Use when building a Model Context Protocol server to expose tools and resources to Claude or other MCP clients. Covers tool schema design, error handling, and local testing over stdio.
MCP Server Authoring
When to trigger
Creating an MCP server, adding a tool to one, or debugging why a client will not call your tool or gets a bad result.
Design the tools first
An MCP tool is a function the model chooses to call. The name and description are prompt engineering, not documentation.
- Name tools by the action:
search_orders,create_invoice. NotordersorinvoiceTool. - Write the description for the model. State when to use the tool and when not to. One or two sentences.
- Make the input schema strict. Required fields required, enums for closed sets, short descriptions per field. A loose schema causes bad calls.
Minimal server (TypeScript, stdio)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({ name: "orders", version: "1.0.0" });
server.tool(
"search_orders",
"Find orders by customer email. Use when the user asks about a specific customer's purchases.",
{ email: z.string().email(), limit: z.number().int().max(50).default(10) },
async ({ email, limit }) => {
const rows = await db.orders.findByEmail(email, limit);
return { content: [{ type: "text", text: JSON.stringify(rows) }] };
}
);
await server.connect(new StdioServerTransport());
Error handling
Return errors as tool results the model can read, not thrown exceptions that kill the transport. On failure return { content: [{ type: "text", text: "No customer found for that email." }], isError: true } so the model can recover.
Testing
- Test over stdio with the MCP inspector before wiring it to a client.
- Log to stderr, never stdout. On a stdio transport, stdout is the protocol channel and any stray write corrupts the JSON-RPC stream.
Pitfalls
- A
console.loganywhere in a stdio server breaks it silently. Route all logging to stderr. - Returning huge blobs (a full table dump) blows the context budget. Paginate and summarize in the tool.
- Vague tool descriptions are the top cause of a model never calling your tool. Rewrite the description before adding retries.
Related
Added 2026-07-01. Back to the Skill Library.

Get Smarter About AI Dev
New tutorials, open-source projects, and deep dives on coding agents - delivered weekly.