Skip to content

Commit 0657e05

Browse files
committed
feat(viz): draw MCP servers
1 parent 237d672 commit 0657e05

File tree

3 files changed

+58
-0
lines changed

3 files changed

+58
-0
lines changed

docs/visualization.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@ pip install "openai-agents[viz]"
1515
You can generate an agent visualization using the `draw_graph` function. This function creates a directed graph where:
1616

1717
- **Agents** are represented as yellow boxes.
18+
- **MCP Servers** are represented as grey boxes.
1819
- **Tools** are represented as green ellipses.
1920
- **Handoffs** are directed edges from one agent to another.
2021

2122
### Example Usage
2223

2324
```python
25+
import os
26+
2427
from agents import Agent, function_tool
28+
from agents.mcp.server import MCPServerStdio
2529
from agents.extensions.visualization import draw_graph
2630

2731
@function_tool
@@ -38,11 +42,22 @@ english_agent = Agent(
3842
instructions="You only speak English",
3943
)
4044

45+
current_dir = os.path.dirname(os.path.abspath(__file__))
46+
samples_dir = os.path.join(current_dir, "sample_files")
47+
mcp_server = MCPServerStdio(
48+
name="Filesystem Server, via npx",
49+
params={
50+
"command": "npx",
51+
"args": ["-y", "@modelcontextprotocol/server-filesystem", samples_dir],
52+
},
53+
)
54+
4155
triage_agent = Agent(
4256
name="Triage agent",
4357
instructions="Handoff to the appropriate agent based on the language of the request.",
4458
handoffs=[spanish_agent, english_agent],
4559
tools=[get_weather],
60+
mcp_servers=[mcp_server],
4661
)
4762

4863
draw_graph(triage_agent)
@@ -60,9 +75,11 @@ The generated graph includes:
6075
- A **start node** (`__start__`) indicating the entry point.
6176
- Agents represented as **rectangles** with yellow fill.
6277
- Tools represented as **ellipses** with green fill.
78+
- MCP Servers represented as **rectangles** with grey fill.
6379
- Directed edges indicating interactions:
6480
- **Solid arrows** for agent-to-agent handoffs.
6581
- **Dotted arrows** for tool invocations.
82+
- **Dashed arrows** for MCP server invocations.
6683
- An **end node** (`__end__`) indicating where execution terminates.
6784

6885
## Customizing the Graph

src/agents/extensions/visualization.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,12 @@ def get_all_nodes(
7171
f"fillcolor=lightgreen, width=0.5, height=0.3];"
7272
)
7373

74+
for mcp_server in agent.mcp_servers:
75+
parts.append(
76+
f'"{mcp_server.name}" [label="{mcp_server.name}", shape=box, style=filled, '
77+
f"fillcolor=lightgrey, width=1, height=0.5];"
78+
)
79+
7480
for handoff in agent.handoffs:
7581
if isinstance(handoff, Handoff):
7682
parts.append(
@@ -119,6 +125,11 @@ def get_all_edges(
119125
"{agent.name}" -> "{tool.name}" [style=dotted, penwidth=1.5];
120126
"{tool.name}" -> "{agent.name}" [style=dotted, penwidth=1.5];""")
121127

128+
for mcp_server in agent.mcp_servers:
129+
parts.append(f"""
130+
"{agent.name}" -> "{mcp_server.name}" [style=dashed, penwidth=1.5];
131+
"{mcp_server.name}" -> "{agent.name}" [style=dashed, penwidth=1.5];""")
132+
122133
for handoff in agent.handoffs:
123134
if isinstance(handoff, Handoff):
124135
parts.append(f"""

tests/test_visualization.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
from unittest.mock import Mock
23

34
import graphviz # type: ignore
@@ -12,6 +13,9 @@
1213
)
1314
from agents.handoffs import Handoff
1415

16+
if sys.version_info >= (3, 10):
17+
from .mcp.helpers import FakeMCPServer
18+
1519

1620
@pytest.fixture
1721
def mock_agent():
@@ -27,6 +31,10 @@ def mock_agent():
2731
agent.name = "Agent1"
2832
agent.tools = [tool1, tool2]
2933
agent.handoffs = [handoff1]
34+
agent.mcp_servers = []
35+
36+
if sys.version_info >= (3, 10):
37+
agent.mcp_servers = [FakeMCPServer(server_name="MCPServer1")]
3038

3139
return agent
3240

@@ -62,6 +70,7 @@ def test_get_main_graph(mock_agent):
6270
'"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, '
6371
"fillcolor=lightyellow, width=1.5, height=0.8];" in result
6472
)
73+
_assert_mcp_nodes(result)
6574

6675

6776
def test_get_all_nodes(mock_agent):
@@ -90,6 +99,7 @@ def test_get_all_nodes(mock_agent):
9099
'"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, '
91100
"fillcolor=lightyellow, width=1.5, height=0.8];" in result
92101
)
102+
_assert_mcp_nodes(result)
93103

94104

95105
def test_get_all_edges(mock_agent):
@@ -101,6 +111,7 @@ def test_get_all_edges(mock_agent):
101111
assert '"Agent1" -> "Tool2" [style=dotted, penwidth=1.5];' in result
102112
assert '"Tool2" -> "Agent1" [style=dotted, penwidth=1.5];' in result
103113
assert '"Agent1" -> "Handoff1";' in result
114+
_assert_mcp_edges(result)
104115

105116

106117
def test_draw_graph(mock_agent):
@@ -134,6 +145,25 @@ def test_draw_graph(mock_agent):
134145
'"Handoff1" [label="Handoff1", shape=box, style=filled, style=rounded, '
135146
"fillcolor=lightyellow, width=1.5, height=0.8];" in graph.source
136147
)
148+
_assert_mcp_nodes(graph.source)
149+
150+
151+
def _assert_mcp_nodes(source: str):
152+
if sys.version_info < (3, 10):
153+
assert "MCPServer1" not in source
154+
return
155+
assert (
156+
'"MCPServer1" [label="MCPServer1", shape=box, style=filled, '
157+
"fillcolor=lightgrey, width=1, height=0.5];" in source
158+
)
159+
160+
161+
def _assert_mcp_edges(source: str):
162+
if sys.version_info < (3, 10):
163+
assert "MCPServer1" not in source
164+
return
165+
assert '"Agent1" -> "MCPServer1" [style=dashed, penwidth=1.5];' in source
166+
assert '"MCPServer1" -> "Agent1" [style=dashed, penwidth=1.5];' in source
137167

138168

139169
def test_cycle_detection():

0 commit comments

Comments
 (0)