Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/github-mcp/git_mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
""",
},
],
tools=tool_node,
tool_node=tool_node,
trim_context=True,
)

Expand Down
2 changes: 1 addition & 1 deletion examples/github-mcp/mcp_file_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
""",
},
],
tools=tool_node,
tool_node=tool_node,
trim_context=True,
)

Expand Down
2 changes: 1 addition & 1 deletion examples/react-mcp/react-mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
""",
},
],
tools=tool_node,
tool_node=tool_node,
trim_context=True,
)

Expand Down
18 changes: 18 additions & 0 deletions examples/xquik-mcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Xquik MCP Example

This example connects Agentflow's `ToolNode` to the remote Xquik MCP server and
loads its tool schemas without changing any framework defaults.

Install Agentflow's MCP extra, provide an API key through the environment, and
run the example:

```bash
uv sync --extra mcp
export XQUIK_API_KEY="your-api-key"
uv run python examples/xquik-mcp/client.py
```

The script prints the available tool names. Use the same `ToolNode` as an
agent's `tool_node` when building a graph. See the
[Xquik MCP overview](https://docs.xquik.com/mcp/overview) for authentication,
tool behavior, and safety requirements.
46 changes: 46 additions & 0 deletions examples/xquik-mcp/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""Discover Xquik tools through Agentflow's MCP adapter."""

import asyncio
import os

from fastmcp import Client

from agentflow.core import ToolNode


MCP_URL = "https://xquik.com/mcp"


def require_api_key() -> str:
"""Read the Xquik API key without embedding it in source."""
api_key = os.getenv("XQUIK_API_KEY")
if not api_key:
raise RuntimeError("Set XQUIK_API_KEY before running this example")
return api_key


def build_client(api_key: str) -> Client:
"""Build a Streamable HTTP client for the remote Xquik MCP server."""
return Client(
{
"mcpServers": {
"xquik": {
"url": MCP_URL,
"transport": "streamable-http",
"headers": {"Authorization": f"Bearer {api_key}"},
}
}
}
)


async def main() -> None:
"""Load the remote tool schemas through Agentflow."""
tool_node = ToolNode([], client=build_client(require_api_key()))
tools = await tool_node.all_tools()
names = sorted(tool["function"]["name"] for tool in tools)
print(f"Available Xquik tools: {', '.join(names)}")


if __name__ == "__main__":
asyncio.run(main())
31 changes: 31 additions & 0 deletions tests/examples/test_mcp_examples.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""Static checks for MCP examples that should not require live services."""

import ast
from pathlib import Path


MCP_AGENT_EXAMPLES = (
"examples/github-mcp/git_mcp.py",
"examples/github-mcp/mcp_file_download.py",
"examples/react-mcp/react-mcp.py",
)
REPO_ROOT = Path(__file__).parents[2]


def test_mcp_examples_use_current_agent_tool_keyword() -> None:
"""Keep MCP examples aligned with Agent's current public constructor."""
for relative_path in MCP_AGENT_EXAMPLES:
tree = ast.parse((REPO_ROOT / relative_path).read_text(encoding="utf-8"))
agent_calls = [
node
for node in ast.walk(tree)
if isinstance(node, ast.Call)
and isinstance(node.func, ast.Name)
and node.func.id == "Agent"
]

assert agent_calls, f"No Agent call found in {relative_path}"
for call in agent_calls:
keywords = {keyword.arg for keyword in call.keywords}
assert "tools" not in keywords, relative_path
assert "tool_node" in keywords, relative_path