Your MCP server

The running jobjob_7Hq2 · delivered · document.fr-CA.pdf · $49.00 charged

Every brand serves an MCP server written from its operation rows. Its tools, hints, output schemas, prompts and instructions come from the same file as its API, so the two never disagree.

Every brand serves MCP at /mcp, and over stdio from its folder. Nobody writes a tool. Each one is a row of the operation registry, rendered for an agent: the same row gives the API its route, the CLI its command and the SDK its call. check fails surface_drift when a tool has no row, so the tools and the routes can't drift apart.

Over it, an agent prices, orders, waits, accepts, sends back and cancels. It never signs or consents for anyone, because a row only a person may call renders no tool (What an agent can never do).

Output
# a client that launches its MCP servers runs the brand over stdio, in its folder:
$ npx services.dev mcp

→ {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18"}}
← {"id":1,"serverInfo":{"name":"lingua","version":"2026-10-25"},"protocolVersion":"2025-06-18"}
→ {"jsonrpc":"2.0","id":2,"method":"tools/list"}
← order_translation · get_job · list_jobs · accept_job · send_back_job · cancel_job · end_job · reorder_job · set_job_policy · send_job_signal · send_job_message · list_job_messages · reissue_ask

# or, while dev runs, it relays to the running brand:
$ npx services.dev mcp --url http://localhost:8787

Over HTTP it's JSON-RPC 2.0 at POST /mcp, one message or a batch. A notification gets 202.

RequestWhat it answers
initializethe brand's name and version, its tools, resources and prompts capabilities, and the instructions below. It speaks 2025-06-18, 2025-03-26 and 2024-11-05, and answers any other version with the newest
tools/listthe buyer toolset; a test key at ?toolset=sandbox adds the sandbox's
tools/callthe row's answer, as content (the words, then the JSON) and structuredContent. isError is true for a refusal, and false for an OFFER
resources/templates/listjob://{id}{?wait}
resources/listthe brand's jobs, each as job://<id>
resources/readone job, now or after ?wait=<seconds>
prompts/list, prompts/getone buy_<service> prompt per Service
ping{}

initialize answers with instructions written from your Services: how to order, when each one charges, which ones wait on a party, and what test mode means. Change a price or add an ask, and the instructions change with it. check holds them to 120 words (instructions_too_long).

Output
→ initialize {"protocolVersion":"2025-06-18"}
← {"protocolVersion":"2025-06-18","serverInfo":{"name":"lingua","version":"2026-10-25"},"capabilities":{"tools":{},"resources":{},"prompts":{}}}

  instructions, written from the brand:
  order_job takes service, the input, max_usd and a key.
  Without max_usd you get an OFFER: send its next call unchanged to order.
  The same key returns the same job.
  Services charge on delivery, except filed-translation (on file's receipt).
  Canceling before then costs $0.
  get_job with wait: 50 waits through any person's line; then accept_job, or send_back_job with a note.
  filed-translation waits on a party: next hand_to_person is a link only that person can use; no tool acts for them.
  This is test mode: a sk_test_ key, simulated AI and money; metadata.sandbox picks branches.

  93 of 120 words
Output
| Tool | Toolset | readOnlyHint | destructiveHint | idempotentHint | Operation |
|---|---|---|---|---|---|
| `order_engagement` | buyer |  | yes | yes | `service.order` |
| `order_translation` | buyer |  | yes | yes | `service.order` |
| `get_job` | buyer | yes |  | yes | `job.get` |
| `list_jobs` | buyer | yes |  | yes | `job.list` |
| `accept_job` | buyer |  | yes | yes | `job.accept` |
| `send_back_job` | buyer |  | yes |  | `job.send_back` |
| `cancel_job` | buyer |  | yes | yes | `job.cancel` |
| `end_job` | buyer |  | yes | yes | `job.end` |
| `reorder_job` | buyer |  | yes |  | `job.again` |
| `set_job_policy` | buyer |  |  | yes | `job.policy` |
| `send_job_signal` | buyer |  |  |  | `job.signal` |
| `send_job_message` | buyer |  |  |  | `job.message` |
| `list_job_messages` | buyer | yes |  | yes | `job.thread` |
| `list_clocks` | sandbox | yes |  | yes | `clock.list` |
| `get_clock` | sandbox | yes |  | yes | `clock.get` |
| `list_subscriptions` | sandbox | yes |  | yes | `subscription.list` |
| `get_subscription` | sandbox | yes |  | yes | `subscription.get` |
| `cancel_subscription` | sandbox |  | yes | yes | `subscription.cancel` |

A tool's hints come from its row's effect. A read is read-only, an act is destructive, and a record, such as set_job_policy or reissue_ask, is neither. A tool is idempotent when the same call twice changes nothing more. An order tool is too, because the offer's next call carries a key, and the same key returns the same job.

Tools come in two toolsets. The buyer's is what every key lists. A test key that connects with ?toolset=sandbox adds test clocks, the event feed and synthetic events. check fails toolset_too_large when the buyer's toolset passes 8,000 tokens, so your brand stays cheap to load into an agent's context.

Each Service gets order_<service>, its input taken straight from the Service's. Its output schema names the three things it can answer: a job, an OFFER, or a refusal with the exact codes this row returns.

Output
→ tools/list
← order_translation
  description   Translation: send a document and a language; get a document back. $49 per job, within 48 hours, charged on delivery. AI delivers every job.
  annotations   {"readOnlyHint":false,"idempotentHint":true,"destructiveHint":true}
  inputSchema
    document    file      required
    to          language  required  a BCP 47 tag, such as fr-CA
    max_usd     number    optional  The most you will pay, in dollars. Missing or below the price, you get an offer instead of a job.
    key         string    optional  Any string you choose. Retrying with the same key returns the same job.
    metadata    object    optional  Your own keys, echoed on the job and its events.
    test_clock  string    optional  A test clock the job runs on: tc_….
  outputSchema  oneOf
    job         status: working | waiting | delivered | accepted | canceled | ended
    OFFER       type, object, price, next
    refusal     code: input_invalid | policy_invalid | key_reused | not_found | not_live | key_missing | key_invalid | body_invalid | version_unknown | api_error

max_usd and key aren't required. Without max_usd, or below the price, the answer is an OFFER, returned as a result and not an error. isError is false, the first text block is what a model reads, and structuredContent is the same answer to the schema. Its next is the exact call that orders, with max_usd and a key filled in, so sending it twice never orders twice.

Output
→ tools/call order_translation {"document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"to":"fr-CA"}
← isError: false
# content[0]: the text an agent reads first
← OFFER · $49 per job, within 48 hours, charged on delivery.
←   terms Charged on delivery. Cancel at $0 until it ships. Send it back with a note and the AI redoes it, or you pay $0. Final 7 days after delivery.
←   next  order_translation {"document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"to":"fr-CA","max_usd":49,"key":"order-49736585"}
# structuredContent: the same answer, to the outputSchema
← {
  "type": "OFFER",
  "object": "offer",
  "service": "translation",
  "title": "Translation",
  "price": {
    "usd": 49,
    "sentence": "$49 per job, within 48 hours, charged on delivery."
  },
  "eta": "48h",
  "terms": "Charged on delivery. Cancel at $0 until it ships. Send it back with a note and the AI redoes it, or you pay $0. Final 7 days after delivery.",
  "next": {
    "do": "order",

Past 12 Services, or three quarters of the token budget, the brand serves one order_job tool instead, the Service named in service:

Terminal
$ ls services   # 13 Services
arabic.ts chinese.ts dutch.ts french.ts german.ts hindi.ts italian.ts japanese.ts korean.ts polish.ts portuguese.ts spanish.ts turkish.ts

→ tools/list
← order_job · get_job · list_jobs · accept_job · send_back_job · cancel_job · end_job · reorder_job · set_job_policy · send_job_signal · send_job_message · list_job_messages · reissue_ask
  order_job.description               Order any of lingua's 13 Services: name it in service, with its own fields. Without max_usd, the offer: the price, the terms and the exact next call.
  order_job.inputSchema.service.enum  arabic · chinese · dutch · french · german · hindi · italian · japanese · korean · polish · portuguese · spanish · turkish

→ tools/call order_job {"service":"german","document":{"url":"https://example.com/lease.pdf","name":"lease.pdf"},"max_usd":49,"key":"contract-de"}
← job_7Hq2 · working · $49.00 held · german

A refusal is isError: true with the one error shape every surface returns, so an agent reads the same code, reason and next it would over HTTP (Answers). Arguments are checked against the tool's input schema before anything runs. A misfit is input_invalid: its next is the same call, each missing field set to null and named in fill. A call the server can't route, such as an unknown tool or a sandbox tool without the sandbox toolset, is a JSON-RPC -32602 that says what to do.

Output
→ tools/call order_translation {"to":"fr-CA"}
← isError: true
# content[0]: the text an agent reads first
← input_invalid: order_translation needs document.
←   next  order_translation {"to":"fr-CA","document":null}
←   docs  http://localhost:8787/docs/errors/input_invalid
# structuredContent: the one error shape every surface returns
← {
  "type": "BLOCKED",
  "category": "invalid_request",
  "code": "input_invalid",
  "reason": "order_translation needs document.",
  "param": "document",
  "next": {
    "do": "service.order",
    "tool": "order_translation",
    "arguments": {
      "to": "fr-CA",
      "document": null
    },

job://{id}{?wait} reads one job, and waits on it like get_job with wait. subject://{id} reads a subject, with its clocks and who is of record.

Each Service gets one prompt that walks an agent through a careful purchase: read the offer, ask the person, order, wait, review, then accept or send back. When a Service waits on a party, its fourth step also tells the agent to pass the link on.

Output
→ prompts/get buy_translation {"document":"lease.pdf","to":"fr-CA"}
← Buy Translation from lingua for me. Input: document: lease.pdf; to: fr-CA.
← 1. Call order_job, naming service "translation", with the input and no max_usd. It answers an OFFER: the price, the terms and the exact next call.
← 2. Show me the price and terms ($49 per job, within 48 hours, charged on delivery) and ask me to agree. Do nothing more until I say yes.
← 3. Send the offer's next call unchanged: it carries max_usd and a key, so sending it twice never orders twice.
← 4. Call get_job with wait: 50 until the job is delivered.
← 5. Show me the output, then call accept_job if I accept it, or send_back_job with my note and the AI redoes it.

The legal example's five Services show it. Every one of them waits on a party, and the instructions say so. Four have a submit step, so each charges on that step's receipt, and the instructions name the receipt:

Output
→ tools/list
← order_matter · get_matter · list_matters · accept_matter · send_back_matter · cancel_matter · end_matter · reorder_matter · set_matter_policy · send_matter_signal · send_matter_message · list_matter_messages · reissue_ask
  13 tools, about 4,569 tokens

→ initialize
  order_matter takes service, the input, max_usd and a key.
  Without max_usd you get an OFFER: send its next call unchanged to order.
  The same key returns the same matter.
  Services charge on file's receipt, except contract-review (on acceptance), demand-letter (on send's receipt), docket-watch (monthly) and formation (on form_entity's receipt).
  Canceling before then costs $0.
  get_matter with wait: 50 waits through any person's line; then accept_matter, or send_back_matter with a note.
  contract-review, demand-letter, formation, provisional-patent and trademark-response wait on a party: next hand_to_person is a link only that person can use; no tool acts for them.
  This is test mode: a sk_test_ key, simulated AI and money; metadata.sandbox picks branches.

The legal example runs all five. The sales example runs a deal.

A streaming session adds progress notifications, resource subscriptions, and a party's ask elicited as a URL. Over plain HTTP the ask arrives as next.hand_to_person. What no tool will ever do is on the next page, What an agent can never do.