1. Conduid
  2. AI
  3. FsMcp
MCP server · AI

FsMcp

FsMcp is an idiomatic F# toolkit for building Model Context Protocol (MCP) servers and clients with type safety, computation expressions, and zero boilerplate.

27Low

Scored 5 hours ago · breakdown

About FsMcp

FsMcp is an MCP server published by Neftedollar in the AI category: fsMcp is an idiomatic F# toolkit for building Model Context Protocol (MCP) servers and clients with type safety, computation expressions, and zero boilerplate.

The repository has 18 stars and 3 forks, with the last commit 2 months ago. Recent commit activity is the strongest signal that reported issues will get attention.

Install

From source
git clone https://github.com/Neftedollar/FsMcp

This server has no ConduID identity, so agent calls to it are not receipted. Pin the version you install and review the source before granting it credentials.

Ask AI

✦

Ask AI about FsMcp

Powered by Claude

Ask about FsMcp — answers draw on its README.

Security checks

  • ✓README presentThe repository ships documentation.

5 checks have not been run yet.

README

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.

CI NuGet Docs

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 block
  • TypedTool.define<'T> — F# record as input, JSON Schema auto-generated via TypeShape
  • Result<'T, McpError> — no exceptions in expected paths, typed errors everywhere
  • Smart constructors — ToolName.create validates at construction, not at runtime
  • Composable middleware — logging, validation, telemetry via Middleware.pipeline
  • 322 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.define with IAsyncEnumerable<Content>
  • Notifications — ContextualTool.define with progress + log callbacks
  • Validation middleware — auto-validates args against schema before handler
  • Telemetry — Telemetry.tracing() (Activity/OTel) + MetricsCollector
  • Hot reload — DynamicServer.addTool / removeTool at runtime
  • Error handling — FsToolkit.ErrorHandling integration via FsMcp.TaskApi

Build & Test

dotnet build       # 7 packages
dotnet test        # 322 tests (Expecto + FsCheck)

Runtime tuning for stdio servers

By default .NET runs the Server GC, which is throughput-optimized and does not proactively return committed heap pages to the OS. For an idle stdio MCP server this can look like a memory leak — RSS grows during a session and stays elevated even when the server is quiet. The runtime releases the memory immediately once the OS signals memory pressure, confirming it was commit-grow, not a genuine leak.

Set these environment variables to reduce idle RSS:

DOTNET_gcServer=0      # Workstation GC — returns memory at idle
DOTNET_gcConcurrent=1  # Concurrent collection — shorter pauses

See docs/runtime-tuning.md for the full explanation, MCP client config examples (Claude Code, Codex), a runtimeconfig.template.json snippet for redistributable tools, and a five-minute diagnostic recipe to distinguish commit-grow from an actual leak.

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

  1. Wrap, don't reimplement — protocol concerns stay in Microsoft SDK
  2. Idiomatic F# — DUs, Result, CEs, pipe-friendly
  3. Type safety — private constructors, no obj in public API
  4. Test-first — Expecto + FsCheck on every function
  5. Composable — middleware, function handlers, no inheritance

Contributing

See CONTRIBUTING.md. Issues and PRs welcome.

License

MIT

README copy captured when this server was indexed; it may lag the repository. The original is authoritative.

Questions

About FsMcp

How do I install FsMcp?

No package has been published for this server, so install it from source: clone the repository and follow its README.

Is FsMcp safe to use with an AI agent?

Its trust score is 27 out of 100 (low). It passes 1 of the 1 static security checks run so far. It has no ConduID identity yet, so agent calls to it are not receipted.

Is FsMcp still maintained?

Yes — the last commit was 2 months ago. The repository has 18 stars and 3 forks.