Files
microsoft--agent-framework/python/samples/02-agents/security/github_mcp_example.py
T
shrutitople 730bcee9ea Python: Autolabelling MCP servers based on hints and Github MCP server ifc labels (#6171)
* Python: add GitHub MCP security label sample

* modified samples to create devui auth token, support debugging with security, and change context label only using the labels of unhidden result from tools

* FIDES: secure MCP labeling, _meta IFC parsing, and docs updates

* FIDES: secure MCP labeling, _meta IFC parsing, and docs updates

* modified docs

* fixed PR comments, simplified github_mcp example

* commented  github_mcp example

* remove the parse_github_mcp_labels and fix the user_identity label propogation

* fix: use standard GitHub MCP endpoint with X-MCP-Features: ifc_labels 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

* address PR comments

* Simplify GitHub MCP security sample to DevUI-only; document SecureAgentConfig quarantine client global behavior

* minor PR comments

* fixing failed checks

* fixing failed checks

---------

Co-authored-by: Eduard van Valkenburg <eavanvalkenburg@users.noreply.github.com>
2026-06-29 08:34:00 +00:00

115 lines
4.6 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
"""GitHub MCP URL + FIDES Example (direct URL connection with local policy enforcement).
This sample connects an agent directly to the remote GitHub MCP URL
(`https://api.githubcopilot.com/mcp/`) while enforcing IFC/FIDES security
locally in your process.
The key idea: wrap the remote MCP URL with `SecureMCPToolProxy(...)` so the MCP
tools run locally. This lets the security middleware inspect tool results, apply
IFC labels, hide untrusted content, and enforce policies before the agent uses
any tool data. The agent is then served through DevUI for interactive use.
Prerequisites (environment variables):
- GITHUB_PAT: GitHub Personal Access Token (required)
- FOUNDRY_PROJECT_ENDPOINT: Foundry project endpoint (required)
- FOUNDRY_MODEL: Foundry model deployment name (optional, defaults to o4-mini)
Run:
uv run samples/02-agents/security/github_mcp_example.py
"""
from __future__ import annotations
import asyncio
import os
import secrets
from contextlib import AsyncExitStack, suppress
from pathlib import Path
import uvicorn
from agent_framework import Agent
from agent_framework.foundry import FoundryChatClient
from agent_framework.security import SecureAgentConfig, SecureMCPToolProxy
from agent_framework_devui._server import DevServer
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
MCP_URL = "https://api.githubcopilot.com/mcp/"
MCP_HEADERS = {"X-MCP-Features": "ifc_labels"} # Opt-in to server-side IFC label emission in _meta
_AGENT_INSTRUCTIONS = (
"You are a helpful GitHub assistant. Use tools to answer accurately. "
"Never fabricate repository data, pull requests, users, or timestamps. "
"If tool data is unavailable, explicitly say retrieval failed. "
"When operations might modify data, explain what action you intend to take."
)
async def main() -> None:
"""Connect to the GitHub MCP URL with FIDES security and serve the agent via DevUI."""
load_dotenv(Path(__file__).parent / ".env")
load_dotenv()
github_pat = os.getenv("GITHUB_PAT")
if not github_pat:
raise RuntimeError("GITHUB_PAT environment variable is required.")
foundry_model = os.getenv("FOUNDRY_MODEL", "o4-mini")
credential = AzureCliCredential()
main_client = FoundryChatClient(model=foundry_model, credential=credential)
quarantine_client = FoundryChatClient(model="gpt-4o-mini", credential=credential)
async with AsyncExitStack() as stack:
# Wrap the remote MCP URL as local tools so FIDES can label inputs/outputs
# and enforce policy checks before tool data is used by the agent.
secure_github = await stack.enter_async_context(
SecureMCPToolProxy(
url=MCP_URL,
headers={"Authorization": f"Bearer {github_pat}", **MCP_HEADERS},
name="GitHub",
description="GitHub MCP server over Streamable HTTP",
)
)
print(f"Connected to MCP URL: {MCP_URL} ({len(secure_github.tools)} tools loaded)")
# SecureAgentConfig is a context provider that applies the IFC/FIDES policy:
# hide untrusted content, enforce policies, and require approval on violations.
config = SecureAgentConfig(
auto_hide_untrusted=True,
enable_policy_enforcement=True,
approval_on_violation=True,
quarantine_chat_client=quarantine_client,
)
agent = await stack.enter_async_context(
Agent(
client=main_client,
name="GitHubSecureMcpUrlAgent",
instructions=_AGENT_INSTRUCTIONS,
tools=secure_github.tools,
context_providers=[config],
)
)
# DevUI's serve(...) is synchronous and would spin up its own event loop,
# orphaning the live MCP session. Drive DevServer via uvicorn directly so
# it shares this loop with the MCP connection.
host, port = "127.0.0.1", 8090
auth_token = os.getenv("DEVUI_AUTH_TOKEN") or secrets.token_urlsafe(32)
server = DevServer(port=port, host=host, auth_enabled=True, auth_token=auth_token)
server.set_pending_entities([agent])
print(f"\nDevUI: http://{host}:{port} (entity: agent_{agent.name})")
print(f"Bearer token: {auth_token}")
print("Press Ctrl+C to stop.\n")
uvicorn_server = uvicorn.Server(uvicorn.Config(server.get_app(), host=host, port=port, log_level="info"))
await uvicorn_server.serve()
if __name__ == "__main__":
with suppress(KeyboardInterrupt):
asyncio.run(main())