FsMcp
   
type GreetArgs = { name: string; greeting: string option }
let server = mcpServer {
name "MyServer"
version "1.0.0"
tool (TypedTool.define "greet" "Greets a person" (fun args -> task {
let greeting = args.greeting |> Option.defaultValue "Hello"
return Ok [ Content.text $"{greeting}, {args.name}!" ]
}) |> unwrapResult)
useStdio
}
Server.run server |> fun t -> t.GetAwaiter().GetResult()
// Input schema auto-generated: name=required, greeting=optional安装
dotnet add package FsMcp.Server # server builder + stdio transport
dotnet add package FsMcp.Client # typed client wrapper
dotnet add package FsMcp.Testing # test helpers + FsCheck generators
dotnet add package FsMcp.TaskApi # FsToolkit.ErrorHandling pipeline
dotnet add package FsMcp.Server.Http # HTTP/SSE transport (opt-in ASP.NET)
dotnet add package FsMcp.Sampling # LLM sampling from server tools为什么是FsMcp?
mcpServer { }CE --在单个块中声明工具、资源和提示TypedTool.define--F#记录作为输入,JSON模式通过TypeShape自动生成Result--预期路径中没有异常,到处都是键入错误- 智能建设者 —
ToolName.create在构建时验证,而不是在运行时验证 - 可组合中间件 --通过以下方式记录、验证、遥测
Middleware.pipeline - 322测试 --对每种域类型进行Expecto+FsCheck属性测试
快速开始
带键入工具的服务器
open FsMcp.Core
open FsMcp.Core.Validation
open FsMcp.Server
type CalcArgs = { a: float; b: float }
let server = mcpServer {
name "Calculator"
version "1.0.0"
tool (TypedTool.define "add" "Add two numbers" (fun args -> task {
return Ok [ Content.text $"{args.a + args.b}" ]
}) |> unwrapResult)
tool (TypedTool.define "divide" "Divide a by b" (fun args -> task {
if args.b = 0.0 then return Error (TransportError "Division by zero")
else return Ok [ Content.text $"{args.a / args.b}" ]
}) |> unwrapResult)
useStdio
}
Server.run server |> fun t -> t.GetAwaiter().GetResult()HTTP传输
dotnet add package FsMcp.Server.Httpopen FsMcp.Server.Http
HttpServer.run server (Some "/mcp") "http://localhost:3001"
|> fun t -> t.GetAwaiter().GetResult()客户
open FsMcp.Core.Validation
open FsMcp.Client
let demo () = task {
let config = {
Transport = ClientTransport.stdio "dotnet" ["run"; "--project"; "../Calculator"]
Name = "TestClient"
ShutdownTimeout = None
}
let! client = McpClient.connect config
let! tools = McpClient.listTools client
let toolName = ToolName.create "add" |> unwrapResult
let args = Map.ofList [
"a", System.Text.Json.JsonDocument.Parse("10").RootElement
"b", System.Text.Json.JsonDocument.Parse("20").RootElement
]
let! result = McpClient.callTool client toolName args
// result : Result
}测试
open FsMcp.Testing
// Direct handler invocation — no network, no process spawning
let result =
TestServer.callTool serverConfig "add"
(Map.ofList ["a", jsonEl 10; "b", jsonEl 20])
|> Async.AwaitTask |> Async.RunSynchronously
result |> Expect.mcpHasTextContent "30" "addition works"建筑
┌─────────────────────────────────────────────────────────────────┐
│ Your F# Code │
│ mcpServer { tool ...; resource ...; prompt ... } │
├──────────────┬──────────────────────────────┬───────────────────┤
│ FsMcp.Server │ FsMcp.Core │ FsMcp.Client │
│ │ │ │
│ CE builder Types (DUs, records) │ Typed wrapper │
│ TypedHandlers Validation (smart ctors) │ Async module │
│ Middleware Serialization (JSON) │ │
│ Streaming Interop (internal) │ │
│ Telemetry │ │
├──────────────┴──────────────────────────────┴───────────────────┤
│ Microsoft ModelContextProtocol SDK │
├─────────────────────────────────────────────────────────────────┤
│ .NET 10 Runtime │
└─────────────────────────────────────────────────────────────────┘包裹
| 包装 | 它的作用 |
|---|---|
| FsMcp。核心 | 域类型、智能构造函数、JSON序列化 |
| FsMcp。服务器 | mcpServer { } CE、类型化处理程序、中间件、stdio传输 |
| FsMcp。服务器。超文本传输协议 | 通过ASP进行HTTP/SSE传输。NET Core(选择加入) |
| FsMcp。客户 | 键入客户端 Result |
| FsMcp。测试 | TestServer.callTool, Expect.mcp*,FsCheck发电机 |
| FsMcp。TaskApi | taskResult { } 通过FsToolkit的管道。错误处理 |
| FsMcp。采样 | 通过MCP采样进行服务器端LLM调用 |
特性
- 键入工具处理程序 —
TypedTool.define使用TypeShape驱动的JSON模式+缓存 - 嵌套CE —
mcpTool { toolName "..."; typedHandler ... } - 流媒体工具 —
StreamingTool.define和IAsyncEnumerable - 通知 —
ContextualTool.define带有进度+日志回调 - 验证中间件 --在处理程序之前根据架构自动验证参数
- 遥测 —
Telemetry.tracing()(活动/OTel)+MetricsCollector - 热重载 —
DynamicServer.addTool/removeTool在运行时 - 错误处理 —
FsToolkit.ErrorHandling整合通过FsMcp.TaskApi
构建与测试
dotnet build # 7 packages
dotnet test # 322 tests (Expecto + FsCheck)stdio服务器的运行时调优
默认情况下。NET运行服务器GC,它是吞吐量优化的,不会主动将提交的堆页面返回给操作系统。对于空闲的stdio MCP服务器,这可能看起来像是内存泄漏——RSS在会话期间会增长,即使服务器安静,也会保持高位。一旦操作系统发出内存压力信号,运行时会立即释放内存,确认这是提交增长,而不是真正的泄漏。
设置这些环境变量以减少空闲RSS:
DOTNET_gcServer=0 # Workstation GC — returns memory at idle
DOTNET_gcConcurrent=1 # Concurrent collection — shorter pauses看 docs/运行时调优.md 如需完整解释,请参阅MCP客户端配置示例(Claude Code、Codex) runtimeconfig.template.json 可再分发工具的代码片段,以及区分提交增长和实际泄漏的五分钟诊断配方。
示例
看 examples/ 对于可运行的MCP服务器:
- EchoServer --echo+反向工具、资源、提示
- 计算器 --加/减/乘/除
- 文件服务器 --read_file、list_directory、file_info
设计原则
- 包裹,不要重新实施 --协议问题仍存在于Microsoft SDK中
- 成语F# --DU、结果、CE、管道友好型
- 类型安全性 --私人构造函数,否
obj公开API - 先测试 --Expecto+Fs检查每个功能
- 可组合 --中间件、函数处理程序、无继承
贡献
看 贡献.md欢迎提出问题和公关。
