Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.closient.com/llms.txt

Use this file to discover all available pages before exploring further.

Every tool exposed by the Closient MCP server carries a structured annotations object that tells the host (Claude, Claude Desktop, the Connectors Directory validator, third-party MCP clients) whether the tool mutates state and whether repeated calls are safe to retry. This is required by the MCP specification and by the Anthropic Connectors Directory submission process — servers that omit the annotations are rejected at submission time.

Annotation fields

Each tool sets a subset of the following hints. They are advisory — agents and hosts use them to decide whether to gate a call behind a confirmation prompt, or whether retrying after a transport error is safe.
FieldMeaning
readOnlyHinttrue → the tool performs no state changes. Hosts may invoke without confirmation.
destructiveHinttrue → the tool may destructively modify user-visible state (delete, overwrite).
idempotentHinttrue → repeated calls with identical arguments yield identical effects.
openWorldHintfalse → the tool reads only from Closient-controlled data (catalog, resolver rules).
titleHuman-readable label for UIs that prefer a polished name over the snake-case tool name.
readOnlyHint=true and destructiveHint=true are mutually exclusive. A CI test (test_destructive_hint_implies_not_read_only) enforces this.

Closient’s policy

We use a narrow reading of destructiveHint: it means destructive changes — deletes, overwrites, revocations, cancellations — not “any mutation.” A tool that adds a record without erasing prior state is flagged readOnlyHint=false without destructiveHint. The trade-off: destructiveHint=true causes most hosts to render an extra “are you sure?” prompt before the call. That’s the right UX before deleting a brand claim or canceling a subscription, and the wrong UX before generating a QR code or recording an audit event. We reserve the hint for the former.

Per-tool annotations

ToolreadOnlyHintdestructiveHintidempotentHintNotes
pingtruetrueHealth probe; no auth, no I/O.
validate_gtintruetruePure validation — no DB access.
resolve_gtintruetrueDB read against the resolver-rule hierarchy.
lookup_producttruetrueDB read against the product catalog.
generate_qr_urlfalsetrueBuilds a Digital Link URL and records the call via @user_action. State-mutating but not destructive.
generate_qr_url is the lone non-read-only tool today. The decision to omit destructiveHint is intentional and pinned by test_no_tool_currently_marked_destructive in backend/closient/tests/test_mcp_tool_annotations.py — the test fails loudly if a future contributor flips the bit without updating policy.

Wire format

Annotations are serialized as a nested object on each entry of the tools/list response. Pydantic’s exclude_none=True mode drops fields that weren’t set, so the wire payload is minimal:
{
  "name": "lookup_product",
  "title": "Look Up Product",
  "description": "Look up structured product data by GTIN. ...",
  "inputSchema": { "type": "object", "properties": { ... } },
  "annotations": {
    "title": "Look Up Product",
    "readOnlyHint": true,
    "idempotentHint": true,
    "openWorldHint": false
  }
}
You can verify the shape against a running server with the MCP Inspector or a one-off curl:
curl -sS https://www.closient.com/mcp/http \
  -H 'Accept: application/json, text/event-stream' \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'

Adding a new tool

When you register a new tool with @mcp.tool() in backend/closient/mcp_tools.py:
  1. Pass annotations=ToolAnnotations(...) with at least one of readOnlyHint / destructiveHint set.
  2. Add a row to the per-tool table above and to the parametrized test_tool_read_only_hint_matches_policy test.
  3. If the tool is destructive, update test_no_tool_currently_marked_destructive so the assertion still reflects ground truth.
The CI test test_every_registered_tool_has_annotations is the safety net — a missing annotation fails the suite before it can ship.