Part of MCP Builders — The Producer Playbook. Chapter 1 of 6.

You have a working API. Maybe it is public REST, maybe internal microservices behind a gateway. Customers or your own team keep asking: “Can Claude / Cursor use this?”

The wrong answer is dumping your OpenAPI spec into a prompt. The right answer is an MCP server — a thin layer that exposes a small set of tools your existing API already knows how to serve.

This chapter gets you from zero to a testable server in one afternoon. Not production-hardened (Chapters 2–6 cover auth, hosting, and shipping) — but real enough to connect in Cursor and learn what agents actually need.

What you are building (one sentence)

An MCP server is a translator: agent-friendly tool calls on the outside, your existing HTTP handlers on the inside.

You are not rewriting your API. You are choosing which operations deserve agent access and wrapping them with clear names, descriptions, and input schemas.

Step 1 — Pick five capabilities, not fifty endpoints

Open your API docs or OpenAPI file. Highlight workflows agents would actually run:

  • Look up a customer before drafting a reply
  • Search open tickets by keyword
  • Create a draft (not publish) document
  • List projects in one workspace
  • Get shipment status for an order ID

Ignore for v1:

  • Admin / delete / bulk export
  • Generic POST /graphql pass-through
  • Anything that requires chaining five calls to be useful

Rule: if a human would need a UI to complete the action safely, do not expose it as a v1 tool.

REST endpointMCP tool nameWhy
GET /v1/customers/:idget_customerSingle-record read — safe first tool
GET /v1/tickets?q=search_ticketsAgent-friendly search, not raw query DSL
POST /v1/draftscreate_draftWrite — ship after reads work
GET /v1/projectslist_projectsBounded list with pagination inside tool

Write this mapping in a TOOLS.md before you write code. You will thank yourself at security review — and when you submit to Influzer.ai.

Step 2 — Name and describe tools for models, not humans

Agents pick tools from tools/list using name + description + schema. Vague tools get mis-invoked.

Bad: handle_customer — “Works with customers.”

Good: get_customer — “Fetch one customer record by Acme customer ID. Read-only. Returns name, plan, and status — not billing history.”

Checklist per tool:

  • Verb-first snake_casesearch_, get_, create_, list_
  • Description says read vs write and data boundary
  • Each parameter has a .describe() with an example value
  • No optional “mode” flags that smuggle admin behavior

Chapter 2 covers what happens when OAuth scopes are too wide. Chapter 1’s job is a narrow tool list — authorization is easier when there is less to authorize.

Step 3 — Project shape (TypeScript + stdio)

Afternoon-friendly stack:

  • Runtime: Node 20+
  • SDK: @modelcontextprotocol/sdk
  • Validation: zod for tool input schemas
  • Transport: stdio for local dev in Cursor (Chapter 4 covers HTTP hosting)
acme-mcp/
  package.json
  src/
    index.ts      # server entry + tool registration
    acme-api.ts   # thin HTTP client to your REST API
    sanitize.ts   # strip fields before returning to model
  TOOLS.md        # endpoint → tool mapping
  README.md       # install + env vars

Keep API client code separate from MCP registration. Your REST client will be reused when you add HTTP transport later.

Step 4 — Minimal server (copy-paste starter)

Install:

npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node

src/index.ts — one read tool wired to your API:

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: "acme-api",
  version: "1.0.0",
});

server.tool(
  "get_customer",
  "Fetch one customer by ID from Acme API (read-only). Returns plan and status.",
  { customer_id: z.string().describe("Acme customer ID, e.g. cus_abc123") },
  async ({ customer_id }) => {
    const res = await fetch(
      `https://api.acme.com/v1/customers/${encodeURIComponent(customer_id)}`,
      { headers: { Authorization: `Bearer ${process.env.ACME_API_KEY}` } }
    );
    if (!res.ok) {
      return { content: [{ type: "text", text: `Acme API error: ${res.status}` }], isError: true };
    }
    const data = await res.json();
    // Never return raw API blobs — trim to what agents need
    const safe = {
      id: data.id,
      name: data.name,
      plan: data.plan,
      status: data.status,
    };
    return { content: [{ type: "text", text: JSON.stringify(safe, null, 2) }] };
  }
);

