一个兼容Python2/3的Model Context Protocol (MCP) 服务端框架,专为CG/DCC软件设计。支持最新的Streamable HTTP传输协议。
- 完整的MCP实现: 支持MCP 2025-06-18规范
- Streamable HTTP: 实现最新的MCP Streamable HTTP传输协议
- Python2/3兼容: 同时支持Python 2.7和Python 3.x
- CG软件集成: 为Maya等CG软件提供专门的适配器
- 插件式架构: 可扩展到其他DCC软件
- 异步处理: 后台运行,不阻塞主程序
- ✅ Autodesk Maya (2018+)
- 🔄 3ds Max (计划中)
- 🔄 Blender (计划中)
- 🔄 Houdini (计划中)
git clone <repository-url>
cd streamable_cg_mcppip install -r requirements.txt-
将
cg_adapters/maya/maya_plugin.py复制到Maya插件目录:- Windows:
%USERPROFILE%/Documents/maya/plug-ins/ - macOS:
~/Library/Preferences/Autodesk/maya/plug-ins/ - Linux:
~/maya/plug-ins/
- Windows:
-
在Maya中加载插件:
- 打开
Window > Settings/Preferences > Plug-in Manager - 找到
maya_plugin.py并勾选Loaded
- 打开
-
启动MCP服务器:
mcpServer -start -host localhost -port 8080
在Maya脚本编辑器中运行:
import threading
from cg_adapters.maya import MayaMCPServer
def run():
server = MayaMCPServer(
host='127.0.0.1',
port=8109,
name="Maya MCP Server"
)
server.start()
thread = threading.Thread(target=run)
thread.start()from mcp_framework.server.base_server import MCPServer
from mcp_framework.protocol.official_compatible_http import OfficialCompatibleHTTPTransport
# 创建MCP服务器
server = MCPServer("My MCP Server")
# 注册工具
def hello_tool(args):
name = args.get('name', 'World')
return f"Hello, {name}!"
server.register_tool(
'hello',
hello_tool,
'问候工具',
{
'type': 'object',
'properties': {
'name': {
'type': 'string',
'description': '要问候的名字'
}
},
'required': ['name']
}
)
# 设置传输层并启动
transport = OfficialCompatibleHTTPTransport('localhost', 8080)
server.set_transport(transport)
server.start()框架为Maya提供了丰富的内置工具:
- 对象管理: 创建、选择、删除、变换对象
- 场景信息: 获取摄像机、灯光、材质等信息
- 命令执行: 执行Python和MEL命令
- 智能提示: 建模助手、场景优化建议
# 在客户端使用工具的示例
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "maya_create_object",
"arguments": {
"object_type": "cube",
"name": "MyCube"
}
}
}MCP_AUTO_START: 设置为true可在Maya启动时自动启动MCP服务器MCP_HOST: 默认服务器主机地址 (默认: localhost)MCP_PORT: 默认服务器端口 (默认: 8080)
# 有状态服务器(推荐)
transport = OfficialCompatibleHTTPTransport('localhost', 8080, stateful=True)
# 无状态服务器
transport = OfficialCompatibleHTTPTransport('localhost', 8080, stateful=False)启动服务器后,可通过以下端点访问:
POST /mcp- 发送MCP消息(符合Streamable HTTP标准)GET /mcp- 建立SSE连接接收服务器消息(当Accept头包含text/event-stream时)DELETE /mcp- 清理会话和断开连接GET /health- 健康检查
- 使用
Mcp-Session-Id头部进行会话管理 - 支持
application/json和text/event-stream响应类型 - 完全兼容标准MCP客户端
- 默认情况下,服务器只监听localhost,仅允许本地连接
- 执行的Maya命令在安全的沙箱环境中运行
- 支持会话管理和权限控制
运行示例程序:
cd examples
python maya_example.py本框架完全符合MCP官方Python SDK标准,实现了MCP 2025-06-18规范:
- ✅ JSON-RPC 2.0消息格式 - 完全兼容官方实现
- ✅ 协议生命周期管理 - 标准初始化和终止流程
- ✅ 能力协商 - 符合官方能力声明格式
- ✅ 工具调用 (Tools) - 完整的工具注册和执行
- ✅ 资源访问 (Resources) - 动态资源发现和读取
- ✅ 提示模板 (Prompts) - 可重用的提示管理
- ✅ Streamable HTTP传输 - 完全兼容官方标准
- ✅ 标准头部名称 -
mcp-session-id,mcp-protocol-version - ✅ 会话管理 - ASCII字符验证、生命周期管理
- ✅ SSE流处理 - 标准事件格式和连接管理
- ✅ 错误处理 - 官方JSON-RPC错误码和格式
- ✅ Python 2.7+ 和 Python 3.x 全面兼容
- ✅ MCP Python SDK客户端 完全兼容
- ✅ Claude Desktop MCP 标准连接
- ✅ Cursor MCP集成 无缝对接
要为其他CG软件创建适配器:
from cg_adapters.base_adapter import CGAdapter, CGMCPServer
class MyAppAdapter(CGAdapter):
def check_availability(self):
# 检查软件是否可用
return True
def initialize(self):
# 初始化适配器
return True
def execute_command(self, command):
# 执行软件命令
pass
def get_scene_info(self):
# 获取场景信息
return {}
# 创建专用服务器
class MyAppMCPServer(CGMCPServer):
def __init__(self):
adapter = MyAppAdapter()
super(MyAppMCPServer, self).__init__("MyApp MCP Server", adapter)可以使用MCP Python SDK或任何支持MCP的客户端连接:
# 使用MCP Python SDK示例
from mcp import Client
from mcp.transport.http import HttpTransport
client = Client(HttpTransport("http://localhost:8080/mcp"))
await client.initialize()
# 调用工具
result = await client.call_tool("maya_create_object", {
"object_type": "sphere",
"name": "MySphere"
})在Maya中测试:
# 在Maya脚本编辑器中运行
execfile(r'D:\code\streamable_cg_mcp\maya_test_simple.py')测试MCP端点:
# 先启动服务器,然后在另一个终端运行
python examples/test_mcp_endpoint.py测试MCP客户端连接:
# 模拟标准MCP客户端行为,测试超时修复
python examples/test_mcp_client.py错误信息: AttributeError: 'module' object has no attribute 'HTTPServer'
解决方案: 确保使用最新版本的代码,我们已经修复了tornado导入问题。
错误信息: ImportError: No module named xxx
解决方案:
- 确保已安装依赖:
pip install -r requirements.txt - 检查Python路径设置
- 在Maya中确保项目路径已添加到sys.path
错误信息: Timed out while waiting for response to ClientRequest. Waited 10 seconds.
原因: MCP客户端无法解析服务器响应格式
解决方案:
- ✅ 已修复:响应格式符合MCP Streamable HTTP标准
- ✅ 使用单个JSON-RPC对象而非数组
- ✅ 正确实现
/mcp端点处理(GET/POST/DELETE) - ✅ 添加DELETE方法支持会话清理
- ✅ 使用
python examples/test_mcp_client.py验证修复
错误信息: 405 DELETE /mcp 或类似的方法不允许错误
原因: MCP客户端发送DELETE请求进行会话清理,但服务器不支持该方法
解决方案:
- ✅ 已修复:添加DELETE方法支持
- ✅ DELETE方法用于清理会话和断开连接
- ✅ 修复204状态码不发送响应体(避免tornado断言错误)
- ✅ 符合MCP客户端标准行为
错误信息: TimeoutError 或 CancelledError 在客户端初始化时
原因: Streamable HTTP协议实现不完全符合MCP官方标准
解决方案:
- ✅ 参考官方Python SDK实现全面重构
- ✅ 头部名称标准化 - 使用
mcp-session-id,mcp-protocol-version而非自定义格式 - ✅ 会话ID验证 - 严格按照官方标准验证ASCII字符 (0x21-0x7E)
- ✅ Accept头部处理 - 根据
Accept: application/json, text/event-stream选择响应模式 - ✅ SSE响应模式 - 支持202异步响应通过SSE发送message事件
- ✅ DELETE方法标准化 - 返回200 OK而非204 No Content
- ✅ 协议版本验证 - 支持
mcp-protocol-version头部验证 - ✅ 错误响应格式 - 完全符合官方JSON-RPC 2.0标准
- ✅ notifications/initialized支持 - 正确处理官方客户端的通知格式
错误信息: AssertionError: Cannot send body with 204
原因: HTTP 204 No Content状态码不允许有响应体,但代码调用了write()方法
解决方案:
- ✅ 已修复:204状态码不再调用write()方法
- ✅ 遵循HTTP标准:204状态码表示成功但无内容返回
错误信息: Address already in use
解决方案: 更改端口号或停止占用端口的程序
问题根源:
json_rpc.py中的JSONRPCRequest构造函数错误地将id=0替换为 UUID- 调试代码在处理通知消息时尝试访问不存在的
id属性
修复:
- 将
self.id = id or text_type(uuid.uuid4())改为self.id = id if id is not None else text_type(uuid.uuid4()) - 调试代码安全检查
hasattr(message, 'id')再访问 ID 属性 - 增强HTTP请求跟踪,监控所有GET/POST/DELETE请求
- 🔧 修复SSE连接立即关闭问题 - SSE连接现在保持打开状态并发送keep-alive
运行SSE连接修复测试:
python test_sse_connection_fix.py这个测试验证SSE连接保持打开状态,解决客户端无法发送tools/list请求的问题。
# 通用MCP服务器测试(自动选择最佳实现)
python start_mcp_server.py
# Maya MCP服务器测试
python start_maya_mcp.py问题根源:
- JSONRPCRequest ID处理错误(
id=0被替换为UUID) - SSE连接立即关闭,导致客户端无法发送后续请求
解决方案:
- 修复ID处理逻辑,正确保持
id=0 - 修复SSE连接保持打开状态,客户端现在可以正常发送 tools/list 请求
最终的官方兼容HTTP实现解决了:
- ✅ 客户端卡死在
await session.initialize()- 完全修复 - ✅ JSON-RPC双重包装问题 - 修复响应结构的双重嵌套
- ✅ Pydantic验证错误 - 解决 'Field required' 错误
- ✅ JSON-RPC响应格式兼容性 - 100%符合官方客户端要求
- ✅ HTTP响应头部标准化 - 严格按照官方服务器格式
- ✅ 请求/响应ID完美匹配 - 确保类型和值完全一致
- ✅ 详细调试日志 - 便于问题诊断
- ✅ 会话管理 - 标准MCP会话生命周期
- ✅ CORS支持 - 完整的跨域资源共享
- ✅ 错误处理 - 标准JSON-RPC错误响应
- ✅ 与官方MCP Python SDK的100%兼容性
启动服务器时添加 --debug 参数获取详细错误信息:
python start_maya_mcp.py --debug --port 8080如果遇到问题,请提供以下信息:
- Python版本
- Maya版本(如果适用)
- 错误消息和堆栈跟踪
- 重现步骤
- 是否运行了测试脚本
MIT License - 详见 LICENSE 文件
- Model Context Protocol 团队
- Autodesk Maya开发团队
- 所有贡献者和测试者
经过深入调试和全面修复,现在实现了:
- ✅ 完整的MCP协议支持 -
initialize→notifications/initialized→tools/list→tools/call完全正常 - ✅ 官方客户端兼容 - 100%兼容官方MCP Python SDK客户端
- ✅ ThreadingHTTPServer - 支持并发请求处理(SSE + JSON-RPC)
- ✅ Unicode编码修复 - 安全处理中文字符,避免编码错误
- ✅ SSE连接保持 - 正确的keep-alive机制
- ✅ 工具Schema完整 - 所有工具都有正确的inputSchema定义
- ✅ Pydantic验证通过 - 不再有字段缺失错误
- ✅ Content-Type头部 - 所有HTTP响应都有正确的Content-Type设置
- ✅ HTML错误响应修复 - 404错误改为JSON格式,消除Content-Type不匹配
- ✅ 通知消息头部修复 -
notifications/initialized等通知消息响应包含正确的Content-Type - ✅ 官方MCP标准通知处理 - 完全符合官方MCP服务器行为,202 Accepted响应
Unexpected content type:
✅ 解决方案:修复所有HTML格式的错误响应,改为JSON格式。
根本原因:使用了BaseHTTPRequestHandler.send_error()发送HTML格式的404错误页面,但MCP客户端只接受JSON或SSE格式的响应。
官方MCP客户端严格检查Content-Type:
- 如果以
application/json开头 → 处理为JSON响应 - 如果以
text/event-stream开头 → 处理为SSE响应 - 如果是
text/html→ 报告"Unexpected content type"错误
修复前 ❌:
# 发送HTML格式的404错误
if self.path != '/mcp':
self.send_error(404, 'Not Found') # Content-Type: text/html
return修复后 ✅:
# 发送JSON格式的404错误
if self.path != '/mcp':
self._send_error_response('Not Found', 404) # Content-Type: application/json
return修复范围:
- ✅
do_GET路径验证404错误 - ✅
do_POST路径验证404错误 - ✅
do_DELETE路径验证404错误 - ✅
DELETE和OPTIONS请求Content-Type头部 - ✅ 通知消息响应Content-Type头部 -
notifications/initialized等通知消息
Error parsing JSON response
pydantic_core._pydantic_core.ValidationError: 1 validation error for JSONRPCMessage
Invalid JSON: EOF while parsing a value at line 1 column 0 [type=json_invalid, input_value=b'', input_type=bytes]
✅ 解决方案:为通知消息添加有效的JSON响应体。
根本原因:notifications/initialized 等通知消息返回空的响应体 b'',但客户端设置了 Content-Type: application/json,期望一个有效的JSON响应。
关键修复:通知消息(如 notifications/initialized)现在完全遵循官方MCP标准:
# 修复前 ❌ - 错误的200状态码和JSON响应
if response is None:
self.send_response(200) # 错误的状态码
self.send_header('Content-Type', 'application/json') # 不应该有Content-Type
empty_response = b'{}' # 不应该有响应体
self.wfile.write(empty_response)
# 修复后 ✅ - 完全符合官方MCP服务器行为
if response is None:
self.send_response(202) # 202 Accepted,符合官方标准
# 不设置Content-Type,因为没有响应体
self._send_cors_headers()
self.send_header('mcp-session-id', session_id)
self.send_header('mcp-protocol-version', '2025-06-18')
self.end_headers()
# 不发送响应体,符合官方MCP服务器行为官方MCP标准:通知消息返回 202 Accepted,无Content-Type,无响应体,客户端不会尝试JSON解析。
pydantic_core._pydantic_core.ValidationError: 11 validation errors for JSONRPCMessage
JSONRPCRequest.method - Field required [type=missing, input_value={}, input_type=dict]
JSONRPCNotification.method - Field required [type=missing, input_value={}, input_type=dict]
...
✅ 解决方案:遵循官方MCP标准,对通知消息返回202 Accepted状态码,无响应体。
根本原因:之前尝试发送空JSON对象 {} 作为响应体,但客户端期望有效的JSON-RPC消息格式。空对象不符合任何JSON-RPC消息类型的Pydantic验证要求。
官方行为:查看官方MCP Python SDK服务器实现,发现对于通知消息(非JSONRPCRequest),应该返回 202 Accepted 状态码,不设置Content-Type头部,不发送响应体。
pydantic_core._pydantic_core.ValidationError: 2 validation errors for ListToolsResult
tools.0.inputSchema - Field required [get_scene_info]
tools.7.inputSchema - Field required [maya_get_selection]
✅ 解决方案:为所有工具添加正确的inputSchema字段,即使工具不需要参数也必须提供空的schema:
# 修复前 ❌
self.register_tool('get_scene_info', handler, 'description')
# 修复后 ✅
self.register_tool('get_scene_info', handler, 'description', {
'type': 'object',
'properties': {},
'required': []
})运行 python test_final_success.py 验证完整功能:
- ✅ 初始化成功
- ✅ 工具列表返回正确
- ✅ 所有工具调用正常
- ✅ 无Pydantic验证错误
让AI轻松访问您的CG工作流程! 🎨✨