IDE MCP
mcp工具
工具申明
使用 @mcp.tool() 装饰器将python方法申明为mcp工具。
from pydantic import Field
@mcp.tool(description="获取指定城市的天气信息")
async def get_weather(city: str = Field(description="需要查询天气的城市"),
date: str = Field(description="查询天气的日期")) -> dict:
return {}tips:
-
不用关心FastMCP的创建和main方法,蓝心九问会动态添加到代码中,fastmcp版本2.11.0,mcp版本1.11.0。
-
使用@mcp.tool()装饰器声明工具,工具可以申明多个但不要重名。
-
使用description字段设置工具描述,如果为空则会将方法注释作为工具描述。
-
工具执行超时时间180秒。
入参申明
mcp工具入参支持如下类型:
| 类型 | 说明 |
|---|---|
| int | 整数类型 |
| float | 浮点数类型 |
| bool | 布尔值类型 |
| str | 字符串类型 |
| dict | 字典类型 |
| enum | 枚举类型 |
| pydantic model | 自定义对象类型 |
| list[int] | 整数列表类型 |
| list[float] | 浮点数列表类型 |
| list[bool] | 布尔值列表类型 |
| list[str] | 字符串列表类型 |
| list[dict] | 字典列表类型 |
| list[pydantic model] | 自定义对象列表类型 |
简单类型
简单类型指的是int、float、str、bool、dict以及他们的数组类型。
from pydantic import Field
@mcp.tool(description="获取指定城市的天气信息")
async def get_weather(date: str = Field(default="2025-10-10", description="查询天气的日期")) -> dict:
return {}使用pydantic Field类辅助申明工具入参。
default:定义该参数默认值。
description:参数描述,用于告知大语言模型该参数的含义。
default说明
mcp sdk会使用pydantic validator对方法入参进行校验,pydantic validator要求没有默认值的参数放前面,有默认值的参数放后面,如下:
def fn(没有默认值参数,有默认值参数)这种校验和python语法是保持一致的,与python语法一样,有下面两种方法通过校验:
-
遵循校验规则,有默认值参数全部放后面。
-
方法入参前面加上 * ,如下:
from pydantic import Field
@mcp.tool(description="获取指定城市的天气信息")
async def get_weather(
*,
date: str = Field(default="2025-10-10", description="查询天气的日期"),
city: str = Field(description="城市")
) -> dict:
return {}自定义类型
自定义类型指的是python自定义类,该类需要继承pydantic BaseModel。
1.定义类型
from pydantic import Field, BaseModel
class User(BaseModel):
user_name: str = Field(default="哈哈", description="用户信息")
age: int = Field(description="年龄")2.使用类型
@mcp.tool(description="保存用户信息")
async def save_user(user: User = Field(description="用户信息")) -> dict:
return {}
@mcp.tool(description="批量保存用户信息")
async def save_users(user: list[User] = Field(description="用户信息列表")) -> dict:
return {}3.支持嵌套
自定类型支持再嵌套自定义类型,但是不支持自定义类型再嵌套自己,递归解析出现栈溢出,如下面User类中存在User类型的属性
class User(BaseModel):
user_name: str = Field(description="用户信息")
age: int = Field(description="年龄")
user: User = Field(description="这个属性又是User类型,产生循环嵌套,不支持") 如何获取系统参数
系统参数指的是蓝心九问平台参数,例如用户工号、登录态信息等,详情如下:
| 系统参数 | 类型 | 说明 |
|---|---|---|
| sys_openid | str | 工号,只有需要登录的场景才有 |
| sys_uuc_info | dict | 登录态信息, 包含token、uucToken、uuid |
| sys_from_debug | bool | 是否来自调试 |
蓝心九问系统参数采用申明注入的方式(只要申明的参数名与系统参数名一致就会自动注入改参数),如下:
@mcp.tool(description="获取指定城市,指定时间的天气信息")
async def get_weather(
sys_openid:str,
sys_uuc_info:dict,
sys_from_debug:bool,
) -> dict:
return {}工具返回
mcp工具方法最好申明返回值类型,建议返回值类型为为dict或者pydantic BaseModel。
from pydantic import Field, BaseModel
@mcp.tool(description="保存用户信息")
async def save_user(user: User = Field(description="用户信息")) -> dict:
return {}
class Result(BaseModel):
user_id:str=Field(description="用户id")
@mcp.tool(description="保存用户信息")
async def save_user(user: User = Field(description="用户信息")) -> Result:
return Result(user_id=user.id)输出schema生成
mcp在调试时会生成mcp工具输出schema,这个schema主要用于工作流插件节点的输出定义,如下图:

