Skip to main content
Glama
jstibal

Openterms-mcp

issue_receipt

Generate cryptographically signed receipts to prove user consent before AI agents perform actions, enabling policy enforcement and independent verification.

Instructions

Issue a cryptographically signed terms receipt BEFORE your agent takes an action. Returns an Ed25519-signed receipt proving consent to terms. If this returns POLICY_DENIED or POLICY_ESCALATION_REQUIRED, STOP and notify the user.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
agent_idYesIdentifier for this agent
action_typeYes
terms_urlYesURL of the terms being agreed to
terms_hashYesSHA-256 hash of the terms document (64 hex chars)
timestampNoISO 8601 timestamp (defaults to now)
pricing_versionNoPricing version (defaults to 2025-01)
action_contextNoOptional metadata (provider, model, endpoint, etc.)

Implementation Reference

  • The handler implementation for the 'issue_receipt' tool, which constructs the payload and makes a POST request to /v1/receipts.
    if name == "issue_receipt":
        payload = {
            "agent_id": arguments["agent_id"],
            "action_type": arguments["action_type"],
            "terms_url": arguments["terms_url"],
            "terms_hash": arguments["terms_hash"],
            "timestamp": arguments.get("timestamp",
                datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + "Z"),
            "pricing_version": arguments.get("pricing_version", "2025-01"),
        }
        if arguments.get("action_context"):
            payload["action_context"] = arguments["action_context"]
    
        resp = client.post("/v1/receipts", json=payload, headers=_headers())
    
        if resp.status_code == 201:
            receipt = resp.json()
            headers_info = receipt.get('headers', {})
            return (
                f"βœ… Receipt issued successfully\n"
                f"  receipt_id: {receipt['receipt_id']}\n"
                f"  canonical_hash: {receipt['canonical_hash']}\n"
                f"  signature: {receipt['signature'][:32]}...\n"
                f"  key_id: {receipt['key_id']}\n"
                f"  amount_charged: {receipt['amount_charged']} (USDC minor units)\n"
                f"  --- Headers for API provider ---\n"
                f"  X-Openterms-Receipt: {headers_info.get('X-Openterms-Receipt', 'n/a')}\n"
                f"  X-Openterms-Verify: {headers_info.get('X-Openterms-Verify', 'n/a')}"
            )
        elif resp.status_code == 403:
            err = resp.json().get("error", {})
            code = err.get("code", "")
            if code == "POLICY_DENIED":
                details = err.get("details", {})
                return (
                    f"🚫 POLICY DENIED β€” Action blocked by workspace policy\n"
                    f"  Policy version: {details.get('policy_version', '?')}\n"
                    f"  Reasons: {', '.join(details.get('reasons', ['Unknown']))}\n"
                    f"  ⚠️  Do NOT proceed with this action. Notify the user."
                )
            elif code == "POLICY_ESCALATION_REQUIRED":
                details = err.get("details", {})
                return (
                    f"⏸️  ESCALATION REQUIRED β€” Human approval needed\n"
                    f"  Policy version: {details.get('policy_version', '?')}\n"
                    f"  Reasons: {', '.join(details.get('reasons', ['Unknown']))}\n"
                    f"  ⚠️  Do NOT proceed. Ask the user to approve this action."
                )
        return _format_error(resp)
  • The schema definition for the 'issue_receipt' tool, including its name, description, and input parameters.
    {
        "name": "issue_receipt",
        "description": (
            "Issue a cryptographically signed terms receipt BEFORE your agent takes an action. "
            "Returns an Ed25519-signed receipt proving consent to terms. "
            "If this returns POLICY_DENIED or POLICY_ESCALATION_REQUIRED, STOP and notify the user."
        ),
        "inputSchema": {
            "type": "object",
            "required": ["agent_id", "action_type", "terms_url", "terms_hash"],
            "properties": {
                "agent_id": {"type": "string", "description": "Identifier for this agent"},
                "action_type": {"type": "string", "enum": ["api_call", "data_access", "purchase", "custom"]},
                "terms_url": {"type": "string", "description": "URL of the terms being agreed to"},
                "terms_hash": {"type": "string", "description": "SHA-256 hash of the terms document (64 hex chars)"},
                "timestamp": {"type": "string", "description": "ISO 8601 timestamp (defaults to now)"},
                "pricing_version": {"type": "string", "description": "Pricing version (defaults to 2025-01)"},
                "action_context": {"type": "object", "description": "Optional metadata (provider, model, endpoint, etc.)"},
            },
        },
    },

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jstibal/openterms-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server