MCP Audit Log¶
The MCP audit log records everything that happens through the MCP server: every client registration, sign-in, consent and token, every session a client starts, every tool call, every guide or help page read and every prompt used, every confirmation a person answers, and every message body or log text returned. Each event is written twice, as a row in the mcp_audit_entry table and as one line in mcp-audit.log. The table is the record QIE reads; the file is the copy that outlives the table's retention and can be shipped to another system.
An entry holds identifiers. It names the user, the client, the address and the object, but it never holds a token, an authorization code, a password, a message body or a system variable value. Secret fields, sample message bodies and variable values are left out of an entry entirely. Two kinds of entry can hold text that came from the client: a refused call keeps its arguments while Return message content is on, with credentials masked, and identify_session keeps the task the model reported. An export leaves both out (see Exporting the log).
Viewing the log¶
Administrators read the log in the console. On the Model Context Protocol (MCP) page in System Administration, click View Entries in the Audit Log section to open the MCP Audit Log dialog. The dialog shows the newest entries first, a page holding as many entries as the list shows without scrolling, and the paging bar under the list moves between pages and shows how many entries match. The dialog opens showing nine entries; resizing or maximizing it changes how many entries a page holds.
The Filters section narrows the list, and Search applies it:
| Filter | Matches |
|---|---|
| From and To | Entries from the start of the From day to the end of the To day. Either may be left empty. |
| User | The QIE user id, exactly. |
| Outcome | One outcome, or Any. |
| Client Id | The client id, exactly. |
| Client Name | Client names that contain the text, in any case. |
| Session | The session id, exactly. |
| Tool | The tool name, exactly, such as save_object. |
Clear empties every filter and shows the newest entries again.
The list shows the Audit Id, time, event, outcome, user, client name, tool, target, duration and the start of the error text of each entry. The client id and version, session, request id, source address, access and scope columns are hidden; show them from any column's menu. The columns keep their widths, and a scroll bar appears when they are wider than the dialog. The Audit Id is the number an error returned to a client names.
Double-click an entry to open it in the MCP Audit Entry window, which shows every field of the entry, the whole error text, and the details as indented JSON that can be selected and copied.
When an entry is written¶
A call that changes QIE, or returns message content, does not run until its entry is written. QIE first writes the entry with the outcome STARTED, then runs the call, then updates the entry with how it ended and how long it took. An entry still marked STARTED therefore marks a call that never finished, for example because the engine stopped.
If QIE cannot write that first entry, it refuses the call, logs a warning and raises the audit failure alert described in Alerts. The client is told that the call did not run.
Each entry is committed on its own, so a change that fails and is rolled back still leaves its entry. Events that do not wait on their entry, such as sign-in events, session starts, reads of object lists, guide and help page reads and prompts, are written once they finish.
Fields¶
| Field | Holds |
|---|---|
| Id | The entry's number. Errors returned to a client name it, so an administrator can find the entry. |
| Timestamp | When the event started. |
| Event type | What happened, from the list below. |
| Outcome | STARTED, SUCCEEDED, REFUSED (refused before it ran), FAILED (ran and failed) or DECLINED (the person declined a confirmation). |
| Duration | Milliseconds from start to finish. |
| User | The QIE user id. |
| Client id, name and version | The registered client and what it calls itself. |
| Token | The number of the token record, never the token. Empty for a call through the Hub, which has no token at the engine. |
| Source address | Where the request came from. |
| Session and request | The client's session id and the request id, when there are any. |
| Tool, access and scope | The tool called, whether it read or wrote, and the token's scope. |
| Target | The zone, object type and object id the call acted on. |
| Result size | Bytes returned. |
| Content returned | Whether message content was returned. |
| Content edited | For a resubmit, whether the message was edited first. |
| Approved revision | For a confirmation, the version of the object the person approved. |
| Error text | Why a call was refused or failed, up to 4,000 characters. |
| Details | Further identifiers for the event, such as the channel and message for a message read. See What the details hold. |
Event types¶
| Event type | Written when |
|---|---|
CLIENT_REGISTERED, CLIENT_REGISTRATION_REJECTED |
A client registered, or a registration was refused. |
CLIENT_METADATA_FETCHED, CLIENT_METADATA_REJECTED |
A client metadata document was fetched, or refused. |
AUTHORIZATION_REFUSED |
A sign-in was refused before consent. |
HUB_OR_LOOPBACK_REFUSED |
A sign-in came through a Hub or from the engine's own host and was refused. |
CONSENT_GRANTED, CONSENT_DENIED |
The person approved or declined the client. |
TOKEN_ISSUED, TOKEN_REFRESHED, TOKEN_REQUEST_REJECTED |
A token was issued, renewed or refused. |
CODE_REUSE_DETECTED, REFRESH_REUSE_DETECTED |
An authorization code or refresh token was used a second time; the tokens issued from it are revoked. |
TOKEN_REVOKED, TOKENS_REVOKED_FOR_USER, TOKENS_REVOKED_ALL, CLIENT_REVOKED, CLIENTS_REVOKED_ALL |
Tokens or clients were revoked, with what triggered it and how many. |
REQUEST_REJECTED |
The listener refused a request. |
RATE_LIMIT_BREACH |
A client kept exceeding a limit, or a person calling through the Hub went over one. |
SESSION_STARTED |
A client started a session. |
TOOL_CALL |
Any tool was called. |
RESOURCE_READ |
A guide, the Code Wizard reference or a help page was read. The entry names it, never its text. |
PROMPT_USED |
A prompt was used to start a task. The entry names the prompt, never what the person filled in. |
CONFIRMATION_REQUESTED, CONFIRMATION_ANSWERED |
A person was asked to confirm an action, and answered. |
REPORT_SENT |
A report about the session was sent to Qvera support. The entry names the report's kind and sizes, never its text. |
CONTENT_READ |
A message body, log text, cache value or sample message was returned. |
TAMPERED_REVISION |
A change arrived with a revision token that failed its check. |
AUDIT_PURGED |
The nightly retention run, with its cutoff and how many entries it deleted. |
RECORDS_CLEANED |
The nightly cleanup of expired tokens, codes and registrations. |
What the details hold¶
| Entry | Details |
|---|---|
SESSION_STARTED |
The protocol version, the client's User-Agent, and the client information and capabilities the client declared. The session id is the one the server gave the session, so every later entry of the session carries it. |
TOOL_CALL |
The id the client gave the call, when it sends one. For a check or a test that ran, what its result reported: whether it was valid, how many problems it found, or why a test run ended, never the text. For a call that did not succeed, its error code. For a refused call while Return message content is on, its arguments, with credentials masked and a certificate, key or configuration file recorded only by its length, up to 4,000 characters. |
TOOL_CALL of identify_session |
The model, effort level and task the model reported. The server instructions ask the model to call identify_session at the start of a session; QIE records what it reports and checks none of it. |
RESOURCE_READ, PROMPT_USED |
The address of the resource, or the name of the prompt. |
REPORT_SENT |
The report's kind, the length of the model's text and of the person's comment, whether the person approved it through a form or through the model, whether a name and an email were given, which entries it carried and how many, and the size of the zipped export. |
Calls Through the Hub¶
An engine that accepts MCP calls through its Hub records them in this log as it records calls to its own MCP server, with these differences (see MCP Calls Through the Hub):
- The client id is
hub:followed by the Hub's id for the AI client, and the client name and version are the ones the AI client gave the Hub. - The session is the Hub's MCP session, the request id is the id of the statement the Hub signed for the call, and the source address is the address the Hub saw the client call from.
- No token is recorded.
- An action the person must confirm writes
CONFIRMATION_REQUESTEDwhen the first call stops with the question, andCONFIRMATION_ANSWEREDwhen the second call brings the answer. - A
RATE_LIMIT_BREACHentry for a person over a limit has the client idhub, and its details name the statement.
A Hub keeps a log of its own for its MCP server, with the same fields. Its entry for a call to a site names the site's label as site, or the tag as tag, and the statements it signed as statementIds in the details, so it can be matched with the site's entry (see The MCP Server on a Hub).
Exporting the log¶
To send the log to Qvera support, click Export in the MCP Audit Log dialog. In the Export MCP Audit Log dialog, choose Export one session and enter the Session, or Export a date and time range and enter the From and To. When an entry is selected, the dialog opens on its session; otherwise it opens on the dialog's dates, or on today. Export downloads a ZIP file holding one JSON Lines file, mcp-audit-log-<date>-<time>.jsonl.
The first line of the file describes the export: the engine's name and version, when it was exported and by whom, and the session or range. Each line after it is one entry, oldest first, with the same fields as a line of mcp-audit.log.
The export leaves out what can hold message content, whatever Return message content is set to now or was set to when the entry was written:
- a refused call's arguments
- the task
identify_sessionreported - the error text of every entry that did not fail, because a refusal can quote what was sent
Each is replaced by {"$omitted": <name>, "length": <characters>}, so the reader can see that it was there. A failed entry keeps its error text, which QIE writes itself: the exception's type and code locations without its message, or a fixed sentence. One export holds at most 50,000 entries; for more, export a shorter range.
The send_report tool sends the same export of its own session with a report, after the person approves it (see Sending a report to Qvera).
The log file¶
mcp-audit.log is in the logs folder of the QIE home directory. Each line is one JSON object with the same fields as the table row, so a line can be matched to its row by id. A call that is written before it runs appears twice: once when it starts and once when it ends. The file rolls over at 50 MB and keeps twenty files.
In a container, the same lines also go to standard output, each carrying the logger name mcp-audit, so the platform's log collector picks them up.
Retention¶
Each night QIE deletes table entries older than the Audit Retention (days) setting on the Model Context Protocol (MCP) page, 365 days by default, and writes one AUDIT_PURGED entry naming the cutoff and the count. A setting of 0 keeps every entry. In a cluster, one node runs the deletion. On a Hub, the deletion runs with the Hub's daily cleanup (see The MCP Server on a Hub). The log files are not affected by retention.
There is no way to delete entries by hand, the message purge does not touch them, and they are never part of a configuration export.