const transport = new StdioServerTransport();
await server.connect(transport);

Add "type": "module" in package.json and run with npx tsx src/index.ts for local testing.

Patterns that matter:

  • Encode path params — never concatenate user input into URLs unsafely
  • Return isError: true on API failures — agents retry smarter with explicit errors (Chapter 5 goes deeper)
  • Sanitize output — strip emails, tokens, internal IDs you do not want in context
  • Env vars for secrets — not argv, not committed config

Step 5 — Add tools one at a time

After get_customer works, add search_tickets:

  • Hide pagination inside the tool — accept query + optional limit (default 10, max 25)
  • Return summary rows, not megabyte JSON
  • Log upstream latency — slow tools frustrate agents

Resist adding write tools until reads are stable in Cursor for a full day. When you add create_draft, name it so the model knows it mutates state.

Step 6 — Connect in Cursor (15 minutes)

Add to .cursor/mcp.json (project) or global MCP settings:

{
  "mcpServers": {
    "acme-api": {
      "command": "npx",
      "args": ["-y", "tsx", "/absolute/path/to/acme-mcp/src/index.ts"],
      "env": {
        "ACME_API_KEY": "your-staging-key"
      }
    }
  }
}

Restart MCP in Cursor. Verify tools appear. Prompt explicitly:

Use the get_customer tool to fetch customer cus_abc123 and summarize their plan.

If the tool does not fire, name it in the prompt. Generic questions skip tool use.

Full consumer-side setup patterns: first MCP servers in Cursor.

Step 7 — Afternoon checklist before you call it v0.1

  • ☐ 3–5 read tools working against staging API
  • TOOLS.md maps each tool → REST endpoint + scope
  • ☐ Responses sanitized — no credential leakage in tool output
  • ☐ README lists env vars and staging-only warning
  • ☐ Tested in Cursor with explicit tool prompts
  • ☐ Write tools deferred or clearly named

That is v0.1. Not listed publicly yet. Not OAuth-hardened. But enough to demo internally and decide if the tool boundaries feel right.

Mistakes that waste the afternoon

Exposing call_api(method, path, body)

Convenient for you; catastrophic for agents. You have rebuilt arbitrary API access with extra steps.

Returning full upstream JSON

Burns context, leaks fields you forgot existed. Trim aggressively.

Twelve tools on day one

Debug one tool at a time. Overlap confuses models and you.

Skipping TOOLS.md

You will forget which endpoint maps where. Security review will stall.

Production keys in shared config

Staging keys only until Chapter 2 auth is done.

How this connects to the rest of MCP Builders

ChapterWhen
1 — you are hereFirst tools + local test
2 — OAuthBefore any user connects real accounts
3 — Enterprise authBefore selling to teams with IdP
4 — HostingWhen stdio is not enough
5 — ErrorsBefore production traffic
6 — Ship + directoryWhen you are ready to be discovered

Strategic context (why bother)

Wrapping your API as MCP is how you enter the agent toolchain without forcing every client to learn your SDK. Strategic case: MCP vs REST for developers and MCP as the new SDK layer.

When v0.1 feels right, browse how mature servers document tools in the Top 100 — match that clarity when you submit.

Quick answers

GraphQL instead of REST?

Same logic — one tool per capability, not “run arbitrary query.” Wrap specific operations your agents need.

Python instead of TypeScript?

Use the official Python MCP SDK with the same tool boundaries. Afternoon timeline still holds.

Do I need OAuth for an internal demo?

Staging API key in env is fine for solo dev. Read Chapter 2 before teammates connect.

When should I submit to Influzer.ai?

After OAuth or auth story is honest, tools are documented, and you have a public README — Chapter 6 walks through it. Early submit is OK if you mark it beta.

Final thought

Your API already does the hard work — business logic, validation, persistence. The MCP layer is product design: which capabilities agents get, what they are called, and what comes back in context.

Pick five tools. Ship one. Test in Cursor before dinner. Then read Chapter 2 before you let anyone else connect.

Next: OAuth, tokens, and the over-permission trap · Full chapter list