Haskell MCP服务器
使用Stack在Haskell中实现模型上下文协议(MCP)服务器。
特性
- JSON-RPC 2.0协议:完全符合MCP规范
- WebSocket通信:实时双向通信
- 工具系统:可扩展的工具注册和执行
- 资源管理:资源列表和阅读能力
- 类型安全:利用Haskell的类型系统进行健壮的协议处理
内置工具
- 回声 -回显输入消息
- 当前时间 -获取当前系统时间
- 计算 -执行基本算术计算
内置资源
- config://server.json -服务器配置信息
先决条件
- 堆栈 (Haskell构建工具)
- GHC 9.2+(将由Stack自动安装)
快速开始
- 克隆并构建项目:
git clone
cd haskell-mcp-server
stack build- 运行服务器:
stack exec haskell-mcp-server-exe或者指定自定义端口:
stack exec haskell-mcp-server-exe -- 8080- 测试服务器:
服务器将监听 ws://127.0.0.1:3000 (或您指定的端口)用于WebSocket连接。
发展
项目结构
haskell-mcp-server/
├── app/
│ └── Main.hs # Application entry point
├── src/
│ └── MCP/
│ ├── Types.hs # MCP protocol types
│ ├── Server.hs # Core server implementation
│ └── Tools.hs # Tool definitions and handlers
├── test/
│ └── Spec.hs # Test suite
├── package.yaml # Package configuration
├── stack.yaml # Stack configuration
└── README.md添加自定义工具
要添加新工具,请创建 Tool 定义和处理程序:
myTool :: Tool
myTool = Tool
{ toolName = "my_tool"
, description = "Description of what the tool does"
, inputSchema = object
[ "type" .= ("object" :: Text)
, "properties" .= object
[ "param" .= object
[ "type" .= ("string" :: Text)
, "description" .= ("Parameter description" :: Text)
]
]
, "required" .= (["param"] :: [Text])
]
}
myToolHandler :: Value -> IO ToolResult
myToolHandler args = do
-- Your tool logic here
return $ ToolResult
[ ContentItem "text" "Tool result" ]
Nothing然后在中注册 Main.hs:
addTool server myTool myToolHandler添加自定义资源
同样,对于资源:
let myResource = Resource
{ resourceUri = "custom://my-resource"
, resourceName = "My Resource"
, resourceDescription = Just "Custom resource description"
, mimeType = Just "application/json"
}
addResource server myResource $ return $ object
[ "data" .= ("resource content" :: Text) ]测试
运行测试套件:
stack test生产大楼
创建优化的构建:
stack build --ghc-options="-O2"协议遵从
此服务器实现了模型上下文协议规范:
- 初始化:通过协议版本协商进行适当的握手
- 工具列表:通过动态工具发现
tools/list - 工具执行:通过以下方式安全执行工具
tools/call - 资源管理:资源列表和阅读通过
resources/list和resources/read - 错误处理:标准JSON-RPC错误响应
依赖项
关键依赖关系包括:
aeson-JSON序列化/反序列化websockets-WebSocket服务器实现stm-用于并发状态的软件事务存储器text-高效的文本处理containers-数据结构(地图等)
许可证
BSD3许可证-有关详细信息,请参阅许可证文件。
贡献
- 分叉存储库
- 创建要素分支(
git checkout -b feature/amazing-feature) - 进行更改
- 添加新功能的测试
- 确保所有测试通过(
stack test) - 提交您的更改(
git commit -am 'Add amazing feature') - 推到分支(
git push origin feature/amazing-feature) - 打开拉取请求
客户端使用示例
以下是如何使用WebSocket客户端连接到服务器:
JavaScript客户端示例
const ws = new WebSocket('ws://127.0.0.1:3000');
// Initialize the connection
ws.onopen = () => {
// Send initialize request
ws.send(JSON.stringify({
jsonrpc: "2.0",
method: "initialize",
params: {
protocolVersion: { major: 2024, minor: 11 },
capabilities: {
roots: { listChanged: true },
sampling: {}
},
clientInfo: {
name: "test-client",
version: "1.0.0"
}
},
id: 1
}));
};
ws.onmessage = (event) => {
const response = JSON.parse(event.data);
console.log('Received:', response);
// After initialization, send initialized notification
if (response.id === 1) {
ws.send(JSON.stringify({
jsonrpc: "2.0",
method: "initialized",
params: {}
}));
// List available tools
ws.send(JSON.stringify({
jsonrpc: "2.0",
method: "tools/list",
params: {},
id: 2
}));
// Call echo tool
ws.send(JSON.stringify({
jsonrpc: "2.0",
method: "tools/call",
params: {
name: "echo",
arguments: { message: "Hello, MCP!" }
},
id: 3
}));
}
};Python客户端示例
import asyncio
import websockets
import json
async def test_client():
uri = "ws://127.0.0.1:3000"
async with websockets.connect(uri) as websocket:
# Initialize
init_request = {
"jsonrpc": "2.0",
"method": "initialize",
"params": {
"protocolVersion": {"major": 2024, "minor": 11},
"capabilities": {
"roots": {"listChanged": True},
"sampling": {}
},
"clientInfo": {
"name": "python-test-client",
"version": "1.0.0"
}
},
"id": 1
}
await websocket.send(json.dumps(init_request))
response = await websocket.recv()
print("Init response:", json.loads(response))
# Send initialized notification
await websocket.send(json.dumps({
"jsonrpc": "2.0",
"method": "initialized",
"params": {}
}))
# List tools
await websocket.send(json.dumps({
"jsonrpc": "2.0",
"method": "tools/list",
"params": {},
"id": 2
}))
tools_response = await websocket.recv()
print("Tools:", json.loads(tools_response))
# Call calculator tool
await websocket.send(json.dumps({
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "calculate",
"arguments": {"expression": "2 + 3 * 4"}
},
"id": 3
}))
calc_response = await websocket.recv()
print("Calculation result:", json.loads(calc_response))
# Run the client
asyncio.run(test_client())配置
可以通过修改来配置服务器 defaultServer 功能在 MCP/Server.hs:
-- Custom server configuration
customServer :: IO MCPServer
customServer = do
server <- defaultServer
return server
{ serverInfo = ServerInfo
{ name = "my-custom-server"
, version = "1.0.0"
, protocolVersion = MCPVersion 2024 11
}
}故障排除
常见问题
- 端口已在使用中:启动服务器时更改端口号
stack exec haskell-mcp-server-exe -- 8080- 构建错误:确保您拥有最新的Stack版本
stack upgrade
stack clean
stack build- WebSocket连接被拒绝:检查服务器是否正在运行以及端口是否正确
调试模式
对于详细日志记录,您可以修改服务器以包含更多调试输出:
-- In MCP/Server.hs, add more putStrLn statements
handleMessage server req = do
putStrLn $ "Received method: " ++ T.unpack (method req)
-- ... rest of the function业绩说明
- 服务器使用STM(软件事务存储器)进行线程安全状态管理
- WebSocket连接使用轻量级Haskell线程并发处理
- 工具处理程序应设计为非阻塞,以获得最佳性能
api参考
核心方法
| 方法 | 说明 | 参数 |
|---|---|---|
initialize | 初始化MCP连接 | InitializeParams |
initialized | 初始化完成通知 | 无 |
tools/list | 列出可用工具 | 无 |
tools/call | 执行工具 | ToolCallArgs |
resources/list | 列出可用资源 | 无 |
resources/read | 阅读资源 | {uri: string} |
错误代码
| 代码 | 描述 |
|---|---|
| -32700 | 分析错误 |
| -32600 | 无效请求 |
| -32601 | 找不到方法 |
| -32602 | 参数无效 |
| -32603 | 内部错误 |
路线图
- \[\]添加对流媒体工具响应的支持
- \[\]实现资源订阅
- \[\]添加内置文件系统工具
- \[\]实现工具结果缓存
- \[\]添加配置文件支持
- \[\]性能优化
- \[\]Docker容器化
支持
对于问题、议题或贡献:
- 在GitHub上打开一个问题
- 检查 MCP规范 有关协议详细信息
- 查看Haskell文档以了解特定语言的问题
