最近在折腾MCP(模型上下文协议)对接DeepSeek API,想搞个能自动查天气和数据库的Agent。环境是Python 3.10 + FastMCP库,DeepSeek用的是chat模型。结果设置完tools之后,发送查询请求,DeepSeek那边一直返回空response,偶尔报个“函数调用参数格式错误”。我查了官方文档,tool定义里parameters写的是JSON Schema格式,应该没毛病啊……是不是MCP的tool调用方式和普通function calling有区别?还是说DeepSeek本身对MCP支持不完善?有没有踩过坑的老哥指点一下,感激不尽。
MCP对接DeepSeek时,模型一直返回空响应,是我姿势不对吗?
全部回复
共 189 条我也在搞MCP接DeepSeek,遇到过一模一样的空响应。后来发现大概率不是MCP的锅,是DeepSeek那边对tool的strict模式支持有点迷,你试试把parameters里的required字段全部去掉,然后每个属性都加上默认值,有时候它解析不了严格的JSON Schema。
还有个坑是FastMCP默认会把tool的输入包装成一层,比如“input”或者“arguments”这种嵌套结构,但DeepSeek期望的是平铺的parameters,你得在注册tool的时候手动定义一下input_schema,别直接用FastMCP的自动转换。我之前就是卡在这,改完立马正常。
另外你说偶尔报“函数调用参数格式错误”,我猜可能是你传参的时候用了MCP的content格式,但DeepSeek要的是裸JSON对象。你可以把MCP那边的响应先打印出来看一眼,看它到底返回了什么结构,再决定怎么解析。
要是还不行,试试把模型换成deepseek-chat的旧版快照,或者临时用OpenAI兼容模式接一下,DeepSeek的function calling本身就不太稳,MCP等于又套了一层,双重buff容易出幺蛾子。我最后是直接绕开FastMCP,自己手写HTTP请求调工具,反而简单多了。
大概率是MCP把tools参数包了一层,DeepSeek不认,得手动展平再传。你试试直接调function calling接口绕开FastMCP。
我之前也遇到过一模一样的空响应,后来发现问题出在tool的parameters里没用strict模式。DeepSeek对JSON Schema的解析比OpenAI严格得多,比如required字段如果没写全,它不会报错而是直接返回空。你可以试试把每个参数都加个description,哪怕写“用户输入”这种废话,有时候都能触发正常解析。另外MCP的tool调用其实走的是DeepSeek的function calling接口,不是独立的MCP通道,所以本质上还是得符合它那套参数校验逻辑。我最后是直接把FastMCP生成的schema打印出来,手动跟DeepSeek文档里的示例比对,发现它默认给的类型是string但没写enum,导致模型不知道怎么填。你检查下是不是也有类似问题,比如数组类型的items没定义清楚。还有个小坑,如果tools列表里混入了MCP的resource定义,DeepSeek会直接忽略整个请求,返回空。我建议你先用纯function calling方式调通,再套MCP层,这样能快速定位是协议问题还是模型问题。
我之前也踩过类似的坑,最后发现问题出在MCP的tool返回格式和DeepSeek预期的function calling结构不完全一致上。FastMCP默认会把工具结果包装成特定结构,但DeepSeek的chat模型对tools参数的schema要求挺严格的,尤其是parameters里如果用了$ref或者嵌套太深,它可能直接解析失败返回空。你可以试试把tool定义简化,所有属性都平铺开,别用anyOf这种复合类型,然后严格按OpenAI的function calling格式来写,MCP那边做个适配层手动转换一下。另外确认下你传的arguments是不是字符串化的JSON,DeepSeek有时要的是dict而不是string,这个也容易踩。还有个冷门点,如果模型返回了tool_calls但content为空,别慌,这其实是正常行为,你得自己处理调用逻辑,把工具结果回传后它才会生成最终回复,空响应不一定是错误。建议先打个日志把DeepSeek的完整response打出来看看,有时候是HTTP状态码200但choices里内容为空,那基本就是schema问题没跑了。
我之前也卡在这过,后来发现大概率不是MCP的问题,而是FastMCP和DeepSeek在tool schema的兼容性上有点微妙差别。你检查下parameters里的type是不是写成了"object",但没强制要求properties里每个字段都带description,DeepSeek对缺失description的字段特别敏感,容易解析成空。另外,空响应有个常见原因就是模型觉得工具参数不明确,直接放弃了调用,你可以试试把tools定义里的strict模式关掉,或者手动在请求里加个temperature参数,有时候默认值会让输出概率分布太均匀。还有个坑是MCP返回的tool_call_id格式,DeepSeek要求必须是字符串,但FastMCP有时会传成整数,你打印下实际请求体看看。我之前是把FastMCP的版本降到0.9.4才稳的,最新版反而有bug。你要是还不行,可以先用纯OpenAI格式的function calling测试下DeepSeek,排除下是不是MCP协议转换层的问题。
我上周也卡在同样的问题上,最后发现是FastMCP把tool的parameters又包了一层,DeepSeek那边不认这种嵌套格式。你直接打印一下发出去的请求体,看看tools里的schema是不是被MCP改过结构了。另外空响应多半是模型觉得参数必填项缺失,试试把description写详细点,或者手动补几个默认值进去。
我之前用LangChain接DeepSeek也遇到过类似情况,后来把MCP的tool定义直接转成OpenAI格式就好了,你可以试试绕过FastMCP自己构造请求。还有个小坑是DeepSeek对strict模式支持不好,JSON Schema里别加additionalProperties: false,不然容易触发参数校验报错。可以先拿最简单的无参tool测试连通性,再逐步加复杂度。
检查下是不是把MCP的server地址传错到model里去了,我之前就是初始化时把client和server搞混,导致模型收到的是空工具列表。另外FastMCP新版本改过函数签名,如果你用的是旧教程代码,有可能工具注册成功了但实际没绑定上。干脆在tool函数里加个print,看DeepSeek到底有没有触发调用,这样能定位是解析问题还是网络问题。
空响应大概率是tool返回格式没严格按DeepSeek的function calling规范,试试把参数直接写成字符串再解析。
之前我也卡在这过,大概率不是MCP的问题,是DeepSeek对tool choice那个字段有要求,得显式指定成auto或者强制调用,不然它容易返回空。另外你检查下FastMCP生成的parameters里有没有混进MCP自己的类型标注,比如$schema之类,DeepSeek解析这种会直接炸。我之前是把FastMCP的schema手动转成纯JSON Schema才通的,你可以试试直接打印发出去的payload看看格式。
我之前也踩过类似的坑,最后发现问题不在MCP协议本身,而是FastMCP对tool参数的序列化方式和DeepSeek那边解析的预期不太一样。你检查一下实际发出去的请求体,特别是tools数组里每个function的parameters,有没有被FastMCP包成字符串或者多套了一层结构?我遇到过它把JSON Schema的$schema字段也塞进去,导致DeepSeek直接不认。另外,DeepSeek的function calling对strict模式支持得很迷,你试试把parameters里的additionalProperties显式设为false,然后确保所有必填字段都在required里,不然它有时候会静默返回空内容而不是报错。还有一个偏方,就是先用最简单的单tool测试,比如只传一个location参数,排除多工具并发时MCP内部路由的干扰。如果还不行,直接抓包看返回的原始响应,有时候空响应其实是HTTP 200但choices[0].message.content是null,而tool_calls字段里其实有东西,需要你自己解析处理一下。
我之前也遇到过一模一样的坑,查了半天发现MCP里tool的parameters必须严格用JSON Schema的格式,但DeepSeek对嵌套对象或者required字段的兼容性有点迷,有时候空响应其实就是它没解析到参数。你试试把参数定义简化成纯字符串或者扁平结构,别用太复杂的$ref引用,我这么改完就正常了。另外FastMCP版本更新到0.4以上没,老版本对tool返回格式的封装有bug,升级一下可能就顺了。
大概率是DeepSeek的tool call格式跟OpenAI不完全一致,你得把MCP返回的schema转换下再传进去。
之前我也卡这,后来发现空响应是参数格式问题,直接写死JSON试试。
我上周也遇到过一模一样的情况,最后发现是MCP的tool返回格式跟DeepSeek的function calling不完全兼容,尤其是参数里如果有嵌套对象,解析容易出问题。你试试把tools定义直接按OpenAI的格式传,别走FastMCP的封装层,或者检查下response里有没有finish_reason字段,可能是被截断了。另外空响应有时候是temperature设太高,调低到0.2左右能稳定不少。
大概率是MCP把工具参数包了一层,DeepSeek只认裸的function calling格式,试试直接传tools参数别走MCP那层。
我之前也卡这儿,后来发现得把JSON Schema里的$schema字段去掉才行。
大概率是tool返回的JSON没按DeepSeek要求的格式包一层,试试把结果塞进content里看看。
我之前也遇到过类似情况,折腾了两天才发现是MCP返回的tool result格式和DeepSeek预期的不太一样。FastMCP默认走的是MCP的规范化输出,但DeepSeek的function calling其实更倾向于OpenAI那种直白的JSON结构,你得在注册tool的时候手动把response_format或者strict模式调一下。另外空响应多半是参数校验没通过,建议把DeepSeek的API日志打开,看看它到底收到了什么,我那次就是parameters里多了一层嵌套,它直接不认。还有个小坑,DeepSeek对tool name的命名规则很敏感,不能用横杠只能用下划线,不然静默失败。你要是用FastMCP的话,试试把它内置的convert_to_openai_tools方法打印出来,对比下官方示例里的schema,基本就能定位问题。实在不行就绕开MCP,直接用DeepSeek官方SDK的function calling,把tools列表硬编码传进去,等调通了再封装回MCP层,这样排查起来更清晰。
我之前也卡在这过,问题多半不在MCP本身,而是DeepSeek对tool的strict模式支持有点迷。你试试把parameters里的required字段全去掉,或者手动把JSON Schema转成最简形式,有时候多一个默认值它都识别不了。另外FastMCP底层会自己包一层格式,跟直接调API的function calling确实不完全一样,建议抓个实际发出去的请求体看看,对比下官方示例的差异,八成是某个字段名对不上。
我之前也遇到过一模一样的情况,最后发现问题出在MCP返回的tool schema和DeepSeek官方function calling的字段名有细微差别上。FastMCP默认会在parameters外面包一层自己的结构,但DeepSeek这边认的是最原始的JSON Schema,你得手动把那个多余的嵌套剥掉才行。另外空响应大概率不是模型不支持,而是它已经解析出函数调用,但你的代码没把tools结果正确回传给第二轮对话,导致它觉得没话说了就直接返回空。你可以试着在发送请求前打印一下实际发给API的payload,对比一下DeepSeek文档里的例子,看看是不是多了什么MCP特有的元数据字段。还有个坑是DeepSeek对tool choice参数比较敏感,如果没显式设置成auto或required,它可能就默认不触发工具调用。我当时是直接把tools定义里的description写得更详细,顺便把temperature调低到0.1,空响应就消失了,你可以试试。
我之前也卡在这过,问题大概率不是DeepSeek不支持MCP,而是你工具返回的格式跟它预期的function calling参数结构没对齐。FastMCP包装后的tool schema有时候会嵌套一层,DeepSeek那边读不到你定义的顶层parameters,就容易报空响应。你试试直接在请求里手动把tools参数打印出来,跟OpenAI格式对比下,差异应该一眼就能看出来。另外确认下模型版本,deepseek-chat对工具调用的支持确实没reasoner模型那么稳,换个模型说不定就好了。
我之前也踩过这个坑,搞了一晚上最后发现是tool的description写太短了,DeepSeek对参数说明特别敏感,你得把每个字段的用途和格式都写清楚,不然它容易自己脑补一个错误格式。另外MCP和原生function calling确实有区别,MCP的tool调用会走一层协议转换,你确认下FastMCP版本是不是最新的,老版对JSON Schema的$ref解析有bug,会把参数变成空对象。还有个小细节,DeepSeek的chat模型如果同时返回多个tool_calls,有时候响应体里content会是null,这不是报错,你得检查一下返回结构里tool_calls字段而不是只盯着content看。我建议你先把tools列表精简到只剩一个查询天气的简单函数,排除数据库那边的干扰,然后打印一下发给API的完整请求体,对比官方示例看参数名是不是被FastMCP改写了。如果还不行,试试把parameters里的required字段去掉,DeepSeek对必填参数的处理比OpenAI严格,缺一个就整个报格式错误。最后实在不行就换用requests直接裸调API,绕开MCP层,先验证DeepSeek那边能不能正常响应,这样能快速定位是协议问题还是模型问题。
我之前也踩过类似的坑,最后发现是MCP返回的tool结果格式和DeepSeek预期的function calling不完全一致,尤其是参数嵌套层级,建议先打印一下实际发出去的请求体对比官方示例。另外空响应大概率是模型觉得没有tool能回答,或者参数校验失败直接放弃了,你可以试试在system prompt里强制它先调用工具再回答。还有个小细节,FastMCP默认的tool名是带命名空间的,DeepSeek可能不认带点的名字,改成下划线试试。