Summary
Add a source column to the AuditLog model and propagate request source information through @fastify/request-context so audit loggers can record how an action was triggered.
Why
MCP platform tools use app.inject() to call existing HTTP routes. Audit logging happens inside those route handlers. From the route's perspective, an MCP-originated request looks identical to a regular API request. Without explicit signaling, administrators have no way to tell "user did this in the UI" from "3rd party agent did this via MCP" from "first-party expert agent did this."
A queryable database column (rather than burying it in the JSON body) lets administrators filter audit logs by source directly, which is the primary use case: "show me everything an MCP agent did in the last 24 hours."
@fastify/request-context is the carrier because it's already used for PAT metadata (#7411). The audit loggers can read from it without changing their function signatures or touching every route handler that calls them. The source is set once (during auth / header processing) and available everywhere downstream.
What to do
Database migration
- Add
source column to AuditLogs table: STRING, nullable, default null
- Null represents the legacy/UI path (cookie session, no PAT, no MCP header)
Source detection and propagation
- After
verifySession, detect the request source and store it in @fastify/request-context:
- Cookie session (no PAT): source remains null (or
ui if we want to be explicit)
- PAT present, no MCP header:
api
- MCP custom header
X-FF-Audit-Source: mcp: mcp (3rd party agent)
- MCP custom header
X-FF-Audit-Source: mcp:expert: mcp:expert (first-party agent)
- Also store the token identifier from
X-FF-Audit-Token header when present, for traceability to the specific PAT/integration
Audit logger updates
- Update the centralized audit logging functions (
platformLog, userLog, teamLog, projectLog, applicationLog, deviceLog in forge/db/controllers/AuditLog.js) to read source from @fastify/request-context and pass it to the model
- Update the AuditLog view formatter to include
source in the output
- Update the
triggerObject() in formatters.js to include the token identifier when available
Tests
- MCP-originated action (via
app.inject() with custom headers) records source: 'mcp'
- First-party expert action records
source: 'mcp:expert'
- Direct PAT API call records
source: 'api'
- Cookie session action records
source: null (or ui)
- Token identifier is included in the audit body when present
- Existing audit entries (before migration) have null source
- Audit log API returns the source field
References
Summary
Add a
sourcecolumn to the AuditLog model and propagate request source information through@fastify/request-contextso audit loggers can record how an action was triggered.Why
MCP platform tools use
app.inject()to call existing HTTP routes. Audit logging happens inside those route handlers. From the route's perspective, an MCP-originated request looks identical to a regular API request. Without explicit signaling, administrators have no way to tell "user did this in the UI" from "3rd party agent did this via MCP" from "first-party expert agent did this."A queryable database column (rather than burying it in the JSON body) lets administrators filter audit logs by source directly, which is the primary use case: "show me everything an MCP agent did in the last 24 hours."
@fastify/request-contextis the carrier because it's already used for PAT metadata (#7411). The audit loggers can read from it without changing their function signatures or touching every route handler that calls them. The source is set once (during auth / header processing) and available everywhere downstream.What to do
Database migration
sourcecolumn toAuditLogstable: STRING, nullable, default nullSource detection and propagation
verifySession, detect the request source and store it in@fastify/request-context:uiif we want to be explicit)apiX-FF-Audit-Source: mcp:mcp(3rd party agent)X-FF-Audit-Source: mcp:expert:mcp:expert(first-party agent)X-FF-Audit-Tokenheader when present, for traceability to the specific PAT/integrationAudit logger updates
platformLog,userLog,teamLog,projectLog,applicationLog,deviceLoginforge/db/controllers/AuditLog.js) to read source from@fastify/request-contextand pass it to the modelsourcein the outputtriggerObject()informatters.jsto include the token identifier when availableTests
app.inject()with custom headers) recordssource: 'mcp'source: 'mcp:expert'source: 'api'source: null(orui)References
forge/db/controllers/AuditLog.jsforge/auditLog/formatters.js@fastify/request-contextsetup: Add PAT (Personal Acces Tokens) scopes #7411 (T1, T2)