Connect a DSPy agent to Civic’s MCP Hub using streamable HTTP transport
Connect a DSPy agent to Civic by wrapping MCP tools as Python callables and passing them to dspy.ReAct. DSPy v2.6 does not include a built-in MCP client, so tools are discovered via the raw mcp package.
Discover tools via the raw mcp client and wrap each one as a Python callable for DSPy:
Copy
import osimport asynciofrom dotenv import load_dotenvimport dspyfrom mcp.client.streamable_http import streamablehttp_clientfrom mcp import ClientSessionload_dotenv()CIVIC_URL = os.environ["CIVIC_URL"]CIVIC_TOKEN = os.environ["CIVIC_TOKEN"]async def call_tool(name: str, args: dict) -> str: """Call a single MCP tool and return the result as a string.""" headers = {"Authorization": f"Bearer {CIVIC_TOKEN}"} async with streamablehttp_client(CIVIC_URL, headers=headers) as (r, w, _): async with ClientSession(r, w) as session: await session.initialize() result = await session.call_tool(name, args) return str(result.content)async def get_civic_tools(): """Discover all Civic tools and wrap them as Python callables.""" headers = {"Authorization": f"Bearer {CIVIC_TOKEN}"} async with streamablehttp_client(CIVIC_URL, headers=headers) as (r, w, _): async with ClientSession(r, w) as session: await session.initialize() result = await session.list_tools() def make_tool(name: str, desc: str): def tool_fn(**kwargs): return asyncio.run(call_tool(name, kwargs)) tool_fn.__name__ = name tool_fn.__doc__ = desc or f"Call the {name} tool" return tool_fn return [make_tool(t.name, t.description or "") for t in result.tools]# Configure DSPy with Claudedspy.configure(lm=dspy.LM("anthropic/claude-sonnet-4-6"))# Discover and wrap toolstools = asyncio.run(get_civic_tools())print(f"{len(tools)} tools loaded")# Create a ReAct agentreact = dspy.ReAct(signature="question -> answer", tools=tools)
result = react(question="What events do I have today?")print(result.answer)
DSPy v2.6 has no built-in MCPClient. This pattern uses the raw mcp package for discovery and wraps each tool as a Python callable. Each tool call opens a new MCP connection — for production use, consider connection pooling.