Python: Sample for using WorkIQ MCP server using a gateway for labelling and IFC policy evaluation - #6860
Conversation
…ecurity, and change context label only using the labels of unhidden result from tools
…nto pr/fides-mcp-autolabel-clean
…nto pr/fides-mcp-autolabel-clean
… instead of /insiders - Switch MCP_URL from /mcp/insiders to /mcp/ in github_mcp_example.py - Add MCP_HEADERS constant with X-MCP-Features: ifc_labels to opt-in to server-side IFC label emission in _meta payloads - Fix SecureMCPToolProxy to pass headers via httpx.AsyncClient so they are included on session.initialize(), not just on tool calls (was causing 401 to silently surface as anyio cancel-scope CancelledError) - Update README, FIDES_DEVELOPER_GUIDE, FIDES_IMPLEMENTATION_SUMMARY, and 0024-prompt-injection-defense.md to remove all /insiders references
…ntConfig quarantine client global behavior
…nto pr/fides-mcp-autolabel-clean
1. Can connect to workiq via gateway
2. Can read the top-level label specified under "$", {"ifc": {"$" : {...}}}
3. ConfidentialityLabel supports readers list. Context aggreation happens correctly on readers (only checked for basic-ifc).
TODO:
1. Add support for policy enforcement on the gateway.
2. Add / Confirm that FIDES works with the readers
…nto add/gateway-integration-for-workiq
There was a problem hiding this comment.
Pull request overview
This PR extends the Python FIDES security layer to support a “PRIVATE with readers” confidentiality lattice and adds gateway-driven policy enforcement for MCP tools (via a Fides Gateway eval_policy call). It also introduces a runnable WorkIQ email/teams sample and updates the security sample README and core security tests accordingly.
Changes:
- Add
ConfidentialityLabelreader restrictions (list/frozenset readers) and update label combine/serialization and policy checks accordingly. - Add optional gateway policy enforcement (
SecureMCPToolProxy(gateway_policy=True)) that attaches a per-tool_gateway_policy_fnand routes enforcement througheval_policy. - Add a WorkIQ gateway sample script plus README entry and update unit tests for the new confidentiality semantics.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| python/samples/02-agents/security/workiq-email-example.py | New runnable sample demonstrating WorkIQ MCP usage via a local Fides Gateway plus approval flow handling. |
| python/samples/02-agents/security/README.md | Documents the new WorkIQ gateway sample, setup steps, and expected behavior. |
| python/packages/core/tests/test_security.py | Updates unit tests to reflect the new “PRIVATE with readers” confidentiality model. |
| python/packages/core/agent_framework/security.py | Implements reader-aware confidentiality labels and gateway-driven policy enforcement hooks for MCP tools. |
| level. Useful for checking whether content may flow to a destination | ||
| that allows up to a given confidentiality level. | ||
| """ | ||
| return self._priority <= other._priority |
There was a problem hiding this comment.
Should reader-set containment be part of this comparison? The PR adds reader-restricted private labels and combines them by intersection, but this lets ConfidentialityLabel(["Alice"]) flow to ConfidentialityLabel(["Bob"]) because both have priority 1. Since max_allowed_confidentiality uses this path, reader-restricted data can be sent to the wrong private audience.
| """ | ||
| if conf.readers: | ||
| return sorted(conf.readers) | ||
| if conf.is_private: |
There was a problem hiding this comment.
Should the gateway payload preserve PRIVATE separately from PUBLIC here? _conf_wire() documents [] as public/no reader restriction, but it also serializes ConfidentialityLabel.PRIVATE with no readers as []. With gateway_policy=True, an allow returns before built-in confidentiality checks run, so legacy private context can be evaluated remotely as public. Could we encode the private level explicitly or fall back to local enforcement when the label cannot be represented faithfully on the gateway wire?
| try: | ||
| return cast(dict[str, Any], json.loads(text)) | ||
| except (json.JSONDecodeError, TypeError): | ||
| return {"decision": "allow", "message": str(text)} |
There was a problem hiding this comment.
Should parse failures from eval_policy become error instead of allow? In gateway mode an allow skips _fallback_to_builtin_policy, so non-JSON text, a schema mismatch, or an empty/unstructured result lets the protected tool call execute even though the policy decision was not actually evaluated. Could these parse/no-response branches return decision: "error" or request approval so the fallback path handles them?
|
Please re-open when ready to fix the conflict and address the remaining feedback. Thank you. |
Motivation & Context
FIDES enforces information-flow-control policy locally today. This change lets a FIDES-secured agent delegate policy decisions to an external FIDES Gateway fronting its MCP servers, while still tracking labels and surfacing approvals locally. It also adds a runnable WorkIQ email/teams sample as a reference.
Description & Review Guide
SecureMCPToolProxy(gateway_policy=True) delegates per-call policy checks to the gateway's eval_policy (allow/deny/ask).
ConfidentialityLabel gains a readers lattice [PRIVATE] with a readers frozenset, intersected on combine via ConfidentialityLabel.combine, and parses gateway _meta IFC labels including the top-level "$" scope.
New [workiq-email-example.py] sample ([--cli] using ToolApprovalMiddleware, plus README and tests.