Attach MCP tools to a Python agent¶
Start a local MCP server, let URSA discover its tools, and attach those tools to
a ChatAgent. This example keeps the server deliberately small so you can see
the complete connection before adapting it to a real service.
The workflow has two processes:
laboratory_server.pyserves onelist_measurementstool over local Streamable HTTP.attach_mcp_tools.pyreadsconfig.yaml, initializes the configured model, attaches the discovered MCP tool, and asks the agent to use it.
Read the MCP configuration guide for other transports and authentication settings. The Python scripts guide explains model initialization and direct agent use.
Prepare the example¶
Open a terminal in this folder, install the locked environment, and set your OpenAI key.
The example uses URSA's default OpenAI model. Follow models and inference providers before running it with another provider.
Inspect the server configuration¶
The client reads this MCP server definition:
The endpoint is local and does not include authentication. Keep it bound to your machine for this exercise.
Start the MCP server¶
In the first terminal, run:
Leave that process running. It serves the MCP endpoint at
http://127.0.0.1:8000/mcp.
Attach and use the tool¶
Open a second terminal in the same folder, set OPENAI_API_KEY there as shown
above, and run:
The script prints the tool-to-server mapping returned by add_mcp_tools(), then
prints the agent's summary. Confirm that list_measurements is attached from
the laboratory server and that the answer identifies alloy-b as the largest
reported strength while noting that alloy-c was measured at another
temperature.
The client implementation is short enough to inspect in full:
import asyncio
from pathlib import Path
from langchain_core.messages import HumanMessage
from ursa.agents import ChatAgent
from ursa.cli.config import UrsaConfig
from ursa.util.mcp import start_mcp_client
async def main() -> None:
config = UrsaConfig.from_file(Path("config.yaml")).resolve()
agent = ChatAgent(
llm=config.llm_model.init_chat_model(),
workspace=Path("ursa-script-workspace"),
)
mcp_client = start_mcp_client(config.mcp_servers)
tool_sources = await agent.add_mcp_tools(mcp_client)
print("Attached MCP tools:", tool_sources)
result = await agent.ainvoke({
"messages": [
HumanMessage(
content=(
"Use the laboratory tools to list the available measurements, "
"then summarize the strongest sample and any temperature "
"difference that limits a direct comparison."
)
)
],
"thread_id": agent.thread_id,
})
print(result["messages"][-1].content)
if __name__ == "__main__":
asyncio.run(main())
add_mcp_tools() accepts tool_name="list_measurements" or a list of names
when an agent should receive only selected server tools. The server must already
be running when discovery begins.
Adapt the example¶
Add another @mcp.tool() function to laboratory_server.py, restart the server,
and run the client again. Update the prompt so the agent has a clear reason to
choose the new tool. Review the MCP reference when
you add production transports, credentials, or remote endpoints.