Skip to content

Python: Enforce FIDES MCP IFC labels - #9879

Draft
Eduard van Valkenburg (eavanvalkenburg) wants to merge 1 commit into
fides-agui-replay-provenancefrom
fides-mcp-ifc-enforcement
Draft

Eduard van Valkenburg (eavanvalkenburg) wants to merge 1 commit into
fides-agui-replay-provenancefrom
fides-mcp-ifc-enforcement

Conversation

@eavanvalkenburg

Copy link
Copy Markdown
Member

Motivation & Context

Generated MCP results can lose server-provided _meta.ifc when the generic Host payload budget is exhausted. This can
make successful results fall back to a less restrictive local label. Detailed MCP errors also pass through generic
function-error conversion, where labels can be lost, committed for a swallowed retry, applied to the wrong session
scope, or serialized with private marker objects. Invalid or oversized identity IFC must not inherit local principals.

This is the fifth and final layer of the upstream replacement stack for #9652. It builds on #9875, #9876, #9877, and
#9878 and isolates MCP IFC enforcement from the lower service-history, replay-authority, and AG-UI provenance layers.

Description & Review Guide

  • What are the major changes? Generated MCP calls now retain _meta.ifc through an independently bounded private
    carrier outside generic Host payload budgets. Successful results combine local policy with server IFC, while
    invalid, oversized, or unretainable IFC fails closed, including identity confidentiality and principal handling.
    Detailed MCP errors receive the same labels after retry finalization, preserve argument-derived confidentiality and
    local identity principals, update the invocation's captured session scope, and remove private markers before
    serialization. Hidden built-in MCP placeholders no longer regain carrier metadata during finalization, so replay
    stays integrity-clean.
  • What is the impact of these changes? MCP success and detailed-error results preserve conservative IFC across
    payload exhaustion, retries, persistence, session restore, and hidden replay. There are no public API or breaking
    changes. Validation passed with:
    • cd python && uv run pytest -q packages/core/tests/core/test_mcp.py -k 'exhausted_aggregate_budget_preserves_ifc_security_label or exhausted_aggregate_budget_enforces_ifc_on_detailed_mcp_error or retried_mcp_error_does_not_commit_swallowed_label or mcp_error_finalizer_updates_invocation_session_scope or mcp_error_without_ifc_preserves_local_identity_principals or restriction_only_mcp_error_preserves_private_argument_fallback or oversized_identity_ifc_fails_closed_without_confidentiality_downgrade or invalid_identity_ifc_cannot_inherit_local_principals or hidden_builtin_mcp_result_replays_without_tainting_context' (10 passed)
    • cd python && uv run pytest -q packages/core/tests/test_security.py -k 'stamp_contents_invalid_meta_fails_closed or stamp_contents_unretained_ifc_fails_closed' (5 passed)
    • cd python && uv run pytest -q -n auto --dist worksteal packages/core/tests/core/test_mcp.py packages/core/tests/test_security.py (passed)
    • cd python && uv run poe syntax -P core -C (passed)
    • cd python && uv run poe pyright -P core (0 errors and 0 warnings)
    • cd python && uv run poe test-typing -P core (mypy, pyrefly, pyright, ty, and zuban passed)
    • cd python && uv run poe test -P core (9,174 passed, 20 skipped, 2 xfailed; 92% coverage)
  • What do you want reviewers to focus on? Please review the independent IFC retention boundary, fail-closed identity
    behavior, terminal-error finalizer timing, session-scope capture, and hidden-placeholder metadata cleanup. This layer
    intentionally excludes service-history reuse, AG-UI or general replay logic, and the deferred follow-up to
    automatically hide untrusted MCP detailed-error text.

Related Issue

Fixes #9652

#9652 is the original source draft. This PR is its MCP IFC enforcement replacement layer, stacked as
#9875 -> #9876 -> #9877 -> #9878 -> this PR rather than a competing implementation.

AI Assistance

  • No material AI assistance was used.
  • This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.

AI assistance details: GitHub Copilot assisted with implementation, regression tests, validation, and this PR
description.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.

@github-code-quality

github-code-quality Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Code coverage overview

