MCP & A2A Integration Guide
OpenFang implements both the Model Context Protocol (MCP) and Agent-to-Agent (A2A) protocol, enabling deep interoperability with external tools, IDEs, and other agent frameworks.
Table of Contents
Part 1: MCP (Model Context Protocol)
MCP Overview
The Model Context Protocol (MCP) is a JSON-RPC 2.0 based protocol that standardizes how LLM applications discover and invoke tools. OpenFang supports MCP in both directions:
- As a client: OpenFang connects to external MCP servers (GitHub, filesystem, databases, Puppeteer, etc.) and makes their tools available to all agents.
- As a server: OpenFang exposes its own agents as MCP tools, so IDEs like Cursor, VS Code, and Claude Desktop can call OpenFang agents directly.
OpenFang implements MCP protocol version 2024-11-05.
Source files:
- Client:
crates/openfang-runtime/src/mcp.rs - Server handler:
crates/openfang-runtime/src/mcp_server.rs - CLI server:
crates/openfang-cli/src/mcp.rs - Config types:
crates/openfang-types/src/config.rs(McpServerConfigEntry,McpTransportEntry)
MCP Client
The MCP client (McpConnection in openfang-runtime) allows OpenFang to connect to any MCP-compatible server and use its tools as if they were built-in.
Configuration
MCP servers are configured in config.toml using the [[mcp_servers]] array:
[[mcp_servers]]
name = "github"
timeout_secs = 30
env = ["GITHUB_PERSONAL_ACCESS_TOKEN"]
[mcp_servers.transport]
type = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
Advanced MCP Server Configuration
Each entry maps to a McpServerConfigEntry struct:
| Field | Type | Default | Description |
|---|---|---|---|
name |
String |
required | Display name, used in tool namespacing |
transport |
McpTransportEntry |
required | How to connect (stdio or SSE) |
timeout_secs |
u64 |
30 |
JSON-RPC request timeout |
env |
Vec<String> |
[] |
Env vars to pass through to the subprocess |
Transport Types
OpenFang supports two MCP transports, defined by McpTransport:
Stdio -- Spawns a subprocess and communicates via stdin/stdout with newline-delimited JSON-RPC:
[mcp_servers.transport]
type = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]
SSE -- Connects to a remote HTTP endpoint and sends JSON-RPC via POST:
[mcp_servers.transport]
type = "sse"
url = "https://mcp.example.com/api"
Tool Connectivity Lifecycle
The McpConnection struct manages the lifetime of the connection:
pub struct McpConnection {
config: McpServerConfig,
tools: Vec<ToolDefinition>,
transport: McpTransportHandle, // Stdio or SSE
next_id: u64, // JSON-RPC request counter
}
When the connection is dropped, stdio subprocesses are automatically killed via Drop:
impl Drop for McpConnection {
fn drop(&mut self) {
if let McpTransportHandle::Stdio { ref mut child, .. } = self.transport {
let _ = child.start_kill();
}
}
}
MCP Server
OpenFang can also act as an MCP server, exposing its agents as callable tools to external MCP clients.
How It Works
Each OpenFang agent becomes an MCP tool named openfang_agent_{name} (with hyphens replaced by underscores). The tool accepts a single message string parameter and returns the agent's response.
For example, an agent named code-reviewer becomes the MCP tool openfang_agent_code_reviewer.
Supported JSON-RPC Methods
| Method | Description |
|---|---|
initialize |
Handshake; returns server capabilities and info |
notifications/initialized |
Client confirmation; no response |
tools/list |
Returns all available tools with names, descriptions, and input schemas |
tools/call |
Executes a tool and returns the result |
Task Lifecycle
An A2aTask tracks the full lifecycle of a cross-agent interaction.
| Status | Description |
|---|---|
Submitted |
Task received but not yet started |
Working |
Task is being actively processed by the agent |
Completed |
Task finished successfully |
Cancelled |
Task was cancelled by the caller |
Failed |
Task encountered an error |
Part 2: A2A (Agent-to-Agent Protocol)
A2A Overview
The Agent-to-Agent (A2A) protocol enables cross-framework agent interoperability, allowing agents built with different frameworks to discover each other's capabilities and exchange tasks.
OpenFang implements A2A in both directions:
- As a server: Publishes Agent Cards describing each agent's capabilities, accepts task submissions, and tracks task lifecycle.
- As a client: Discovers external A2A agents at boot time, sends tasks to them, and polls for results.
Agent Card
An Agent Card is a JSON document that describes an agent's identity, capabilities, and supported interaction modes. It is served at the well-known path /.well-known/agent.json per the A2A specification.
Example card:
{
"name": "code-reviewer",
"description": "Reviews code for bugs, security issues, and style",
"url": "http://127.0.0.1:50051/a2a",
"version": "0.1.0",
"capabilities": {
"streaming": true,
"pushNotifications": false,
"stateTransitionHistory": true
},
"skills": [
{
"id": "file_read",
"name": "file read",
"description": "Can use the file_read tool",
"tags": ["tool"],
"examples": []
}
],
"defaultInputModes": ["text"],
"defaultOutputModes": ["text"]
}
A2A API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
GET |
/.well-known/agent.json |
Public | Agent Card for the primary agent |
POST |
/a2a/tasks/send |
Public | Submit a task to an agent |
A2A Configuration
A2A is configured in config.toml under the [a2a] section:
[a2a]
enabled = true
listen_path = "/a2a"
[[a2a.external_agents]]
name = "research-agent"
url = "https://research.example.com"
Security
MCP Security
- Subprocess Sandboxing: Stdio MCP servers run with
env_clear()to prevent leaking secrets to untrusted MCP server processes. - Request Timeout: All MCP requests have a configurable timeout (default 30 seconds) to prevent hung connections.
A2A Security
- Rate Limiting: A2A endpoints go through the same GCRA rate limiter as all other API endpoints.
- Task Store Bounds: The
A2aTaskStoreis bounded with FIFO eviction of completed/failed/cancelled tasks, preventing memory exhaustion from task accumulation.