Skip to content

Python: Sample for using WorkIQ MCP server using a gateway for labelling and IFC policy evaluation - #6860

Closed
shrutitople wants to merge 30 commits into
microsoft:mainfrom
shrutitople:add/gateway-integration-for-workiq
Closed

Python: Sample for using WorkIQ MCP server using a gateway for labelling and IFC policy evaluation #6860
shrutitople wants to merge 30 commits into
microsoft:mainfrom
shrutitople:add/gateway-integration-for-workiq

Conversation

@shrutitople

Copy link
Copy Markdown
Contributor

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.

shrutitople and others added 27 commits May 22, 2026 13:48
…ecurity, and change context label only using the labels of unhidden result from tools
… 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
    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
Copilot AI review requested due to automatic review settings July 1, 2026 14:17
@giles17 Giles Odigwe (giles17) added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python labels Jul 1, 2026
@github-actions github-actions Bot changed the title Sample for using WorkIQ MCP server using a gateway for labelling and IFC policy evaluation Python: Sample for using WorkIQ MCP server using a gateway for labelling and IFC policy evaluation Jul 1, 2026
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/core/agent_framework
   security.py110132370%178, 182, 202–205, 254, 584–585, 603, 606–607, 685–686, 688, 736, 748, 764, 781, 958, 974–977, 1035, 1039, 1042–1044, 1052–1056, 1061–1064, 1066, 1081–1085, 1090–1094, 1098–1101, 1103, 1115–1116, 1119–1120, 1196, 1202–1203, 1208, 1210–1211, 1241–1242, 1270–1278, 1301–1304, 1414–1415, 1446–1447, 1466–1467, 1471–1472, 1517–1518, 1605–1606, 1835, 1839, 1843, 1946, 1951–1952, 1956, 1960–1962, 1973–1974, 2018, 2030, 2032, 2049, 2070, 2081, 2107, 2142–2143, 2171, 2174–2177, 2179, 2182, 2184–2185, 2187–2188, 2190, 2192, 2194, 2198–2199, 2201–2204, 2207, 2215, 2217–2219, 2226–2227, 2229, 2239, 2242–2243, 2249–2250, 2252, 2272–2273, 2276–2279, 2286, 2288–2289, 2296–2297, 2310–2312, 2317, 2320–2322, 2330, 2332–2333, 2340–2341, 2354–2356, 2362, 2364, 2387, 2497, 2525–2527, 2562–2564, 2572, 2580, 2852–2853, 2855, 2857–2859, 2862, 2874, 2880–2881, 2883, 2914, 2918–2921, 2923, 3073–3074, 3087, 3110, 3177, 3187–3188, 3349–3350, 3354–3356, 3361, 3363–3371, 3376–3377, 3379–3383, 3385–3387, 3390–3392, 3394–3395, 3400, 3403–3404, 3408, 3410, 3446–3448, 3484, 3512–3513, 3516–3517, 3551, 3557–3559, 3561, 3563, 3565, 3572, 3577, 3582, 3595, 3600–3601, 3606–3607, 3609–3623, 3625, 3627, 3729–3732, 3734–3735, 3737, 3739, 3745, 3754, 3763–3767, 3773–3775, 3779, 3785–3786, 3790, 3799–3801, 3808, 3813, 3818, 3823, 3828, 3833, 3845–3846, 3853–3855, 3857–3860
TOTAL43544530187% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
8539 33 💤 0 ❌ 0 🔥 1m 55s ⏱️

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.

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 ConfidentialityLabel reader 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_fn and routes enforcement through eval_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.

Comment thread python/packages/core/agent_framework/security.py
Comment thread python/packages/core/agent_framework/security.py
Comment thread python/packages/core/agent_framework/security.py
Comment thread python/packages/core/agent_framework/security.py
Comment thread python/samples/02-agents/security/workiq-email-example.py Outdated
Comment thread python/packages/core/agent_framework/security.py Outdated
Comment thread python/packages/core/agent_framework/security.py Outdated
Comment thread python/packages/core/agent_framework/security.py Outdated
Comment thread python/samples/02-agents/security/workiq-email-example.py Outdated
Comment thread python/samples/02-agents/security/workiq-email-example.py Outdated
Comment thread python/samples/02-agents/security/workiq-email-example.py Outdated
Comment thread python/samples/02-agents/security/workiq-email-example.py Outdated
Comment thread python/packages/core/agent_framework/security.py
level. Useful for checking whether content may flow to a destination
that allows up to a given confidentiality level.
"""
return self._priority <= other._priority

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.

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:

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.

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)}

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.

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?

@moonbox3

Copy link
Copy Markdown
Contributor

Please re-open when ready to fix the conflict and address the remaining feedback. Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants