ae67bff5a3
Self-hosted platform for building, testing, and shipping LangChain/LangGraph agents. Deep-agent sub-agents on the canvas, a live tracing/observability timeline, auto-provisioned built-in tools with import/export, per-environment tool variables, streamed evaluations, and per-user auth token forwarding.
64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
"""MCP tool-kind wiring tests (monkeypatched server - no live MCP connection)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
from langchain_core.tools import StructuredTool
|
|
|
|
from forge.db.base import SessionLocal
|
|
from forge.models import McpClient
|
|
from forge.services.runtime import make_runtime_ctx
|
|
from forge.tools import mcp as mcp_mod
|
|
from forge.tools.mcp import McpUnavailable, load_mcp_tool
|
|
|
|
|
|
def _fake_remote_tool(name="search"):
|
|
async def _run(q: str) -> str:
|
|
return f"results for {q}"
|
|
return StructuredTool.from_function(coroutine=_run, name=name, description="remote search")
|
|
|
|
|
|
async def _make_client(tenant="t_mcp", project="p_mcp") -> str:
|
|
async with SessionLocal() as s:
|
|
row = McpClient(tenant_id=tenant, project_id=project, name="demo", transport="streamable_http", url="https://mcp.example/sse")
|
|
s.add(row)
|
|
await s.commit()
|
|
await s.refresh(row)
|
|
return row.id
|
|
|
|
|
|
async def test_adapters_import_available():
|
|
# The optional extra is installed in this env; the loader resolves the client class.
|
|
assert mcp_mod._require_adapters() is not None
|
|
|
|
|
|
async def test_load_mcp_tool_finds_remote_tool(monkeypatch):
|
|
cid = await _make_client()
|
|
|
|
async def fake_client_and_tools(row, tenant_id, project_id):
|
|
return object(), [_fake_remote_tool("search")]
|
|
|
|
monkeypatch.setattr(mcp_mod, "_client_and_tools", fake_client_and_tools)
|
|
ctx = make_runtime_ctx("t_mcp", "p_mcp")
|
|
tool = await load_mcp_tool({"mcp_client_id": cid, "remote_tool_name": "search"}, ctx)
|
|
assert tool.name == "search"
|
|
assert await tool.ainvoke({"q": "hello"}) == "results for hello"
|
|
|
|
|
|
async def test_load_mcp_tool_unknown_remote(monkeypatch):
|
|
cid = await _make_client()
|
|
monkeypatch.setattr(mcp_mod, "_client_and_tools", lambda *a, **k: _noop([]))
|
|
ctx = make_runtime_ctx("t_mcp", "p_mcp")
|
|
with pytest.raises(McpUnavailable):
|
|
await load_mcp_tool({"mcp_client_id": cid, "remote_tool_name": "nope"}, ctx)
|
|
|
|
|
|
async def test_load_mcp_tool_missing_client():
|
|
ctx = make_runtime_ctx("t_mcp", "p_mcp")
|
|
with pytest.raises(McpUnavailable):
|
|
await load_mcp_tool({"mcp_client_id": "does-not-exist", "remote_tool_name": "x"}, ctx)
|
|
|
|
|
|
async def _noop(tools):
|
|
return object(), tools
|