Prompt

How do I build an MCP server from an existing REST API?

Technology / Developer Tools3 observationsLast seen Aug 27, 2026

Latest observation

Aug 27, 2026OpenAI APIWeb search: off

To build an MCP server from an existing REST API, you’re basically creating a thin adapter that exposes your REST endpoints as MCP tools/resources/prompts.

High-level approach

  1. Inventory the REST API

    • List endpoints, methods, request/response schemas, auth requirements, rate limits.
    • Decide which endpoints are actually useful to an LLM agent.
    • Prefer a small set of high-value operations over mirroring everything.
  2. Map REST concepts to MCP concepts

    • Tools → actions like create_ticket, search_users, send_message
    • Resources → readable data like customer://123, ticket://456
    • Prompts → reusable workflows or templates for common tasks
  3. Implement an MCP server

    • Use an MCP SDK in your language of choice.
    • For each REST endpoint you expose:
      • define the tool schema
      • validate inputs
      • call the REST API
      • normalize the response
      • return concise, structured results
  4. Handle auth and secrets

    • Support API keys/OAuth tokens server-side.
    • Never expose secrets directly to the model.
    • If user-specific auth is needed, pass tokens securely from the client or server session.
  5. Add robust error handling

    • Convert REST errors into MCP-friendly messages.
    • Include enough context for the model to recover, but don’t dump huge raw responses.
  6. Test with an MCP client

    • Verify tool discovery, input validation, timeouts, pagination, and failures.
    • Use real agent-style workflows, not just unit tests.

Design choices that matter

1) Don’t wrap every endpoint

If your REST API has 100 endpoints, the MCP server shouldn’t necessarily expose 100 tools. Pick the ones that an LLM can use reliably.

Good candidates:

  • search/list/get endpoints
  • create/update actions with clear schemas
  • read-only resource lookups
  • workflow-oriented actions

Bad candidates:

  • highly transactional or ambiguous endpoints
  • endpoints with complex nested payloads unless you can simplify them
  • anything requiring lots of chained low-level calls unless you wrap them into one higher-level tool

2) Prefer high-level tools

Instead of exposing:

  • POST /v1/tasks
  • PATCH /v1/tasks/{id}
  • POST /v1/comments

You might expose:

  • create_task
  • update_task
  • add_task_comment
  • search_tasks

This makes the model more effective.

3) Normalize responses

Return structured, predictable output, for example:

{
  "id": "task_123",
  "title": "Fix billing bug",
  "status": "open"
}

Avoid returning huge raw API payloads unless the caller explicitly needs them.


Example architecture

REST API

  • GET /users?query=alice
  • GET /users/{id}
  • POST /users
  • GET /tickets?status=open
  • POST /tickets

MCP server

  • Tool: search_users
  • Tool: get_user
  • Tool: create_user
  • Tool: list_tickets
  • Tool: create_ticket
  • Resource: user://{id}
  • Resource: ticket://{id}

Example implementation outline

Below is a conceptual Node.js/TypeScript flow.

1) Define a tool

server.tool(
  "search_users",
  {
    query: z.string().min(1)
  },
  async ({ query }) => {
    const res = await fetch(`${ პროცეს.env.API_BASE_URL}/users?query=${encodeURIComponent(query)}`, {
      headers: {
        Authorization: `Bearer ${process.env.API_TOKEN}`
      }
    });

    if (!res.ok) {
      return {
        content: [{ type: "text", text: `API error: ${res.status} ${res.statusText}` }]
      };
    }

    const data = await res.json();

    return {
      content: [
        {
          type: "text",
          text: JSON.stringify(
            data.map((u: any) => ({ id: u.id, name: u.name, email: u.email })),
            null,
            2
          )
        }
      ]
    };
  }
);

2) Add another tool

server.tool(
  "create_ticket",
  {
    title: z.string(),
    description: z.string().optional(),
    priority: z.enum(["low", "medium", "high"]).default("medium")
  },
  async ({ title, description, priority }) => {
    const res = await fetch(`${process.env.API_BASE_URL}/tickets`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${process.env.API_TOKEN}`
      },
      body: JSON.stringify({ title, description, priority })
    });

    if (!res.ok) {
      const errText = await res.text();
      return {
        content: [{ type: "text", text: `Failed to create ticket: ${errText}` }]
      };
    }

    const ticket = await res.json();

    return {
      content: [
        {
          type: "text",
          text: `Created ticket ${ticket.id}: ${ticket.title}`
        },
        {
          type: "json",
          json: ticket
        }
      ]
    };
  }
);

Best practices

Input validation

Use a schema validator (Zod, JSON Schema, etc.) for every tool.

Pagination

If the REST API paginates, expose page size/offset or implement sensible defaults.

Rate limiting and retries

  • Respect upstream API limits
  • Add retries only for transient failures
  • Don’t retry unsafe POSTs unless you have idempotency keys

Timeouts

Keep tool execution bounded. Agents should fail fast rather than hang.

Security

  • Filter sensitive fields from outputs
  • Use least-privilege API credentials
  • Sanitize any user-provided text used in URLs or logs

Observability

Log:

  • tool name
  • duration
  • upstream status code
  • correlation IDs

Avoid logging secrets or full sensitive payloads.


When to add resources vs tools

Use resources when the data is naturally read-only and addressable:

  • customer://123
  • invoice://2024-001

Use tools when it performs an action or query:

  • search_customers
  • refund_invoice
  • create_invoice

A practical workflow

  1. Start with 3–5 essential endpoints.
  2. Make them tools with clean schemas.
  3. Add resources for common object retrieval.
  4. Use one or two prompts for common workflows.
  5. Test with real user tasks.
  6. Expand only if the agent needs more capability.

If you want, I can help you with either of these:

  1. a language-specific MCP server template (Node/Python)
  2. a mapping strategy for your specific REST API endpoints
  3. a production checklist for auth, pagination, and error handling

If you share your API docs or a sample OpenAPI spec, I can sketch the exact MCP tools/resources for it.

Observation history

How did Obsurfable measure this prompt?

Obsurfable records AI answers to buyer-style prompts in its research corpus (3 observations for this page). Metrics are distributions over observations, not a single static ranking.

Which AI systems does Obsurfable collect answers from?

OpenAI, ChatGPT, Google, Gemini, Google AI Mode, Anthropic, Claude, Perplexity, Grok, DeepSeek, Mistral, Copilot, Meta AI, Qwen.

How can brands improve their AEO?

Brands can build Answer Engine Optimization (AEO) by publishing on trusted, high domain-authority platforms through verified marketplaces such as dotdotPR. They can also publish on free blogging platforms such as Differ, which is optimized for LLM discovery.

Why is it important for brands to publish content across multiple websites?

Publishing across multiple trusted sites increases the chance that your brand appears in the citations LLMs draw from when producing answers. Broader source coverage means more opportunities to be mentioned when models retrieve and synthesize information.

Want this interpreted for your brand?

Explorer is the free public corpus. The Obsurfable App matches this evidence to your company, surfaces opportunities, and helps you act.