Note
Go to the end to download the full example code.
MCP¶
The tutorial covers the following features of AgentScope in support of the MCP (Model Context Protocol):
Support both HTTP (streamable HTTP and SSE) and StdIO MCP servers
Provide both stateful and stateless MCP clients
Provide both server-level and function-level MCP tool management
Here the stateful/stateless distinction refers to whether the client maintains a persistent session with the MCP server or not. The table below summarizes the supported MCP client types and protocols:
Client Type |
HTTP (Streamable HTTP and SSE) |
StdIO |
---|---|---|
Stateful Client |
|
|
Stateless Client |
|
import asyncio
import json
import os
from agentscope.mcp import HttpStatefulClient, HttpStatelessClient
from agentscope.tool import Toolkit
MCP Client¶
In AgentScope, MCP clients are responsible for
connecting to the MCP server,
obtaining tool functions from the server, and
calling tool functions in the MCP server.
There are two types of MCP clients in AgentScope: Stateful and Stateless. They only differ in how to manage the session with the MCP server.
Stateful Client: The stateful MCP client maintains a persistent session with the MCP server within its lifetime. The developers should explicitly call
connect()
andclose()
methods to manage the connection lifecycle.Stateless Client: The stateless MCP client creates a new session when calling the tool function, and destroys the session right after the tool function call, which is much more lightweight.
Note
The StdIO MCP server only has stateful client, when
connect()
is called, it will start the MCP server locally and then connect to it.For stateful clients, developers must ensure the client is connected when calling the tool functions.
Taking Gaode map MCP server as an example, the creation of stateful and stateless clients are very similar:
stateful_client = HttpStatefulClient(
# The name to identify the MCP
name="mcp_services_stateful",
transport="streamable_http",
url=f"https://mcp.amap.com/mcp?key={os.environ['GAODE_API_KEY']}",
)
stateless_client = HttpStatelessClient(
# The name to identify the MCP
name="mcp_services_stateless",
transport="streamable_http",
url=f"https://mcp.amap.com/mcp?key={os.environ['GAODE_API_KEY']}",
)
Both stateful and stateless clients provide the following methods:
Method |
Description |
---|---|
|
List all tools available in the MCP server. |
|
Get a callable function object from the MCP server by its name. |
MCP as Tool¶
AgentScope provides fine-grained management of MCP tools, including both server-level and function-level management.
Server-Level Management¶
You can register all tools from an MCP server into Toolkit
as follows.
Tip
Optionally, you can specify a group name to organize the tools. Refer to Tool section for group-wise tools management.
toolkit = Toolkit()
async def example_register_stateless_mcp() -> None:
"""Example of registering MCP tools from a stateless client."""
# Register all tools from the MCP server
await toolkit.register_mcp_client(
stateless_client,
# group_name="map_services", # Optional group name
)
print(
"Total number of MCP tools registered:",
len(toolkit.get_json_schemas()),
)
maps_geo = next(
tool
for tool in toolkit.get_json_schemas()
if tool["function"]["name"] == "maps_geo"
)
print("\nThe example ``maps_geo`` function:")
print(
json.dumps(
maps_geo,
indent=4,
ensure_ascii=False,
),
)
asyncio.run(example_register_stateless_mcp())
Total number of MCP tools registered: 15
The example ``maps_geo`` function:
{
"type": "function",
"function": {
"name": "maps_geo",
"description": "将详细的结构化地址转换为经纬度坐标。支持对地标性名胜景区、建筑物名称解析为经纬度坐标",
"parameters": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "待解析的结构化地址信息"
},
"city": {
"type": "string",
"description": "指定查询的城市"
}
},
"required": [
"address"
]
}
}
}
To remove the registered tools, you can use the remove_tool_function
to remove a specific tool function, or remove_mcp_clients
to remove all tools from a specific MCP.
async def example_remove_mcp_tools() -> None:
"""Example of removing MCP tools."""
print(
"Total number of tools before removal: ",
len(toolkit.get_json_schemas()),
)
# Remove a specific tool function by its name
toolkit.remove_tool_function("maps_geo")
print("Number of tools: ", len(toolkit.get_json_schemas()))
# Remove all tools from the MCP client by its name
await toolkit.remove_mcp_clients(client_names=["mcp_services_stateless"])
print("Number of tools: ", len(toolkit.get_json_schemas()))
asyncio.run(example_remove_mcp_tools())
Total number of tools before removal: 15
Number of tools: 14
Number of tools: 0
Function-Level Management¶
We notice the demand for more fine-grained control over MCP tools, such as post-processing the tool results, or use them to create a more complex tool function.
Therefore, AgentScope supports to obtain the callable function object from MCP by its name, so that you can
call it directly,
wrap it into your own function, or anyway you like.
Additionally, you can specify whether to wrap the tool result into ToolResponse
object in AgentScope, so that you can use it seamlessly with the Toolkit
.
If you set wrap_tool_result=False
, the raw result type mcp.types.CallToolResult
will be returned.
Taking the maps_geo
function as an example, you can obtain it as a callable function object as follows:
async def example_function_level_usage() -> None:
"""Example of using function-level MCP tool."""
func_obj = await stateless_client.get_callable_function(
func_name="maps_geo",
# Whether to wrap the tool result into ToolResponse in AgentScope
wrap_tool_result=True,
)
# You can obtain its name, description and json schema
print("Function name:", func_obj.name)
print("Function description:", func_obj.description)
print(
"Function JSON schema:",
json.dumps(func_obj.json_schema, indent=4, ensure_ascii=False),
)
# Call the function object directly
res = await func_obj(
address="Tiananmen Square",
city="Beijing",
)
print("\nFunction call result:")
print(res)
asyncio.run(example_function_level_usage())
Function name: maps_geo
Function description: 将详细的结构化地址转换为经纬度坐标。支持对地标性名胜景区、建筑物名称解析为经纬度坐标
Function JSON schema: {
"type": "function",
"function": {
"name": "maps_geo",
"description": "将详细的结构化地址转换为经纬度坐标。支持对地标性名胜景区、建筑物名称解析为经纬度坐标",
"parameters": {
"type": "object",
"properties": {
"address": {
"type": "string",
"description": "待解析的结构化地址信息"
},
"city": {
"type": "string",
"description": "指定查询的城市"
}
},
"required": [
"address"
]
}
}
}
Function call result:
ToolResponse(content=[{'type': 'text', 'text': '{"results":[{"country":"中国","province":"北京市","city":"北京市","citycode":"010","district":"东城区","street":[],"number":[],"adcode":"110101","location":"116.397455,39.909187","level":"兴趣点"}]}'}], metadata=None, stream=False, is_last=True, is_interrupted=False, id='2025-08-15 09:50:00.992_066f7d')
Further Reading¶
For more details, see:
Total running time of the script: (0 minutes 6.325 seconds)