# MCP server

Endpoint: `POST https://themassprint.com/mcp` — stateless streamable-HTTP JSON-RPC 2.0. No API key. One exception, and it is opt-in: `cancel_order` honours `Authorization: Bearer mp_live_...` if you send it, which is what lets a platform stop a press on an order its key created (see below).

Add to Claude Code:

```bash
claude mcp add --transport http massprint https://themassprint.com/mcp
```

## Tools

### get_catalog
No arguments. Returns SKUs, formats, prices (INR paise + display), page limits, trim sizes, shipping coverage, and the workflow summary.

### create_upload
No arguments. Returns:
```json
{ "upload_id": "upl_...", "put_url": "https://themassprint.com/v1/uploads/upl_.../content?exp=...&sig=...", "expires_at": "...", "max_bytes": 104857600 }
```
PUT the raw PDF bytes to `put_url`. The PUT response carries the validation report; a 422 means the PDF has blocking issues (each with a `resolution`).

Keyless limits, per calling origin: **20 create_upload calls per hour** and **at most 5 unused uploads open at once**. Exceeding either returns HTTP 429 (`rate_limited`) with a `Retry-After` header. Keyless `put_url`s expire after **2 hours** — PUT the bytes promptly, and reuse an `upload_id` you already hold instead of creating extras. Uploads already attached to an order are never affected. Authenticated REST callers (`Authorization: Bearer mp_live_...`) have neither limit and keep the 24-hour upload window.

### validate_pdf
`{ "upload_id": "upl_..." }` → the stored validation report:
```json
{ "printable": true, "page_count": 32, "trim": { "width_mm": 203, "height_mm": 203, "name": "square-8in" }, "errors": [], "warnings": [{ "code": "no_bleed", "message": "...", "resolution": "..." }] }
```

### create_order
```json
{
  "upload_id": "upl_...",
  "format": "hardcover",
  "copies": 1,
  "email": "buyer@example.com",
  "address": { "name": "...", "line1": "...", "city": "Chennai", "state": "TN", "pincode": "600020", "phone": "+91 98765 43210" }
}
```
Returns `{ order, order_token, ... }` where `order.payment_url` is the Razorpay link.
**Save `order_token` immediately — it is shown only once.**

`format` is `paperback` or `hardcover` for storybooks, `sticker-a3` for a single-sheet
A3 portrait sticker / wall chart (the PDF must be exactly 1 page at 842×1191 pt), or
`greeting-card-a5` for a folded A5 card (exactly 2 pages at 859×612 pt, bleed included).

### get_order
`{ "order_id": "ord_...", "order_token": "opt_..." }` → current status, tracking once shipped, and `payment_url` again while unpaid. A platform may send `Authorization: Bearer mp_live_...` instead of the token for orders that key created.

### cancel_order
`{ "order_id": "ord_...", "order_token": "opt_..." }`. Any authorized caller may cancel while `awaiting_payment`. The API key that **created** the order may also cancel it while `confirmed` or `printing` — stopping production before the press finishes — so send `Authorization: Bearer mp_live_...` on this call if you are the platform that placed the order (`order_token` is then optional). `shipped` and `delivered` can never be canceled; that is a refund conversation, not a cancel. Canceling moves no money, and it **releases the `client_reference`** — replaying that reference afterwards creates a new order and prints it. If the order changes status before the cancel lands, you get `not_cancellable` naming the status it moved to; a cancel never reports success it did not achieve.

### apply_enterprise
`{ "company_name": "...", "contact_email": "...", "gstin": "29ABCDE1234F1Z5", "expected_monthly_volume": 200 }` → applies for an invoiced enterprise account (no payment links, platform pricing, monthly settlement). GSTIN required. Reviewed before issue; the key is emailed to contact_email once approved — no key is returned in the call.

## Error handling

Tool failures return `isError: true` with a JSON body: `{ "error": "<code>", "message": "...", "resolution": "how to fix it" }`. Follow the resolution; the same call retried after fixing the input succeeds. See [errors](https://themassprint.com/docs/errors).
