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:
| Field | Value |
|---|---|
action | mcp.tool_called |
target_id | The tool name (e.g. evenhand_questions_create). |
actor_user_id | The authenticated user. |
payload.source | mcp |
payload.mcp_client_id | The OAuth client_id of the calling client. |
payload.mcp_session_id | The access-token row id — stable per session, distinct from the token text. |
payload.input_keys | Shallow summary of the tool input's top-level keys. Raw values are not logged. |
payload.requires_write | Whether 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_deniedwithreason: "missing_per_tool_grant". See Write gating. - Per-deal opt-ins — disable MCP writes for a specific deal. Future write calls naming that
deal_idreturnpermission_deniedwithreason: "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_listno longer includes the deal.evenhand_deals_getreturns{ 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_foundposture 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_idanswers that. - A revocation needs to be reasoned about retroactively. The
payload.mcp_session_idlets 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.