printful-mcp
# printful-mcp
A **read-only** [Model Context Protocol](https://modelcontextprotocol.io) server for the [Printful](https://www.printful.com) print-on-demand API.
Lets an AI assistant answer questions about your Printful store — order status, what a print file actually contains, which variant maps to which artwork — without being able to spend your money.
## Why read-only
An agent that can approve a print-on-demand order can start production that cannot be undone and bill you for it. There is no tool here that creates, approves, holds or cancels an order, and no generic write path. Those actions belong in Printful's dashboard, where a human sees them first.
If you want writes, fork it and add them deliberately. Please don't ask this package to grow a `POST`.
## Install
```bash
npx printful-mcp
```
Get a token from the [Printful developer portal](https://developers.printful.com) (Settings → API), then add the server to your MCP client.
**Claude Code** — `.mcp.json` in your project, or `~/.claude.json` for every project:
```json
{
"mcpServers": {
"printful": {
"command": "npx",
"args": ["-y", "printful-mcp"],
"env": { "PRINTFUL_TOKEN": "your-token-here" }
}
}
}
```
**Claude Desktop** — the same block in `claude_desktop_config.json`.
The token is read **only** from the environment. It is never accepted as a command-line argument, because process arguments are visible to other users on the machine.
## Tools
| Tool | What it does |
|---|---|
| `printful_whoami` | Verify the token and list readable stores. Start here. |
| `printful_list_orders` | List orders, filterable by status. |
| `printful_get_order` | One order in full — line items, print files, thread options. By Printful id or the connected store's external id. |
| `printful_list_store_products` | Sync products in the connected store. |
| `printful_get_store_product` | One sync product, its variants and the print files attached to each. |
| `printful_get_catalog_product` | A Printful catalog blank, its variants and print placements. |
| `printful_get` | Escape hatch for allowlisted read paths without a dedicated tool. |
Every tool is annotated `readOnlyHint: true`.
## Configuration
| Variable | Required | Effect |
|---|---|---|
| `PRINTFUL_TOKEN` | yes | Printful API token. |
| `PRINTFUL_MCP_REDACT_PII` | no | Set to `1` to mask recipient names, street addresses, phone numbers, emails and tax numbers in every response. City, state and country are preserved. |
### About that PII flag
Printful orders carry real customer names and addresses. Anything a tool returns lands in a model's context and usually in a saved transcript. If you don't need shipping details, set `PRINTFUL_MCP_REDACT_PII=1` and keep that data out of the conversation entirely — you still see order status, items and print files.
It is opt-in rather than default because "where is this order going" is a legitimate and common question, and silently masking it would be surprising.
## Security posture
- **GET only.** No code path issues another HTTP method.
- **Path allowlist.** The base URL is a constant; callers pass a path, never a URL. Paths are matched against an allowlist, and `..`, `//`, backslashes and absolute URLs are refused before any request is made.
- **No redirect following.** A redirect off `api.printful.com` is an error, not a hop.
- **Token hygiene.** Read from the environment, never logged, and every outbound string is scrubbed of it as a second line of defence.
- **Bounded requests.** 15-second timeout, 2 MB response cap, capped backoff on HTTP 429.
- **Small surface.** Two runtime dependencies: the MCP SDK and Zod.
Found a security problem? Open an issue — or, if you'd rather not do it in public, say so in a minimal issue and I'll follow up privately.
## Development
```bash
npm install
npm run build
PRINTFUL_TOKEN=... node scripts/smoke.mjs
```
`scripts/smoke.mjs` speaks real MCP over stdio against the live API: it checks the handshake, the tool list and annotations, a successful call, and that the guardrails actually refuse disallowed paths, traversal attempts, absolute URLs and mutually exclusive arguments.
## License
MIT
TDQS
Scored across 7 tools
Each tool targets a distinct resource (auth, store product, catalog product, orders), and printful_get is explicitly described as an escape hatch for paths without a dedicated tool. However, the allowlist still includes paths like /orders and /store/products that also have dedicated tools, creating minor overlap.
All tools share a clear printful_ prefix, and most follow a get_/list_ verb pattern. Printful_whoami and the generic printful_get deviate from the noun+verb convention, but the pattern remains predictable overall.
Seven tools is a well-scoped set for a read-only Printful integration, covering auth, products, and orders without unnecessary bloat. Each tool has a clear role, and the count feels appropriate for the server's purpose.
The server covers the core read workflows: authentication/store id, catalog and store product details, and order listing/retrieval, with the escape hatch filling gaps for tax/countries and other allowed endpoints. It is intentionally read-only, so missing create/update operations are not a gap, though a dedicated list-catalog-products tool would round out the surface.