MCP servers
Integrations MCP server
The Integrations MCP server's endpoint, key, tools, prompt, and resource.
Endpoint
POSThttps://www.postaged.com/api/integrations/mcp
Authenticate with an integration key (it starts with pk_int_) in the Authorization: Bearer header. Create a dedicated key with New key on your integration's page and use it only for Claude, so you can revoke it on its own.
The server takes JSON-RPC 2.0 messages over HTTP POST (MCP protocol version 2025-06-18) and introduces itself as postaged-integration. It answers every notification with 202 and an empty body. See Connect Claude to add it to your Claude client.
Claude Code
claude mcp add --transport http postaged https://www.postaged.com/api/integrations/mcp --header "Authorization: Bearer ${POSTAGED_API_KEY}"Tools
The server has 10 tools. Each returns one text item: JSON for every tool except get_docs, which returns the build guide as Markdown.
inspect_integration
Read this integration's name, status, whether setup is finished, any open sync, and the API limits. Call this first.
No arguments.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "inspect_integration",
"arguments": {}
}
}{
"id": "int_ABCD1234",
"name": "Dandy CRM",
"status": "setup_needed",
"setupComplete": false,
"openSync": null,
"hint": "No sync is open. Call begin_sync to start one.",
"limits": {
"recordsPerBatch": 1000,
"bytesPerBatch": 4456448,
"declaredFields": 1000
}
}get_schema
List the fields currently declared for this integration.
No arguments.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_schema",
"arguments": {}
}
}{
"fields": [
{
"key": "email",
"label": "Email",
"type": "text",
"semantic": "email"
},
{
"key": "deal_value",
"label": "Deal value",
"type": "number",
"semantic": null
}
]
}declare_schema
Declare the fields you will push. A proposal: fields the customer has already typed in Postaged keep their type and this fails naming them. Ask the customer before calling.
- fieldsarray of objectsRequiredEach: key, label, type (text|number|date|yes_no|choice), optional semantic.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "declare_schema",
"arguments": {
"fields": [
{
"key": "email",
"label": "Email",
"type": "text",
"semantic": "email"
},
{
"key": "deal_value",
"label": "Deal value",
"type": "number"
}
]
}
}
}{
"ok": true,
"added": 1,
"noLongerSent": []
}validate_records
Check records against the contract and write NOTHING. Use this while building; it needs no open sync.
- recordsarray of objectsRequired
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "validate_records",
"arguments": {
"records": [
{
"external_id": "crm-1042",
"fields": { "email": "dana@example.com", "deal_value": "1500" }
},
{ "external_id": "crm-0977", "op": "delete" }
]
}
}
}{
"accepted": 2,
"rejected": 0,
"rejections": [],
"warnings": []
}begin_sync
Open a sync. 'full' marks everything it does not see as removed at completion; 'delta' removes only what you delete explicitly. One open sync at a time.
- modestringOptionalOne of
fullordelta.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "begin_sync",
"arguments": { "mode": "full" }
}
}{
"id": "0b6f3c3e-8d2a-4f7e-9a51-2c4d8e6f1a20",
"mode": "full",
"expiresAt": "2026-10-04T14:05:00.000Z",
"batchesReceived": 0,
"recordsUpserted": 0,
"recordsDeleted": 0,
"recordsRejected": 0,
"status": "open"
}push_records
Push a batch into the open sync. Returns what was accepted, one rejection per bad row, and the values dropped from accepted rows grouped by field, each with a count and up to 5 example record ids; one bad record never stops the others.
- recordsarray of objectsRequired
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "push_records",
"arguments": {
"records": [
{
"external_id": "crm-1042",
"fields": { "email": "dana@example.com", "deal_value": "1500" }
},
{ "external_id": "crm-0977", "op": "delete" }
]
}
}
}{
"accepted": 1,
"deleted": 1,
"rejections": [],
"warnings": []
}complete_sync
Close the open sync, applying a full sync's removal sweep.
No arguments.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "complete_sync",
"arguments": {}
}
}{
"ok": true,
"markedRemoved": 14,
"nextStep": "Tell the owner to finish setup in Postaged to choose which fields to keep."
}sync_status
Report the open sync's progress and counts, if one is open.
No arguments.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "sync_status",
"arguments": {}
}
}{
"openSync": {
"id": "0b6f3c3e-8d2a-4f7e-9a51-2c4d8e6f1a20",
"mode": "full",
"expiresAt": "2026-10-04T14:05:00.000Z",
"batchesReceived": 2,
"recordsUpserted": 1998,
"recordsDeleted": 0,
"recordsRejected": 2
}
}get_rejections
List the records a sync rejected (or, with type 'warning', the values it dropped from accepted records), one page at a time. Defaults to the latest sync. Pass nextAfter back as after for the next page.
- syncIdstringOptionalThe sync to read. Omit for the latest sync.
- typestringOptionalOne of
rejectionorwarning. - afterstringOptionalThe nextAfter value of the previous page.
- limitnumberOptionalItems per page, 1 to 1000. Defaults to 100.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_rejections",
"arguments": {
"syncId": "0b6f3c3e-8d2a-4f7e-9a51-2c4d8e6f1a20",
"type": "rejection",
"limit": 100
}
}
}{
"syncId": "0b6f3c3e-8d2a-4f7e-9a51-2c4d8e6f1a20",
"type": "rejection",
"items": [
{
"id": 7,
"batch": 1,
"index": 3,
"externalId": "crm-1077",
"code": "invalid_email",
"field": "email",
"message": "\"email\" does not look like an email address."
}
],
"nextAfter": null
}get_docs
The full integration guide: endpoints, caps, error codes, and scheduling.
No arguments.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_docs",
"arguments": {}
}
}# Build a daily Postaged sync for Dandy CRM
You are setting up a one-way daily sync from this project's CRM into Postaged,
which prints and mails physical letters to the contacts you send.
## What you are building
(The guide continues with the schema, sync, error, and scheduling steps.)Prompt
build_daily_sync: The full instructions for building a daily Postaged sync from this CRM.
prompts/get returns the same guide get_docs does. Build a daily CRM sync is adapted from it for a person to read.
{
"jsonrpc": "2.0",
"id": 1,
"method": "prompts/get",
"params": { "name": "build_daily_sync" }
}Resource
postaged://contract (Push API contract, text/markdown): Endpoints, caps, and error codes.
resources/read returns the same guide as Markdown.
{
"jsonrpc": "2.0",
"id": 1,
"method": "resources/read",
"params": { "uri": "postaged://contract" }
}Errors
JSON-RPC errors come back with HTTP 200. Authentication, availability, and rate-limit failures use the push API's error body with HTTP 401, 403, or 429. A tool that cannot do what it was asked returns a result whose text carries error and message. See Errors for an example of each, and the Error reference for every code.
Limits
- 4.25 MB per request body, and 1,000 records per
push_recordscall. - About 120 requests per minute per source IP, counted together with the push API. Over the limit:
429 rate_limitedwithRetry-After: 60.