Languages: Python

Python / code-coverage/python

fides-agui-replay-pr... 4d4e216 fides-mcp-ifc-enforc... 7f43e7d +/-
92% 92% 0%
View coverage of the 7 most impacted files.
File fides-agui-replay-pr... 4d4e216 fides-mcp-ifc-enforc... 7f43e7d +/-
packages/core/a...amework/_mcp.py 91% 91% 0%
packages/ag-ui/...i/_agent_run.py 90% 90% 0%
packages/core/a.../_middleware.py 96% 96% 0%
packages/core/a...ework/_tools.py 95% 95% 0%
packages/core/a...work/_agents.py 95% 95% 0%
packages/core/a...ork/security.py 90% 91% +1%
packages/ag-ui/...age_adapters.py 93% 94% +1%

ℹ️ Calculations represent line coverage. Results may change as calculations are updated.


Updated October 30, 2026 13:45 UTC

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (1 commit(s)): 39ebe60bfd7a
Model: gpt-5.6-sol

Overview

This PR adds an independently bounded private carrier for MCP IFC metadata and defers detailed-error label commits until terminal result finalization, with strong regression coverage for payload exhaustion, retries, session scope, invalid identity labels, and hidden replay. The carrier is invocation-local and its private metadata is removed from normal finalized results. One Medium regression remains: malformed IFC on a successful result can leave a non-serializable private authority marker in public result properties when label-tracking middleware is absent.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
1 verified finding remained after source verification (1 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/core/agent_framework/security.py

metadata={"source": "invalid_mcp_ifc"},
),
)
props[_AUTHORITATIVE_SECURITY_LABEL] = _INTERNAL_RESULT_MARKER

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a server supplies malformed or partial IFC, this object-valued marker remains in additional_properties unless LabelTrackingFunctionMiddleware consumes it. apply_mcp_security_labels() wraps tools independently of that middleware, so a default (trust_server_ifc=False) call can return this marker and json.dumps(Content.to_dict()) raises TypeError, allowing malformed remote metadata to break result or session serialization. Please keep the fail-closed label but ensure the private authority marker is consumed before the public result is returned, or represent authority without placing a non-serializable object in result properties.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Critical IFC enforcement gaps allow malformed identity labels and post-wrapper result parsing to bypass security restrictions, including exposing restricted error details.

3 open issues
What changed in this PR

This PR adds the final MCP enforcement layer of the FIDES hardening stack, preserving information-flow labels across payload limits, retries, and session persistence.

Changes:

  • Retains server IFC metadata independently of generic Host payload budgets and fails closed on invalid or unretainable metadata.
  • Labels terminal MCP errors in the captured session scope and prevents carrier metadata from returning to hidden placeholders.
  • Adds regressions for budget exhaustion, identity restrictions, retries, serialization, and hidden-result replay.
File Description
python/​packages/​core/​tests/​test_security.py Tests fail-closed handling of invalid and unretained IFC.
python/​packages/​core/​tests/​core/​test_mcp.py Adds MCP label enforcement, error, persistence, and replay regressions.
python/​packages/​core/​agent_framework/​security.py Enforces retained IFC and captures terminal-error labels and scope.
python/​packages/​core/​agent_framework/​_tools.py Adds private carrier state, error finalization, and hidden-placeholder metadata handling.
python/​packages/​core/​agent_framework/​_mcp.py Introduces independently bounded IFC retention and propagation.

Review effort: Balanced

Comment on lines +387 to +388
if self.security_meta is not None:
updated_item.additional_properties[_MCP_TOOL_RESULT_SECURITY_META_KEY] = self.security_meta
Comment on lines +5613 to +5614
dynamic = _label_from_mcp_meta(security_meta_mapping.get("meta"))
invalid_ifc = dynamic is None
Comment on lines +2174 to +2175
for key in carrier.exclusive_outer_keys | carrier.exclusive_item_keys:
updated_item.additional_properties.pop(key, None)

This branch was previously deployed

1 inactive deployment
github-app-auth — 7f43e7d5 Deployed Oct 30, 2026 by eavanvalkenburg via add_label #26682
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants