feat: add MCP tool integration via MCPO (#11)

This commit is contained in:
Daniel
2026-07-26 14:11:07 +10:00
committed by GitHub
parent 2ceba471d0
commit 4099d4aa82
11 changed files with 420 additions and 27 deletions
+63
View File
@@ -14,6 +14,7 @@ from steward.config import Settings
from steward.llm.client import LLMClient
from steward.tools.client import (
ToolClient,
_discover_mcpo_server_names,
_operation_id,
_resolve_operation,
_schema_to_json_schema,
@@ -25,6 +26,22 @@ from tests.conftest import make_settings
# Fixtures
# ---------------------------------------------------------------------------
MCPO_ROOT_SPEC: dict[str, Any] = {
"openapi": "3.1.0",
"info": {
"title": "MCP OpenAPI Proxy",
"version": "1.0",
"description": (
"Automatically generated API from MCP Tool Schemas\n\n"
"- **available tools**:\n"
" - [github](/github/docs)\n"
" - [memory](/memory/docs)\n"
),
},
"paths": {},
}
SIMPLE_SPEC: dict[str, Any] = {
"openapi": "3.0.0",
"info": {"title": "Test", "version": "1.0.0"},
@@ -248,6 +265,10 @@ def test_resolve_operation_not_found():
# ---------------------------------------------------------------------------
def test_discover_mcpo_server_names():
assert _discover_mcpo_server_names(MCPO_ROOT_SPEC) == ["github", "memory"]
@pytest.mark.asyncio
async def test_tool_client_get_spec():
"""get_spec() fetches from /openapi.json and caches the result."""
@@ -277,6 +298,48 @@ async def test_tool_client_get_tools():
assert tools2 is tools
@pytest.mark.asyncio
async def test_tool_client_get_tools_from_mcpo_subservers():
with respx.mock:
respx.get("http://tools.local/openapi.json").mock(
return_value=Response(200, json=MCPO_ROOT_SPEC)
)
respx.get("http://tools.local/github/openapi.json").mock(
return_value=Response(200, json=SIMPLE_SPEC)
)
respx.get("http://tools.local/memory/openapi.json").mock(
return_value=Response(200, json=SIMPLE_SPEC)
)
client = ToolClient("http://tools.local")
tools = await client.get_tools()
tool_names = {tool["function"]["name"] for tool in tools}
assert "github__list_items" in tool_names
assert "memory__list_items" in tool_names
assert len(tools) == 6
@pytest.mark.asyncio
async def test_tool_client_call_mcpo_namespaced_tool():
with respx.mock:
respx.get("http://tools.local/openapi.json").mock(
return_value=Response(200, json=MCPO_ROOT_SPEC)
)
respx.get("http://tools.local/github/openapi.json").mock(
return_value=Response(200, json=SIMPLE_SPEC)
)
respx.get("http://tools.local/memory/openapi.json").mock(
return_value=Response(200, json=SIMPLE_SPEC)
)
respx.get("http://tools.local/github/items").mock(
return_value=Response(200, json=[{"id": 1}])
)
client = ToolClient("http://tools.local")
result = await client.call("github__list_items", {"limit": 10})
assert "id" in result
@pytest.mark.asyncio
async def test_tool_client_call_get_with_query():
with respx.mock: