Skip to main content

Audit & revocation

Every deal-scoped MCP tool call leaves an auditable trail. Tokens, per-tool grants, and per-deal opt-ins are independently revocable by the user without operator involvement. When a user's deal participation ends, MCP visibility collapses to match — without a 403, without an error, and without leaking the existence of resources outside the user's tenancy.

Audit log

Every deal-scoped tool call writes one row to audit_log:

FieldValue
actionmcp.tool_called
target_idThe tool name (e.g. evenhand_questions_create).
actor_user_idThe authenticated user.
payload.sourcemcp
payload.mcp_client_idThe OAuth client_id of the calling client.
payload.mcp_session_idThe access-token row id — stable per session, distinct from the token text.
payload.input_keysShallow summary of the tool input's top-level keys. Raw values are not logged.
payload.requires_writeWhether the tool needed mcp:write.

The same hash-chain that covers web-app activity covers MCP-emitted rows. The chain verifier handles them identically; there is no separate MCP audit pipeline.

Raw input values are not logged. The input_keys summary is enough for an operator to ask "did this client call this tool on this deal with these field names" without writing secrets, PII, or free-text question bodies into the audit log.

Tools that don't write an audit row

evenhand_deals_list does not write an audit row. The chain is scoped on (organization_id, deal_id) and a deal-list call has no single deal to bind to. The OAuth token row is the canonical trail for those calls — every access token records its creation, last-used timestamp, and the user it represents.

Revocation paths

There are three revocation paths, ordered from most to least common:

1. User-driven session revocation

A user manages every aspect of their MCP authorization from Settings → Connected MCP clients (/buyer/settings/mcp-clients). The page surfaces:

  • Connected clients — revoke any client. All tokens issued to that client are invalidated immediately. The next MCP request from that client returns HTTP 401.
  • Per-tool grants — disable a specific write tool. Existing in-flight calls complete; future calls return permission_denied with reason: "missing_per_tool_grant". See Write gating.
  • Per-deal opt-ins — disable MCP writes for a specific deal. Future write calls naming that deal_id return permission_denied with reason: "missing_per_deal_optin".

Revocation is immediate and does not require operator action.

2. RLS shutdown when participation ends

When a user is removed from a deal's participant list — or their participation moves to revoked — the deal disappears from their MCP read surface. The shape of that change is deliberately quiet:

  • Read tools return empty results, not 403. evenhand_deals_list no longer includes the deal. evenhand_deals_get returns { deal: null, participants: [] }. evenhand_questions_list, evenhand_offers_list, and the QoE tools return empty arrays.
  • The MCP API does not distinguish "deal doesn't exist" from "deal exists but you can't see it." This mirrors the REST API's <resource>_not_found posture and is intentional — leaking existence is a tenancy boundary leak.
  • Write calls against the deal hit the per-deal opt-in gate or RLS deeper in the dispatcher; in either case they fail closed without revealing whether the deal still exists.

There is no notification from the server when this happens. Clients that retain deal_id references should handle empty responses without escalating them.

3. Operator-driven hard revocation

If a user has lost control of a client and cannot reach the settings page (for example, they have lost access to their device or their account), your brokerage's support contact at Evenhand can revoke on your behalf if required. The operator path is documented in the platform's internal runbooks and uses the same revocation primitive the settings page does — there is no privileged backdoor.

Contact your brokerage's Evenhand support representative with the user's email and the approximate time window during which the compromise occurred. Operator-driven revocation is recorded in the audit log as a distinct action so the trail of "who revoked what, when" is preserved.

How to read the audit log

The audit_log is internal infrastructure; brokerages don't have direct SQL access. The audit trail is consulted when:

  • A user disputes an action attributed to them. The trail tells the operator which client made the call and which deal was touched.
  • A security review needs the full set of tools a given client has called across a window. Filtering by payload.mcp_client_id answers that.
  • A revocation needs to be reasoned about retroactively. The payload.mcp_session_id lets the operator scope the question to a specific access-token lifetime.

If you need the audit log inspected for a specific incident, contact your brokerage's Evenhand support representative. The platform retains audit-log rows for the lifetime of the brokerage tenancy.