chore: initialize sandbox and overwrite remote content
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled
Some checks failed
Pre-commit / run (ubuntu-latest) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_en (ubuntu-latest, 3.10) (push) Has been cancelled
Deploy Sphinx documentation to Pages / build_zh (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (macos-15, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (ubuntu-latest, 3.12) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.10) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.11) (push) Has been cancelled
Python Unittest Coverage / test (windows-latest, 3.12) (push) Has been cancelled
This commit is contained in:
207
docs/tutorial/en/src/task_mcp.py
Normal file
207
docs/tutorial/en/src/task_mcp.py
Normal file
@@ -0,0 +1,207 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
.. _mcp:
|
||||
|
||||
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:
|
||||
|
||||
.. list-table:: Supported MCP client types and protocols
|
||||
:header-rows: 1
|
||||
|
||||
* - Client Type
|
||||
- HTTP (Streamable HTTP and SSE)
|
||||
- StdIO
|
||||
* - Stateful Client
|
||||
- ``HttpStatefulClient``
|
||||
- ``HttpStatelessClient``
|
||||
* - Stateless Client
|
||||
- ``StdIOStatefulClient``
|
||||
-
|
||||
|
||||
"""
|
||||
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()`` and ``close()`` 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.
|
||||
# - When multiple `HttpStatefulClients` or `StdIOStatefulClients` are connected, they should be closed in Last In First Out (LIFO) order to prevent errors.
|
||||
#
|
||||
# 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:
|
||||
#
|
||||
# .. list-table:: MCP Client Methods
|
||||
# :header-rows: 1
|
||||
#
|
||||
# * - Method
|
||||
# - Description
|
||||
# * - ``list_tools``
|
||||
# - List all tools available in the MCP server.
|
||||
# * - ``get_callable_function``
|
||||
# - 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 :ref:`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())
|
||||
|
||||
# %%
|
||||
# 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())
|
||||
|
||||
# %%
|
||||
# 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())
|
||||
|
||||
# %%
|
||||
# Further Reading
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
#
|
||||
# For more details, see:
|
||||
#
|
||||
# - :ref:`tool`
|
||||
# - :ref:`agent`
|
||||
#
|
||||
Reference in New Issue
Block a user