{
  "id": "mcp-server-typescript",
  "type": "loadout",
  "title": "MCP server (TypeScript)",
  "summary": "Rules and an add-a-tool skill for building a Model Context Protocol server in TypeScript that agents can actually use well — clear tools, safe inputs, no stdout noise.",
  "description": "Building your own MCP server is the fastest way to give agents a new superpower, and the easiest way to\ngive them a confusing one. This loadout teaches your agent the official TypeScript SDK patterns, how to\ndesign tools that models pick correctly, how to keep a stdio server's output clean, and how to test with\nthe MCP Inspector before plugging it into Claude Code, Cursor or Codex.\n",
  "highlights": [
    "Tool design rules: verb-first names, descriptions written for the model, small typed inputs",
    "stdio hygiene — logs to stderr only, or the protocol breaks",
    "Input validation with zod and errors returned as tool results, not crashes",
    "Add-a-tool skill with an Inspector test loop"
  ],
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "Agent Loadout",
    "url": "https://loadout.jnw-it.com"
  },
  "categories": [
    "ai-agents"
  ],
  "stacks": [
    "mcp",
    "typescript",
    "nodejs"
  ],
  "files": [
    {
      "path": "AGENTS.md",
      "role": "instructions",
      "description": "MCP server project rules.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": "AGENTS.md",
        "codex": "AGENTS.md",
        "cursor": "AGENTS.md",
        "gemini-cli": "AGENTS.md",
        "copilot": "AGENTS.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/AGENTS.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/latest/AGENTS.md",
      "bytes": 2948,
      "lang": "markdown",
      "content": "# AGENTS.md: MCP server (TypeScript)\n\nThis repository is a Model Context Protocol server built with the official TypeScript SDK (`@modelcontextprotocol/sdk`).\nCheck the installed SDK version in `package.json` before writing imports — APIs and import paths have changed between majors.\n\n## Layout\n```\nsrc/index.ts         creates the server, registers tools/resources/prompts, connects the transport\nsrc/tools/<name>.ts  one tool per file: exports a register(server) function\nsrc/lib/             API clients and shared helpers\n```\n\n## Transport\n- **stdio** for local servers launched by the agent. **stdout belongs to the protocol.** Never `console.log` —\n  log with `console.error` (stderr) or a logger pointed at stderr. A stray print corrupts the JSON-RPC stream.\n- **Streamable HTTP** for remote servers. Require auth (bearer token or OAuth) for anything non-public, validate the\n  `Origin` header, and bind to `127.0.0.1` when running locally.\n\n## Tool design (this is the product)\n- The model chooses tools from their **name and description alone**. Write descriptions for the model: what it does,\n  when to use it, what it returns, and any important limits. One to three sentences.\n- Names: `snake_case`, verb first, specific: `search_issues`, `get_issue`, `create_issue_comment`. Not `issues` or `do_action`.\n- Few, focused tools beat many overlapping ones. If two tools are often confused, merge them or sharpen the descriptions.\n- Inputs: small, typed zod schemas with `.describe()` on every field. Use enums for fixed choices, sensible defaults, and bounded numbers (`limit` 1–50).\n- Outputs: concise text the model can use. Return the fields that matter, not the whole raw API response.\n  Paginate or truncate large results and say so in the output (\"showing 20 of 312; pass cursor=... for more\").\n- Mark read-only tools with the `readOnlyHint` annotation and destructive ones with `destructiveHint`.\n\n## Errors and safety\n- Validate inputs (the SDK validates against the zod schema; add business checks on top).\n- Expected failures (not found, rate limited, bad input) are returned as a tool result with `isError: true` and a message\n  the model can act on (\"No issue #42 in repo x/y. Use search_issues to find valid numbers.\"). Do not throw for them.\n- Secrets (API keys) come from environment variables passed in the client's MCP config. Never return them in output.\n- Treat content fetched from the outside world as untrusted: return it as data, never execute it.\n- Destructive tools should require an explicit confirmation field or be left out of v1.\n\n## Testing\n- `npm run build`, then `npx @modelcontextprotocol/inspector node dist/index.js` to list and call tools by hand.\n- Unit-test tool handlers as plain functions, separately from the transport.\n\n## Before you say \"done\"\nBuild passes, the new tool shows up in the Inspector with a clear description, and you have called it at least once with valid and invalid input.\n"
    },
    {
      "path": "CLAUDE.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Claude Code.",
      "skill": null,
      "agent": "claude-code",
      "install_to": {
        "claude-code": "CLAUDE.md",
        "codex": null,
        "cursor": null,
        "gemini-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/CLAUDE.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/latest/CLAUDE.md",
      "bytes": 377,
      "lang": "markdown",
      "content": "# CLAUDE.md\n\n@AGENTS.md\n\n## Claude Code notes\n- Skills from this loadout live in `.claude/skills/`: `add-mcp-tool`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"MCP server (TypeScript)\" v1.0.0 — https://loadout.jnw-it.com/loadouts/mcp-server-typescript/ -->\n"
    },
    {
      "path": "GEMINI.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Gemini CLI.",
      "skill": null,
      "agent": "gemini-cli",
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": null,
        "gemini-cli": "GEMINI.md",
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/GEMINI.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/latest/GEMINI.md",
      "bytes": 378,
      "lang": "markdown",
      "content": "# GEMINI.md\n\n@./AGENTS.md\n\n## Gemini CLI notes\n- Skills from this loadout live in `.agents/skills/`: `add-mcp-tool`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From the Agent Loadout \"MCP server (TypeScript)\" v1.0.0 — https://loadout.jnw-it.com/loadouts/mcp-server-typescript/ -->\n"
    },
    {
      "path": ".cursor/rules/mcp-tools.mdc",
      "role": "rules",
      "description": "Rule applied to tool definition files.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": ".cursor/rules/mcp-tools.mdc",
        "gemini-cli": null,
        "copilot": null
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/.cursor/rules/mcp-tools.mdc",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/latest/.cursor/rules/mcp-tools.mdc",
      "bytes": 832,
      "lang": "markdown",
      "content": "---\ndescription: MCP tool definition conventions\nglobs: src/tools/**/*.ts\nalwaysApply: false\n---\n\n- Tool names are `snake_case`, verb first, unique across the server.\n- Descriptions are written for the model: what, when to use, what it returns, limits. 1–3 sentences.\n- Every input field has a zod type and `.describe(...)`. Bound numbers, use enums for fixed options, give defaults.\n- Return concise `content: [{ type: \"text\", text }]`; summarise large API responses and paginate.\n- Expected failures return `{ isError: true, content: [...] }` with an actionable message. Do not throw.\n- Never write to stdout (`console.log`) — use `console.error`.\n- Annotate read-only tools with `readOnlyHint: true`; destructive ones with `destructiveHint: true`.\n- Secrets come from `process.env`, never from tool arguments or source code.\n"
    },
    {
      "path": "skills/add-mcp-tool/SKILL.md",
      "role": "skill",
      "description": "Designs, implements and tests a new MCP tool.",
      "skill": "add-mcp-tool",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/add-mcp-tool/SKILL.md",
        "codex": ".agents/skills/add-mcp-tool/SKILL.md",
        "cursor": ".agents/skills/add-mcp-tool/SKILL.md",
        "gemini-cli": ".agents/skills/add-mcp-tool/SKILL.md",
        "copilot": ".agents/skills/add-mcp-tool/SKILL.md"
      },
      "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/skills/add-mcp-tool/SKILL.md",
      "raw_latest_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/latest/skills/add-mcp-tool/SKILL.md",
      "bytes": 2635,
      "lang": "markdown",
      "content": "---\nname: add-mcp-tool\ndescription: Design, implement and test a new tool on a TypeScript MCP server. Use when the user asks to add a capability, endpoint or action to their MCP server.\n---\n\n# Add an MCP tool\n\n## 1. Design first (write this down in your reply)\n- **Name:** verb-first `snake_case`, not clashing with existing tools (check `src/tools/`).\n- **Description:** 1–3 sentences for the model — what it does, when to use it instead of the other tools, what it returns.\n- **Inputs:** each field with type, constraints and description. Keep it to what the model can reasonably know.\n- **Output:** the few fields the model needs, formatted as short text or compact JSON.\n- **Failure cases:** list them and the message each returns.\n- **Side effects:** read-only, or does it change something? Destructive tools need a confirmation input or a human decision first.\n\nIf an existing tool almost does this, propose extending it instead of adding a near-duplicate.\n\n## 2. Implement\nCreate `src/tools/<name>.ts` with a `register(server)` function, following the patterns already in the repo and the SDK version installed.\nThe shape (adjust to the installed SDK's API):\n\n```ts\nimport { z } from \"zod\";\nimport type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\n\nexport function register(server: McpServer) {\n  server.registerTool(\n    \"get_weather\",\n    {\n      title: \"Get weather\",\n      description: \"Current weather for a city. Use for present conditions only; there is no forecast. Returns temperature (°C), conditions and wind.\",\n      inputSchema: { city: z.string().min(1).max(100).describe(\"City name, e.g. 'Leeds' or 'Leeds, UK'\") },\n      annotations: { readOnlyHint: true },\n    },\n    async ({ city }) => {\n      const result = await lookupWeather(city);\n      if (!result) {\n        return { isError: true, content: [{ type: \"text\", text: `No city called \"${city}\" found. Try adding the country.` }] };\n      }\n      return { content: [{ type: \"text\", text: `${result.name}: ${result.tempC}°C, ${result.summary}, wind ${result.windKph} km/h` }] };\n    },\n  );\n}\n```\n\nRegister it in `src/index.ts`. Put API calls in `src/lib/` with timeouts.\n\n## 3. Test\n1. `npm run build` (and `npm test` if there are tests; add one for the handler logic).\n2. `npx @modelcontextprotocol/inspector node dist/index.js` → Tools → list → call with valid input, then invalid input.\n3. Confirm nothing is written to stdout except protocol messages.\n\n## 4. Report\nThe tool signature, its description, sample output, and how to reload it in the human's client\n(most clients need the MCP server restarted to see new tools).\n"
    }
  ],
  "requires": {
    "tools": [
      "node"
    ],
    "env": [],
    "mcp": []
  },
  "tested": {
    "status": "untested",
    "last_run": null,
    "fixture": null,
    "per_agent": {
      "claude-code": "untested",
      "codex": "untested",
      "cursor": "untested",
      "gemini-cli": "untested",
      "copilot": "untested"
    }
  },
  "security": {
    "scanned": true,
    "scanner": "intake-precheck/1",
    "verdict": "clean",
    "network": false,
    "scripts": [],
    "notes": "Instructions only."
  },
  "featured": true,
  "deprecated": false,
  "last_checked": "2026-10-15",
  "published": "2026-10-15",
  "updated": "2026-10-15",
  "compatibility": {
    "claude-code": "native",
    "codex": "native",
    "cursor": "native",
    "gemini-cli": "native",
    "copilot": "native"
  },
  "counts": {
    "rules": 2,
    "skills": 1,
    "mcp": 0
  },
  "url": "https://loadout.jnw-it.com/loadouts/mcp-server-typescript/",
  "api_url": "https://loadout.jnw-it.com/api/loadouts/mcp-server-typescript.json",
  "verification": {
    "level": "verified-install",
    "status": "verified",
    "label": "Verified install",
    "verified": true,
    "checked_at": "2026-10-16T22:54:03Z",
    "commit": "ad36e852567aa3c87821d2a9e2fe4052fd983374",
    "content_current": true,
    "agent_tests": "not-run",
    "counts": {
      "pass": 9,
      "fail": 0,
      "skip": 0,
      "not_applicable": 1
    },
    "meaning": "Level 1: automated checks on the files (parse, install paths, MCP config, safety scan, references, size, licence, starter build). No AI agent was run.",
    "checks": [
      {
        "id": "listing",
        "name": "Listing",
        "status": "pass",
        "summary": "Schema valid; 5 files declared, none missing or extra",
        "details": [
          "tested.status: untested (agent tests are separate from Level 1)"
        ]
      },
      {
        "id": "parse",
        "name": "Files parse",
        "status": "pass",
        "summary": "All 5 files parse; front matter valid",
        "details": []
      },
      {
        "id": "adapters",
        "name": "Adapters",
        "status": "pass",
        "summary": "CLAUDE.md and GEMINI.md import AGENTS.md and are up to date",
        "details": [
          "CLAUDE.md: matches the generator",
          "GEMINI.md: matches the generator"
        ]
      },
      {
        "id": "install-paths",
        "name": "Install paths",
        "status": "pass",
        "summary": "Correct paths for all 5 agents; layouts re-discovered",
        "details": [
          "Claude Code: 3 files, 1 skill in .claude/skills/",
          "Codex CLI: 2 files, 1 skill in .agents/skills/",
          "Cursor: 3 files, 1 skill in .agents/skills/, 1 rule",
          "Gemini CLI: 3 files, 1 skill in .agents/skills/",
          "GitHub Copilot: 2 files, 1 skill in .agents/skills/"
        ]
      },
      {
        "id": "mcp",
        "name": "MCP config",
        "status": "n/a",
        "summary": "No MCP servers in this loadout",
        "details": []
      },
      {
        "id": "safety",
        "name": "Safety scan",
        "status": "pass",
        "summary": "Clean: no credentials, risky shell or hidden text",
        "details": [
          "Scanner intake-precheck/1: verdict clean; network no (listing says no)"
        ]
      },
      {
        "id": "references",
        "name": "References",
        "status": "pass",
        "summary": "No cross-file references to check",
        "details": []
      },
      {
        "id": "size",
        "name": "Size limits",
        "status": "pass",
        "summary": "5 files, 7.0 KB total; largest AGENTS.md 2.9 KB",
        "details": [
          "Limits: 30 files, 32 KB per file, 128 KB total, 500 lines per skill or rule"
        ]
      },
      {
        "id": "licence",
        "name": "Licence",
        "status": "pass",
        "summary": "MIT, by Agent Loadout; text in the repository LICENSE",
        "details": []
      },
      {
        "id": "starter-build",
        "name": "Starter build",
        "status": "pass",
        "summary": "MCP server (official TypeScript SDK) with the loadout's example tool builds, starts over stdio, lists the tool and handles valid and invalid calls",
        "details": [
          "Applied 6 loadout files at every agent's paths",
          "Example from skills/add-mcp-tool/SKILL.md:22 (ts) → src/tools/get_weather.ts (with a stub prepended)",
          "✓ install: npm ci --no-audit --no-fund",
          "✓ type-check and build (tsc): npm run build",
          "✓ stdio smoke test: node smoke.mjs",
          "Covers: src/index.ts + src/tools/<name>.ts with register(server), as AGENTS.md lays out",
          "Covers: The skill's example tool compiles against the current SDK and zod",
          "Covers: Over stdio: initialize, tools/list shows the tool with readOnlyHint, a valid call works, a not-found call returns isError, bad input is rejected",
          "Covers: stdout carries only JSON-RPC messages (the loadout's \"never console.log\" rule)"
        ]
      }
    ]
  },
  "install": {
    "claude-code": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "CLAUDE.md",
          "target": "CLAUDE.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/CLAUDE.md",
          "role": "adapter"
        },
        {
          "path": "skills/add-mcp-tool/SKILL.md",
          "target": ".claude/skills/add-mcp-tool/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/skills/add-mcp-tool/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-server-typescript/claude-code.zip"
    },
    "codex": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/add-mcp-tool/SKILL.md",
          "target": ".agents/skills/add-mcp-tool/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/skills/add-mcp-tool/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-server-typescript/codex.zip"
    },
    "cursor": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": ".cursor/rules/mcp-tools.mdc",
          "target": ".cursor/rules/mcp-tools.mdc",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/.cursor/rules/mcp-tools.mdc",
          "role": "rules"
        },
        {
          "path": "skills/add-mcp-tool/SKILL.md",
          "target": ".agents/skills/add-mcp-tool/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/skills/add-mcp-tool/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-server-typescript/cursor.zip"
    },
    "gemini-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "GEMINI.md",
          "target": "GEMINI.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/GEMINI.md",
          "role": "adapter"
        },
        {
          "path": "skills/add-mcp-tool/SKILL.md",
          "target": ".agents/skills/add-mcp-tool/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/skills/add-mcp-tool/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-server-typescript/gemini-cli.zip"
    },
    "copilot": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/add-mcp-tool/SKILL.md",
          "target": ".agents/skills/add-mcp-tool/SKILL.md",
          "raw_url": "https://loadout.jnw-it.com/raw/mcp-server-typescript/1.0.0/skills/add-mcp-tool/SKILL.md",
          "role": "skill"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://loadout.jnw-it.com/download/mcp-server-typescript/copilot.zip"
    }
  },
  "source_zip_url": "https://loadout.jnw-it.com/download/mcp-server-typescript/source.zip"
}