GeezerVison is showing github.com — feat: Add structured audit logging for MCP feature server · Issue #6452 · feast-dev/feast
feat: Add structured audit logging for MCP feature server #6452DescriptionIs your feature request related to a problem? Please describe.
Feast can expose the Python feature server as an MCP server (feature_server.type: mcp, mcp_enabled: true, endpoint /mcp) via fastapi_mcp. Operators and platform teams need a clear audit trail for agent/MCP access: who invoked which tool, what resource was accessed, whether authentication and authorization succeeded or failed, and a correlation ID that links MCP calls to internal REST requests.
Today, MCP integration only logs startup/errors (sdk/python/feast/infra/mcp_servers/mcp_server.py). That is not enough for security and operations use cases:
| Mechanism |
What it does |
Gap for MCP audit |
feature_logging |
Logs served feature values to the offline store |
Not a security audit log; not wired on the Python FastAPI MCP server |
| Uvicorn access logs |
HTTP path/status (off by default) |
No principal, no MCP tool name |
| RBAC logs |
Unstructured permission messages in app logs |
No MCP correlation; often DEBUG-only for allows |
With auth.type: no_auth: MCP tool calls are effectively anonymous — no principal in logs.
With auth.type: oidc / kubernetes: RBAC applies on secured REST routes when the MCP client forwards Authorization (default in fastapi_mcp), but there is still no structured audit stream for MCP tools/call, successful reads, or request correlation. The /mcp transport itself is not audited.
Registry MCP (registry.mcp.enabled: true) has the same gap.
Describe the solution you'd like
Add structured audit logging (JSONL) for the Python MCP feature server, separate from feature_logging:
- Config under
feature_server (e.g. audit_logging.enabled, sink, file_path, require_auth_for_mcp, log_successful_reads).
- Audit events with stable schema:
event_type, UTC timestamp, request_id, principal, source (IP, transport), action (MCP tool / HTTP path), resource, outcome, duration_ms.
- Two emission layers:
- MCP middleware on
/mcp (mcp.tools.call, session events).
- REST + RBAC hooks (
authn.*, authz.decision, http.request) on secured endpoints.
- Header propagation via
FastApiMCP(headers=[...]) so MCP and REST events share request_id (and optionally tool name).
- No sensitive payloads in audit logs (no tokens, entity rows, or feature values).
Optional follow-ups: registry MCP parity, OTEL sink, operator CRD fields.
Example config:
feature_server:
type: mcp
mcp_enabled: true
audit_logging:
enabled: true
sink: stdout
Example event (JSONL):
{
"event_type": "mcp.tools.call",
"timestamp": "2026-05-28T12:00:00.000Z",
"request_id": "…",
"principal": {
"username": "[email protected]",
"roles": ["feast-reader", "ml-engineer"],
"auth_type": "oidc"
},
"source": { "ip": "10.0.0.1", "transport": "mcp-http" },
"action": { "mcp_tool": "get_online_features", "path": "/get-online-features" },
"resource": { "type": "feature_service", "name": "driver_activity_v1", "actions": ["READ_ONLINE"] },
"outcome": "success",
"duration_ms": 42
}ActivityHarperZ9 commented on Jun 28, 2026 This audit stream becomes much more useful if each mcp.tools.call event is treated as a compact execution receipt, not only as an access log row.
I would add a few fields to the proposed schema:
decision_id: stable join key shared by MCP middleware, RBAC decision, downstream REST request, and outcome event
policy_ref / policy_digest: the ruleset or RBAC snapshot that allowed, denied, or required approval
resource_digest: digest of the feature service/view identity and request shape, without feature values or entity rows
outcome_state: success | denied | error | timeout | redacted | unverifiable
prev_event_hash: optional per-session hash chain for tamper-evident export
Two fixture cases would catch most regressions:
- OIDC principal calls
get_online_features successfully: MCP event, authz decision, REST request, and outcome all share decision_id; no feature values are logged.
- Anonymous or unauthorized MCP call: denial event is emitted with principal/auth context, policy reference, and no downstream feature-read event.
That keeps the audit trail useful for security review without turning it into a second feature log or leaking the data Feast is serving.
MetadataMetadataDevelopmentNo branches or pull requestsIssue actions
|