MCP authoring guide

What an MCP server needs to look like to play well with PipedAI triggers.

PipedAI is the orchestrator and ledger; your MCP is where the work happens. Every trigger points at one MCP URL with one set of headers. When the worker fires the trigger, it generates a per-run MCP config, invokes claude -p against your prompt, and Claude calls into your MCP tools to do the actual work and write results.

The contract

  • HTTP MCP server reachable from the worker host. Streamable HTTP transport (Anthropic's standard) is what claude -p --mcp-config speaks.
  • Service-token auth via the Authorization header (or any fixed-name header). The header value is what you put into the trigger's MCP headers JSON.
  • Idempotent or guarded tools. Triggers can fire multiple times (cron, retries) — design tools to either be idempotent or to detect and skip duplicates.

The MCP headers JSON

On the trigger editor, the “MCP headers (JSON)” field takes a flat object of string→string. Every entry is added to the request headers when Claude calls the MCP. The shape is whatever your MCP expects; the most common is an Authorization Bearer:

json{
  "Authorization": "Bearer service-token-xxxxx"
}

Other common patterns:

json{
  "x-api-key": "service-token-xxxxx",
  "x-tenant": "acme-prod"
}
Encryption at rest
These headers are encrypted with libsodium-secretbox under a per-environment data key, which is itself wrapped by a master key on the API server. Plaintext is only handed to the worker via the poll endpoint and never returned in any read response — the trigger detail surface only shows hasMcpHeaders: true, never the values.

Service-token pattern

Marolence MCPs follow the convention /automated/<mcp_name>/mcp with a service token issued by the MCP's own backoffice. Generate one per-trigger so you can revoke it surgically without touching unrelated triggers.

Token rotation

On the trigger editor, the “Rotate token” button opens a dialog for new headers. Behind the scenes it calls POST /api/v1/triggers/:id/rotate-mcp-token which is admin+ only and audited as a separate mcp-token.rotated action. The new headers take effect on the next run; in-flight runs continue with the old.

Authoring the prompt

The prompt is plain English with full access to the MCP tools. Standard guidance:

  • State the goal explicitly. “Process the queue and write results back via create_record.”
  • Reference tool names where useful so Claude has concrete handles.
  • For cases where there's no work, instruct Claude to say so explicitly — PipedAI captures the final assistant message into shortMessage on the run record.
  • For unrecoverable errors, have the prompt emit [error: …] in the assistant text. The worker's fault classifier picks that up and tags the run as a client fault, which suppresses auto-retry.

Payload templating (on-demand)

Triggers fired via POST /triggers/:id/run can carry a JSON payload. The worker substitutes {{payload}} and {{payload.foo.bar}} tokens into the prompt before invoking claude -p. Useful for webhook-driven triggers where the inbound event determines what Claude works on.

Notify downstream systems on completion

For MCPs that need to react to run outcomes (Slack notifications, downstream queues, billing events), set the trigger's webhookUrl + webhookSecret on the trigger editor. PipedAI POSTs an HMAC-SHA256-signed JSON body to that URL on every terminal Run transition (succeeded / failed / timed_out). See the completion webhooks guide for payload shape, retry semantics, and verification samples in Node, Python, and Ruby.