基于sdk生成(☆推荐)
当工具返回为pydantic BaseModel时(方法申明),mcp sdk会基于 BaseModel的定义生成对应的outputSchema。
基于输出逆向生成
如果mcp工具返回的参数类型为dict(方法申明)时,mcp会根据调试结果逆向生成outputSchema 例如某mcp工具输出结果如下:
{
"news": [
{
"id": 0,
"title": "世预赛最后一战!国足vs巴林",
"desc": "",
"cover": "https://fyb-1.cdn.bcebos.com/fyb/de6163834f53ca92c1273fff98ac9078.jpeg",
"author": "",
"timestamp": 0,
"hot": 7808565,
"url": "https://www.baidu.com/s?wd=hello",
"mobileUrl": "https://m.baidu.com/s?word=hello"
}
]
}基于这个输出结果逆向生成出来的outputSchema是和下面的pydantic BaseModel是等价的。
from pydantic import Field, BaseModel
class New(BaseModel):
id:int=Field(description="")
title:int=Field(description="")
……
class Result(BaseModel):
news:list[New]=Field(description="")基于逆向生成出来的outputSchema缺少描述信息,而且要想生成outputSchema完整,需要工具返回的dict包含全部的key,并且value不能为None,list不能没有元素。
手动设置
如果mcp方法返回为dict时,针对上面如果返回的dict少key、有None值或者空list,导致逆向生成出来的outputSchema不完整
九问mcp工具提供了一种自定义outputSchema方式(此种方式的优先级最高),具体使用方式如下图所示:

