Generated from Postwind commit a19b2e4 OpenAPI 0.1.0 synced

Tool rules

The same rules hold for every Postwind tool. A tool is a thin wrapper over the use case of its API operation: validation, rights and errors are those of the API.

Names

  • Names are snake_case, verb first: create_list, launch_campaign.
  • One tool is one API operation, and the operationId of that operation equals the tool name. whoami is GET /api/v1/whoami; get_server_info is GET /api/v1/server.
  • Each tool page links to its operation, and each operation page links back.

Descriptions

Every description answers three questions, as sentences in this order:

  1. Call it … — when to call the tool.
  2. Needs … — what it takes: the kind of key, the scope, the state of the resources.
  3. Returns … — what comes back.

Each tool page shows these as three lines.

Arguments

  • Arguments are strict: the input schema has additionalProperties: false. An extra argument is validation_failed, like an unknown field in the API.
  • A tool without arguments takes {} or no arguments at all.
  • Partial updates work like PATCH in the API: an absent argument keeps the value, null clears it. null on an argument that cannot be cleared is validation_failed, like 422 with field code invalid_type in the API. The input schema of such an argument does not allow null.
  • project_id follows the rule of project context.

Output

  • structuredContent is exactly the body of the API operation’s answer, field for field. Every field is present; an empty one is null.
  • A text block carries the same JSON, for clients that read only text.
  • An API operation that answers 204 without a body gives its tool an output of outcome, effect and the resource as its GET returns it after the call. See below.

Errors

A tool error is a result with isError: true, not a JSON-RPC error:

{
  "isError": true,
  "content": [{ "type": "text", "text": "not_found: …" }],
  "structuredContent": { "code": "not_found", "detail": "…" }
}
  • code and detail are those the API puts in its problem+json. The codes are on the errors page.
  • validation_failed and precondition_failed add errors: [{ "field", "code", "message" }], as 422 does in the API.
  • The text block is "<code>: <detail>".
  • An argument of the wrong form or an extra argument is validation_failed with the parse message in detail. Unlike the API, it names the first violation of form only, without errors. Violations of values come back all at once, with errors, as in the API.
  • storage_timeout means the change may still take effect. Check before you repeat a call that creates something.
  • JSON-RPC errors are left for an unknown tool and for transport failures.
  • A bad key (401), the rate limit (429) and the body limit (413) are answered at the HTTP level, byte for byte as the API.

Confirm and dry run

Ten tools change something that cannot be undone and take a confirm argument:

Tool outcome values
launch_campaign would_launch, would_resume, launched, failed
cancel_campaign would_cancel, cancelled, already_cancelled
revoke_api_key would_revoke, revoked, already_revoked
revoke_admin_key would_revoke, revoked, already_revoked
delete_project would_delete, deleted, already_deleted
delete_list would_delete, deleted, already_deleted
delete_sending_domain would_delete, deleted
erase_subscriber would_erase, erased
add_suppression would_add, added, already_suppressed
remove_suppression would_remove, removed
  • confirm is a boolean and defaults to false.
  • Without confirm: true the call is a dry run. It checks everything the real call checks (the key, existence, preconditions) and writes nothing. It answers with the would_… outcome, effect in words, and the resource as its GET returns it.
  • With confirm: true the call does the work and answers with the outcome and the resource after the call.
  • A deleted sending domain cannot be read, so delete_sending_domain answers deleted with domain: null, and a repeat is not_found.
  • A key has no GET of its own. revoke_api_key and revoke_admin_key return the key as the key list shows it after the call; their dry run also checks the rule of the last admin key.
  • Each tool page shows a dry-run example with "confirm": false.

Limits

  • List tools return at most 100 items per call: limit 1 to 100, default 50. A larger value is validation_failed, field limit, code out_of_range. The cursor is the same as in the API; see Pagination.
  • import_subscribers takes at most 1 000 records per call. More is validation_failed and nothing is written. The API takes up to 10 000 per request.
  • A request body is at most 10 MB, as in the API.

No idempotency key

MCP has no Idempotency-Key. When a call gets no answer, what a repeat does depends on the tool:

Tool A repeat
create_project Fails with conflict: the slug is taken
create_api_key, create_admin_key Creates a second key. Check list_api_keys or list_admin_keys and revoke the extra one
create_list Creates a second list: list names need not be unique. Check list_lists and delete the extra one with delete_list
create_campaign Creates a second campaign. Check list_campaigns first
add_subscriber, import_subscribers Safe: the subscriber id comes from the list and the email address, so a repeat is the same subscriber

Rate limit

Tools share the key’s budget with the API. Each JSON-RPC message counts: initialize, notifications/initialized, tools/list and each tools/call. See Rate limits.