The examples all wrap one upstream: a zero-dependency Node server with an OpenAPI 3.1 description, in sample-api/ of the examples repository. It is small on purpose. Five operations are enough to exercise every decision a real integration has to make.

Files in the repository

Every snippet on this page comes from these files; open them to see the whole thing.

Run it

git clone https://github.com/apitoagents/mcp-examples
cd mcp-examples
node sample-api/server.mjs

It listens on 127.0.0.1:4010, serves its own description at /openapi.json, answers /health, and requires the header X-API-Key: demo-key on everything else. Data lives in memory and resets on restart.

The operations

  • GET /products?q&category&limit&cursor: full-text search over ten products in three categories, paginated with an opaque cursor. limit must be 1 to 20.
  • GET /products/{productId}: one product.
  • POST /orders with an Idempotency-Key header: creates an order for one product. The same key with the same payload returns the original order with 200 instead of 201; the same key with a different payload is a 409.
  • GET /orders/{orderId}: one order.
  • POST /orders/{orderId}/cancel: cancels an order that has not shipped. Irreversible; 409 if it already is.

Errors are JSON with a code (invalid_input, unauthorized, not_found, idempotency_conflict, order_not_cancellable) and a message.

Why five operations become four tools

  • searchProducts and getProduct become the read tools search_products and get_product.
  • createOrder becomes create_order. The Idempotency-Key header is the interesting part: a generator will expose it as a raw input called Idempotency-Key, while the examples name it idempotencyKey and explain in the description that the assistant should generate one per user request and reuse it on retry.
  • cancelOrder becomes cancel_order, a guarded tool that refuses to act until the assistant has shown the user the order and received an explicit confirmation.
  • getOrder is not exposed at all. It is called internally by cancel_order to show the user what will be cancelled. Exposing it would add nothing for the user and one more thing for the assistant to choose between.

That last point is the general rule: the tool list is designed from what a customer wants to get done, then mapped onto the API, not the other way round. The generator comparison shows what happens when it is done the other way round.

Try it with curl

curl -H "X-API-Key: demo-key" "http://127.0.0.1:4010/products?q=lamp&limit=2"

curl -H "X-API-Key: demo-key" -H "Idempotency-Key: demo-0001" \
  -H "Content-Type: application/json" \
  -d '{"productId":"p-1002","quantity":2}' http://127.0.0.1:4010/orders

# Repeat the exact request: same order back, status 200, no duplicate.

Next: build the server that wraps it in TypeScript, Python, C# or PHP.