在返回dict中增加$$mcp_output_schema字段,其值为一个自定义dict,当检测到返回的dict包含该字段时,会根据该字段的值生成schema,达到自定义schema的目的,这是一种兜底方案,建议使用默认生成
代码规范
蓝心九问调试mcp时会对代码进行校验,不遵循下面的规范会导致校验不通过。
禁止使用print函数,在mcp中禁止向标准输出流(stdout)中写入数据,不然会污染mcp的协议通道,这一点很重要。
不要导入使用不到的包,例如下面代码中的time。
import time
@mcp.tool(description="测试")
async def test() -> dict:
return {}自定义类之间依赖,以类B依赖类A为例,下面两点遵循一个即可(python语言要求)。
①被依赖的类写在上面,类A声明在B的前面。
from pydantic import BaseModel, Field
class A(BaseModel):
name: str = Field(description="")
class B(BaseModel):
a: A = Field(description="")②被依赖的类作为属性时加上单引号,例如下面类B中a属性。
from pydantic import BaseModel, Field
class B(BaseModel):
a: 'A' = Field(description="")
class A(BaseModel):
name: str = Field(description="")禁用库
目前蓝心九问mcp并没有实现运行环境隔离,为了保障mcp服务运行安全与稳定,蓝心九问禁用了一些python库,如下:
[
"os",
"builtins",
"threading",
"resource",
"shutil",
"pathlib",
"tempfile",
"subprocess",
"multiprocessing",
"requests",
"httpx",
"socket",
"http",
"aiohttp",
"logging",
"sys",
"ctypes",
"mmap",
"importlib",
"imp",
"runpy",
"pkgutil",
"pickle",
"cPickle",
"marshal",
"shelve",
"pty",
"tty",
"termios",
"shlex",
"fcntl",
"pwd",
"grp",
"spwd",
"signal",
"sysconfig",
"platform",
"urllib",
"ssl"
]内置库
下面是蓝心九问官方提供的SDK。
📢 重要重要: 建议使用异步SDK,同步SDK会导致整个MCP服务的并发能力比较弱。
async_http_client(☆推荐)
async_http_client用于异步请求第三方服务
async_http_client基于httpx封装,使用方式与requests类似,支持GET、PUT、POST、DELETE。
默认timeout=3,需要是数值类型,不支持元组类型。
from pydantic import Field
import async_http_client
@mcp.tool(description="测试")
async def test() -> dict:
response = await async_http_client.get(url="http://127.0.0.1:5001/health")
return {"response":response.json()}http_client
async_http_client的同步版本
http_client基于requests封装,使用方式与requests类似,支持GET、PUT、POST、DELETE。
默认timeout=3,需要是数值类型,不支持元组类型。
import http_client
@mcp.tool(description="测试")
async def test() -> dict:
response = http_client.get(url="http://127.0.0.1:5001/health")
return {"response":response.json()}async_http_proxy_client(☆推荐)
async_http_proxy_client用于线上环境异步请求蛇口机房(测试环境)部署的服务
async_http_proxy_client只能用于简单的接口转发,其能力相较于requests包有较大功能缺失,支持GET、POST。
默认timeout=3。
import async_http_proxy_client
@mcp.tool(description="测试")
async def test() -> dict:
params = {"param": "params"}
cookies = {"cookie":"cookie"}
headers = {"header":"header"}
# for get
response_get = await async_http_proxy_client.get(
url="",
params=params,
cookies=cookies,
headers=headers,
timeout=2
)
# for post
payload = {"payload": "payload"}
response_post = await async_http_proxy_client.post(
url="",
params=params,
json=payload,
cookies=cookies,
headers=headers,
timeout=2
)
if response_get.status_code != 200:
raise RuntimeError("请求失败")
return response_get.json()http_proxy_client
async_http_proxy_client的同步版本
async_bot_caller(☆推荐)
异步调用蓝心九问Bot
from async_bot_caller import AsyncBotCaller
@mcp.tool()
async def test()->dict:
async_bot_caller = AsyncBotCaller(
bot_id=1204,
authorization="app-hQlVZw3weqIGT8tyL1oj904R",
env="test"
)
result = await async_bot_caller.call(
query="你好啊",
input={}, # 预置变量
timeout=10
)env指的是当前使用蓝心九问的环境(测试:test、预发:pre、线上:prd),不是跨环境调用(如:线上调用测试环境插件)
bot_caller
async_bot_caller的同步版本
from bot_caller import BotCaller
@mcp.tool()
async def test()->dict:
bot_caller = BotCaller(
bot_id=1204,
authorization="app-hQlVZw3weqIGT8tyL1oj904R",
env="test"
)
result = bot_caller.call(
query="你好啊",
input={}, # 预置变量
timeout=10
)env指的是当前使用蓝心九问的环境(测试:test、预发:pre、线上:prd),不是跨环境调用(如:线上调用测试环境插件)
async_plugin_caller(☆推荐)
异步调用其他已发布的工具或者mcp
from async_plugin_caller import AsyncPluginCaller
@mcp.tool()
async def test()->dict:
async_plugin_caller = AsyncPluginCaller(plugin_id=967, env="test")
result = await async_plugin_caller.call(
tool_name = "query_site",
arguments = {"siteId": "wk25070212d406c6"},
timeout=10
)env指的是当前使用蓝心九问的环境(测试:test、预发:pre、线上:prd),不是跨环境调用(如:线上调用测试环境插件)
plugin_caller
async_plugin_caller的异步版
from plugin_caller import PluginCaller
@mcp.tool()
async def test()->dict:
plugin_caller = PluginCaller(plugin_id=967, env="test")
result = plugin_caller.call(
tool_name = "query_site",
arguments = {"siteId": "wk25070212d406c6"},
timeout=10
)env指的是当前使用蓝心九问的环境(测试:test、预发:pre、线上:prd),不是跨环境调用(如线上调用测试环境插件)