microsoft / microsoft/agent-framework

Python: [Bug]: Support MCP tool binding by reference in declarative YAML

Open
#4,927 2 comments 2 reactions 1 assignee Assigned to @peibekwe View on GitHub
python workflows
Dominant language
Python
Stars
13.6k
Forks
2.3k
Avg merge
2d 45m
Merged PRs (30d)
358

Description

### Description

MAF declarative workflows require embedding full MCP connection details directly in the YAML tool definition. Two problems make this unworkable:

1. **Custom HTTP headers are not supported in the YAML schema** - only `url` is available. (Tracked in #4582)
2. **There is no way to reference an MCP connection by identifier** - connection details must be redeclared in every agent and workflow that uses the same MCP server, coupling sensitive infrastructure config to business workflow files.

The expected model is that MCP connections are declared once in configuration or code, and workflow YAML references them by name, declaring only which tools each agent is allowed to use.

## Current Behaviour

Passing a pre-built MCP tool via `bindings` is ignored. MAF constructs the tool directly from the YAML spec regardless. Results in `server_url: ''` being sent to the API, returning a 500.

> Note: `McpTool` does accept a `bindings` field (inherited from `Tool`) but `_parse_tool` in `AgentFactory` never reads it for the MCP case, unlike `FunctionTool` where bindings correctly resolve the callable.

The only working workaround is passing the tool at `agent.run()` with no `tools` block in the YAML, **but this loses per-agent tool scoping entirely.**

### Code Sample

```markdown
# Minimal agent YAML — no url, no headers, no connection details.
# Just the name and the allowed tools.
AGENT_YAML = """
kind: Prompt
name: TestAgent
instructions: You are a helpful assistant.
model:
id: gpt-4.1
provider: AzureOpenAI.Responses
tools:
- kind: mcp
name: product-mcp
allowedTools:
- customer_search
# connection resolved from bindings by name at runtime
"""

async def main():
mcp_tool = OpenAIResponsesClient.get_mcp_tool(
name="product-mcp",
description="Product MCP server",
url="http://example.com",
headers={"X-API-Key": "..."},
approval_mode="never_require",
allowed_tools=["customer_search"],
)

factory = AgentFactory(
safe_mode=False,
bindings={"product-mcp": mcp_tool},
)

async with factory.create_agent_from_yaml(AGENT_YAML) as agent:
response = await agent.run("Search for customer Inforshow")
print(f"Response: {response.text}")
```

### Error Messages / Stack Traces

```markdown

```

### Package Versions

agent-framework: 1.0.0rc4, agent-framework-declarative: 1.0.0b260311

### Python Version

3.13

### Additional Context

From our understanding, Azure Foundry solves this cleanly via `project_connection_id`: MCP connections are registered centrally in the project (including headers and secrets) and referenced by name in the YAML:

```yaml
tools:
- type: mcp
server_label: ERP_test_MCP
server_url: http://example.com/
require_approval:
never:
tool_names:
- customer_search
project_connection_id: ERP-test-MCP
```

Could a similar behavior be achievable through MAF, with centralized code definitions?

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.