Skip to content
Development
Skill

/mcp-builder

Use when creating a new MCP (Model Context Protocol) server, extending an existing one, or debugging tool discoverability/performance. Guides through research → implementation → test → eval phases with TypeScript-first guidance matching our stack. Trigger on phrases like "build

From plugin
session-orchestrator
5144 skills14 agents26 commands10 hooks
+1
Install
$ npx -y skills add Kanevry/session-orchestrator --skill mcp-builder --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/mcp-builder

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when creating a new MCP (Model Context Protocol) server, extending an existing one, or debugging tool discoverability/performance. Guides through research → implementation → test → eval phases with TypeScript-first guidance matching our stack. Trigger on phrases like "build

SKILL.md

mcp-builder.SKILL.md
name: mcp-builder
description: Use when creating a new MCP (Model Context Protocol) server, extending an existing one, or debugging tool discoverability/performance. Guides through research → implementation → test → eval phases with TypeScript-first guidance matching our stack. Trigger on phrases like "build an MCP server", "expose X as an MCP tool", "write MCP tools for Y", "integrate Z via MCP".
model: sonnet

MCP Server Development

Adapted from [anthropics/skills/mcp-builder](https://github.com/anthropics/skills/tree/main/skills/mcp-builder). MCP-server quality is measured by how well it lets LLMs accomplish real-world tasks — not by endpoint count.

Stack default for our projects

  • **Language:** TypeScript (matches our stack; static typing + Zod schemas + good LLM code-gen)
  • **Transport:** `stdio` for local tools, **Streamable HTTP (stateless JSON)** for remote
  • **SDK:** [`@modelcontextprotocol/sdk`](https://github.com/modelcontextprotocol/typescript-sdk)
  • **Package manager:** pnpm (never npm/yarn in our repos)

Phase 1 — Research & Plan

1.1 Design principles

**API coverage vs. workflow tools.** Balance comprehensive endpoint coverage with specialized workflow shortcuts. Default to coverage unless you have a clear reason — agents compose basic tools well; workflow tools ossify.

**Tool naming & discoverability.** Consistent prefix + action verb. Examples:

  • `github_create_issue`, `github_list_repos`
  • `gitlab_search_issues`, `gitlab_close_mr`

**Context management.** Return focused, paginated data. Agents suffer when a single tool call floods context.

**Actionable error messages.** Errors must guide the next action:

❌ "Invalid input"
✅ "Field 'project_id' is required. Call gitlab_list_projects to enumerate available IDs."

1.2 Read the spec

  • Sitemap: `https://modelcontextprotocol.io/sitemap.xml`
  • Append `.md` to any page URL for markdown (e.g. `https://modelcontextprotocol.io/specification/draft.md`)

Focus on: tool definitions, resource definitions, transport mechanisms.

1.3 Load SDK docs

  • TS SDK README: `https://raw.githubusercontent.com/modelcontextprotocol/typescript-sdk/main/README.md`
  • Python SDK README: `https://raw.githubusercontent.com/modelcontextprotocol/python-sdk/main/README.md`

Fetch via WebFetch only when needed — don't dump entire docs into context upfront.

1.4 Plan implementation

  • Review the target service's API docs (auth, core endpoints, data models)
  • List endpoints by priority — most-common operations first
  • Identify destructive vs. read-only operations (matters for tool annotations)

Tool-Hosting Pattern — In-Process vs Stdio MCP

Before writing a line of implementation code, choose a hosting pattern. The wrong choice cannot be refactored cheaply once tooling is wired.

Decision tree

≤ 5 tools AND latency-critical (<50ms tool resolution)?
│
├─ Yes → tools share the SDK process AND no external auth required?
│        │
│        ├─ Yes → In-process @tool decorator (single-process, sub-ms resolution)
│        └─ No  → Stdio MCP Server
│
└─ No  → Stdio MCP Server
         (≥ 6 tools, external auth, language/runtime mismatch, long-lived process)

In-process @tool decorator (Python — anthropics/claude-agent-sdk-python)

Use `create_sdk_mcp_server` when your tools live entirely inside the SDK process and you need the lowest possible latency. Source reference: [`examples/mcp_calculator.py` L11–99](https://github.com/anthropics/claude-agent-sdk-python/blob/main/examples/mcp_calculator.py).

from claude_agent_sdk import tool, create_sdk_mcp_server

@tool(name="add", description="Add two numbers", input_schema={"a": int, "b": int})
async def add(args):
    return {"content": [{"type": "text", "text": str(args["a"] + args["b"])}]}

server = create_sdk_mcp_server(name="calc", version="1.0.0", tools=[add])

In-process registration (TypeScript — @modelcontextprotocol/sdk)

Our default stack uses `McpServer.registerTool()` from `@modelcontextprotocol/sdk`. The inline Zod schema is parsed at registration time — no separate schema file needed for small tool sets.

import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';

const server = new McpServer({ name: 'calc', version: '1.0.0' });

server.registerTool(
  'add',
  {
    title: 'Add two numbers',
    inputSchema: { a: z.number(), b: z.number() },
  },
  async ({ a, b }) => ({
    content: [{ type: 'text', text: String(a + b) }],
  }),
);

Tool annotations — `readOnlyHint` and `destructiveHint`

Annotations are first-class SDK metadata that Claude and downstream hooks use for permission decisions. Set them on every tool:

server.registerTool(
  'delete-file',
  {
    title: 'Delete a file',
    inputSchema: { path: z.string() },
    annotations: { readOnlyHint: false, destructiveHint: true },
  },
  handler,
);
  • **`readOnlyHint: true`** — signals the tool only reads state; Claude can call it freely without a permission prompt.
  • **`destructiveHint: true`** — signals irreversible side effects; our `pre-bash-destructive-guard` hook and `agents/security-reviewer.md` both elevate review priority for tools carrying this flag. Any tool that deletes, overwrites, or mutates shared state must set this.
  • Missing `destructiveHint: true` on a destructive tool is a known pitfall — see the "Common pitfalls" table below.

Pattern comparison

| Aspect | In-Process @tool | Stdio MCP Server | |--------|-----------------|------------------| | Tool count | ≤ 5 | 6+ | | Latency | Sub-ms resolution | 5–50 ms IPC overhead | | Auth complexity | Shares SDK auth | Separate auth context | | Language constraint | Must match SDK | Any runtime | | Process isolation | None (in-SDK) | Full (separate child) | | Lifecycle | Bound to SDK session | Long-lived independent |

For the **stdio MCP server** implementation path (≥ 6 tools, external auth, or language mismatch), continue wit

Read more
Ships withsession-orchestrator

Give your agents a working rhythm. You type three commands: /session reads your repository, your open issues and the last session, proposes what to work on, and waits for your correction.

Get the whole plugin

Other skills on session-orchestrator.