Export to SIEM
MintMCP exports activity logs to your SIEM or observability platform in real time using the OpenTelemetry Logs protocol (OTLP/HTTP). Tool invocations, prompt submissions, gateway requests, and access policy changes are captured and forwarded to any OTLP-compatible endpoint, or to a Splunk HEC endpoint.
What gets exported
MintMCP exports logs from three sources:
| Source | What's captured | When |
|---|---|---|
| Agent Monitor | Tool calls from Claude Code, Cursor, and other agents | Before each tool executes (via hooks) |
| Agent Monitor | Prompt submissions from Claude Code and Cursor | Before the prompt is sent to the model |
| MCP Gateway | Tool calls routed through the gateway | After each request completes |
| Audit log | Access grants and revokes on Virtual MCP servers, and connector credential lifecycle events | When the change is recorded |
The schemas below describe the exported event fields. OTLP delivers them as log records; Splunk HEC wraps the same fields as HEC events.
Configure the export
Via the UI
- Go to Enterprise in the sidebar
- Under OTLP Export, choose the Export Format: OTLP (default) or Splunk HEC
- Enter your collector endpoint URL
- Add any required headers (e.g., API keys or authorization tokens). Header values will be encrypted.
- Enable the export
Splunk HEC format
With the Splunk HEC format, each log record is delivered as an HTTP Event Collector event instead of an OTLP payload:
| HEC field | Contents |
|---|---|
time | Event timestamp (epoch seconds) |
event | The log body merged with the log attributes |
source | Always mintmcp |
sourcetype | Always _json |
fields | The log attributes, which Splunk treats as indexed fields |
Attributes appear in both event and fields, so user and tool attribution survives receivers that only ingest event. When a body key collides with an attribute name, the body value wins; non-object bodies (like the tool call array) are nested under a body key inside event.
Log record schemas
Agent Monitor: tool call logs
Each log record contains one or more tool calls that were intercepted by the agent hook.
Attributes:
| Attribute | Type | Description |
|---|---|---|
org.id | string | Organization ID |
event.id | string | Unique event ID (ULID) |
user.id | string | User who triggered the tool call |
matched_rules | string[] | IDs of any Agent Monitor rules that matched (omitted if none) |
Body — an array of tool call objects, each containing:
{
"toolUseId": "toolu_abc123",
"tool": {
"type": "mcp",
"mcp": { "type": "remote", "url": "https://mcp.example.com" },
"name": "query_database"
},
"input": { "sql": "SELECT * FROM users LIMIT 10" },
"matchedRules": ["rule_block_pii_queries"],
"eventSource": "beforeMCPExecution"
}
The tool field identifies what was called:
| Tool type | Fields | Example |
|---|---|---|
| Agent built-in | type: "agent", agent, name | Claude Code's Bash tool |
| Remote MCP | type: "mcp", mcp.type: "remote", mcp.url, name | A MintMCP gateway tool |
| Local stdio MCP | type: "mcp", mcp.type: "stdio", mcp.command, name | A locally-running MCP server |
The eventSource field identifies which agent hook produced the record. Every Cursor tool hook event is exported: beforeShellExecution, afterShellExecution, beforeMCPExecution, afterMCPExecution, preToolUse, postToolUse, afterFileEdit, and beforeReadFile. Claude Code exports PreToolUse and PostToolUse. Prompt submissions are not tool calls, so they flow through their own schema below.
Agent Monitor: prompt submission logs
Each log record represents a prompt submitted by a user in Claude Code or Cursor, captured before the prompt is sent to the model.
Attributes:
| Attribute | Type | Description |
|---|---|---|
org.id | string | Organization ID |
event.id | string | Unique event ID (ULID) |
event.type | string | Always prompt_submit |
event.provider | string | Agent that submitted the prompt (claudecode or cursor) |
user.id | string | User who submitted the prompt |
matched_rules | string[] | IDs of any Agent Monitor rules that matched (omitted if none) |
Body:
{
"prompt": "Deploy the latest changes to staging",
"truncated": false,
"decision": "allow",
"sessionId": "session-abc123"
}
| Field | Type | Description |
|---|---|---|
prompt | string | The prompt text (truncated to 1024 characters, secrets redacted) |
truncated | boolean | Whether the prompt was truncated |
decision | string | allow or deny |
decisionReason | string | Why the prompt was denied (only present when decision is deny) |
sessionId | string | Session or conversation ID |
MCP Gateway logs
Each log record represents one completed request through the gateway.
Attributes:
| Attribute | Type | Description |
|---|---|---|
org.id | string | Organization ID |
event.id | string | Unique event ID |
endpoint.id | string | The MCP endpoint that handled the request |
user.id | string | User who made the request (omitted for non-user callers) |
user.email | string | Email of the user who made the request (omitted when unavailable) |
agent.id | string | Agent identity that made the request (omitted for user callers) |
gateway.id | string | Gateway that routed the request (omitted when unavailable) |
jsonrpc.method | string | JSON-RPC method (e.g., tools/call, tools/list) |
tool.name | string | Tool that was called (for tools/call requests) |
status | string | Result status (see below) |
duration_ms | number | Request handling time in milliseconds |
matched_rules | string[] | IDs of any gateway rules that matched (omitted if none) |
Status values:
| Status | Meaning |
|---|---|
jsonRpcSuccess | Tool call completed successfully |
jsonRpcError | Tool call returned an error |
http202 | Request accepted (streaming) |
http400 | Bad request |
http401 | Unauthorized |
http403 | Forbidden |
Body:
{
"message": "MCP request completed",
"tool.arguments": "{\"sql\": \"SELECT * FROM users LIMIT 10\"}",
"detections": [
{
"ruleId": "rule_block_aws_keys",
"ruleName": "Block AWS access keys",
"scope": "org",
"action": "block",
"matchedText": "AKIA****************",
"start": 142,
"end": 162
}
]
}
The body is truncated to 16 KB to keep export volume bounded. Failed requests use "MCP request failed" as the message and set severity to WARN.
Detections: tools/call records include a detections array when at least one gateway rule fired, with one entry per rule match:
| Field | Type | Description |
|---|---|---|
ruleId | string | Rule that fired |
ruleName | string | Rule name, captured at the time it fired |
scope | string | org for organization rules, global for MintMCP global rules |
action | string | The rule's resolved outcome: block, mask, or allow |
matchedText | string | Text the rule matched; masked when the rule is marked sensitive |
start, end | number | Character offsets of the match |
Rule name, scope, and action are snapshotted when the rule fires, so renaming or deleting a rule later doesn't change what the record says was true at the time. A rule that matched without a specific text match (e.g., a tool-level rule) produces a metadata-only entry. The flat matched_rules attribute carries the same rule IDs for backward compatibility.
Access grant audit logs
Each grant or revoke of access to a Virtual MCP server produces a log record, so your SIEM holds an independent history of who could reach which server and when that changed.
Attributes:
| Attribute | Type | Description |
|---|---|---|
org.id | string | Organization ID |
event.id | string | Unique event ID |
event.action | string | admin_config.access-grant.granted or admin_config.access-grant.revoked |
event.domain | string | Always admin_config |
event.kind | string | Always audit |
event.outcome | string | Always success |
user.id | string | Admin who made the change (omitted for system-initiated changes) |
mintmcp.audit_signature | string | JSON-encoded signature of the underlying audit row (see below) |
Body:
{
"message": "admin_config.access-grant.granted",
"detail": {
"type": "admin_config",
"audit": {
"resourceType": "access-grant",
"object": "g_4X8kQm...",
"relation": "caller",
"subject": "directory_group_01XYZ...",
"action": "grant"
}
}
}
| Field | Description |
|---|---|
object | The Virtual MCP server the grant applies to |
relation | Permission granted: caller (can connect and call tools) or editor (can manage the server, which includes calling) |
subject | Who received it: a user ID, a directory group ID, or all_organization_members |
action | grant or revoke |
Access grant history rows are signed at write time with a per-record digital signature over the row's identity and payload. The signature ships in the mintmcp.audit_signature attribute, and the public verification key is published as JWKS at /.well-known/mintmcp/audit-signing/jwks.json, so you can verify offline that exported records match what MintMCP originally wrote.
Connector credential lifecycle events (credential created, updated, removed, admin-revoked, or system-invalidated) flow through the same audit schema with event.domain set to connector_credential and actions like connector_credential.credential_created. See Audit and observability for what these events cover.
Related
- Agent Monitor overview: How hooks capture agent activity
- Agent Monitor rules: Create rules that match and block tool calls
- Authentication models: How user identity flows through logs