FsMcp
mcp
Warn
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 8 GitHub stars
Code Pass
- Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
Purpose
This toolkit provides an idiomatic F# wrapper around the official Microsoft .NET SDK for building Model Context Protocol (MCP) servers and clients, focusing on type safety and minimal boilerplate.
Security Assessment
Overall risk: Low. The static code scan of 12 files found no dangerous patterns, and the tool requests no inherently dangerous permissions. It relies on standard stdio and optional HTTP/SSE transports for communication rather than executing arbitrary shell commands. No hardcoded secrets, tokens, or API keys were identified in the codebase. Because it is a framework designed for developers to build their own servers, the ultimate security depends heavily on how you implement and configure your specific tools.
Quality Assessment
The project demonstrates strong software engineering practices despite its current low visibility. It boasts 306 automated tests using Expecto and FsCheck, employs robust error handling via F# discriminated unions, and provides comprehensive NuGet packages and documentation. The repository is actively maintained (last pushed today), uses a permissive MIT license, and correctly wraps the official Microsoft SDK rather than reinventing the wheel. However, community trust and adoption are currently very limited, with only 8 GitHub stars, meaning it is still a young project.
Verdict
Safe to use, though keep in mind it is a relatively new and untested community project with low adoption.
This toolkit provides an idiomatic F# wrapper around the official Microsoft .NET SDK for building Model Context Protocol (MCP) servers and clients, focusing on type safety and minimal boilerplate.
Security Assessment
Overall risk: Low. The static code scan of 12 files found no dangerous patterns, and the tool requests no inherently dangerous permissions. It relies on standard stdio and optional HTTP/SSE transports for communication rather than executing arbitrary shell commands. No hardcoded secrets, tokens, or API keys were identified in the codebase. Because it is a framework designed for developers to build their own servers, the ultimate security depends heavily on how you implement and configure your specific tools.
Quality Assessment
The project demonstrates strong software engineering practices despite its current low visibility. It boasts 306 automated tests using Expecto and FsCheck, employs robust error handling via F# discriminated unions, and provides comprehensive NuGet packages and documentation. The repository is actively maintained (last pushed today), uses a permissive MIT license, and correctly wraps the official Microsoft SDK rather than reinventing the wheel. However, community trust and adoption are currently very limited, with only 8 GitHub stars, meaning it is still a young project.
Verdict
Safe to use, though keep in mind it is a relatively new and untested community project with low adoption.
FsMcp is an idiomatic F# toolkit for building Model Context Protocol (MCP) servers and clients with type safety, computation expressions, and zero boilerplate.
README.md
FsMcp
FsMcp is an idiomatic F# toolkit for building Model Context Protocol (MCP) servers and clients. It wraps the official Microsoft ModelContextProtocol .NET SDK with computation expressions, typed tool handlers, Result-based error handling, and composable middleware — so you can build MCP servers in F# with type safety and zero boilerplate.
type GreetArgs = { name: string; greeting: string option }
let server = mcpServer {
name "MyServer"
version "1.0.0"
tool (TypedTool.define<GreetArgs> "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
Install
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
Why FsMcp?
mcpServer { }CE — declare tools, resources, prompts in a single blockTypedTool.define<'T>— F# record as input, JSON Schema auto-generated via TypeShapeResult<'T, McpError>— no exceptions in expected paths, typed errors everywhere- Smart constructors —
ToolName.createvalidates at construction, not at runtime - Composable middleware — logging, validation, telemetry via
Middleware.pipeline - 306 tests — Expecto + FsCheck property tests on every domain type
Quick Start
Server with typed tools
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<CalcArgs> "add" "Add two numbers" (fun args -> task {
return Ok [ Content.text $"{args.a + args.b}" ]
}) |> unwrapResult)
tool (TypedTool.define<CalcArgs> "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 transport
dotnet add package FsMcp.Server.Http
open FsMcp.Server.Http
HttpServer.run server (Some "/mcp") "http://localhost:3001"
|> fun t -> t.GetAwaiter().GetResult()
Client
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<Content list, McpError>
}
Testing
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"
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────────────┘
Packages
| Package | What it does |
|---|---|
| FsMcp.Core | Domain types, smart constructors, JSON serialization |
| FsMcp.Server | mcpServer { } CE, typed handlers, middleware, stdio transport |
| FsMcp.Server.Http | HTTP/SSE transport via ASP.NET Core (opt-in) |
| FsMcp.Client | Typed client with Result<'T, McpError> |
| FsMcp.Testing | TestServer.callTool, Expect.mcp*, FsCheck generators |
| FsMcp.TaskApi | taskResult { } pipeline via FsToolkit.ErrorHandling |
| FsMcp.Sampling | Server-side LLM invocation via MCP sampling |
Features
- Typed tool handlers —
TypedTool.define<'T>with TypeShape-powered JSON Schema + caching - Nested CE —
mcpTool { toolName "..."; typedHandler ... } - Streaming tools —
StreamingTool.definewithIAsyncEnumerable<Content> - Notifications —
ContextualTool.definewith progress + log callbacks - Validation middleware — auto-validates args against schema before handler
- Telemetry —
Telemetry.tracing()(Activity/OTel) +MetricsCollector - Hot reload —
DynamicServer.addTool/removeToolat runtime - Error handling —
FsToolkit.ErrorHandlingintegration viaFsMcp.TaskApi
Build & Test
dotnet build # 7 packages
dotnet test # 306 tests (Expecto + FsCheck)
Examples
See examples/ for runnable MCP servers:
- EchoServer — echo + reverse tools, resource, prompt
- Calculator — add/subtract/multiply/divide
- FileServer — read_file, list_directory, file_info
Design Principles
- Wrap, don't reimplement — protocol concerns stay in Microsoft SDK
- Idiomatic F# — DUs, Result, CEs, pipe-friendly
- Type safety — private constructors, no
objin public API - Test-first — Expecto + FsCheck on every function
- Composable — middleware, function handlers, no inheritance
Contributing
See CONTRIBUTING.md. Issues and PRs welcome.
License
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found