FsMcp.Client 2.0.0

dotnet add package FsMcp.Client --version 2.0.0
                    
NuGet\Install-Package FsMcp.Client -Version 2.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FsMcp.Client" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FsMcp.Client" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="FsMcp.Client" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FsMcp.Client --version 2.0.0
                    
#r "nuget: FsMcp.Client, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FsMcp.Client@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FsMcp.Client&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=FsMcp.Client&version=2.0.0
                    
Install as a Cake Tool

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 cancellable handlers, Result-based error handling, secure ASP.NET Core composition, and enterprise-managed authorization.

CI NuGet License: MIT 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 cancellationToken -> task {
        cancellationToken.ThrowIfCancellationRequested()
        let greeting = args.greeting |> Option.defaultValue "Hello"
        return Ok [ Content.text $"{greeting}, {args.name}!" ]
    }) |> unwrapResult)
}

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  # Streamable HTTP transport (opt-in ASP.NET)
dotnet add package FsMcp.Sampling   # sampling types and explicit test helpers

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 constructorsToolName.create validates at construction, not at runtime
  • Cancellable handlers — the protocol request token reaches F# tool, resource, and prompt handlers
  • Secure hosting composition — caller-owned DI, typed SDK filters, and ASP.NET authorization
  • Enterprise-managed authorization — typed ID-JAG client flow with bounded refresh/retry
  • Broad test suite — Expecto + FsCheck properties plus real wire/transport tests

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 cancellationToken -> task {
        cancellationToken.ThrowIfCancellationRequested()
        return Ok [ Content.text $"{args.a + args.b}" ]
    }) |> unwrapResult)

    tool (TypedTool.define<CalcArgs> "divide" "Divide a by b" (fun args cancellationToken -> task {
        cancellationToken.ThrowIfCancellationRequested()
        if args.b = 0.0 then return Error (TransportError "Division by zero")
        else return Ok [ Content.text $"{args.a / args.b}" ]
    }) |> unwrapResult)
}

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>
}

Sampling status

SamplingTool.define is deliberately fail-closed in 2.0: its 1.x transport path never reached the connected client. Use the SDK request-scoped sampling primitive directly until FsMcp exposes a correctly wired replacement.

Enterprise-managed authorization

FsMcp.Client 2.0 includes an F#-first wrapper for the stable MCP ID-JAG enterprise authorization profile: validated opaque configuration, bounded single-flight token caching, cancellation, redacted failures, same-origin Bearer injection, and one controlled refresh/retry after 401.

See the Enterprise-Managed Authorization guide for the client flow and the required ASP.NET Core resource-server protection.

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      │
│ DI composition Serialization (JSON)          │ EMA / ID-JAG      │
│ SDK filters    Interop (internal)           │                   │
├──────────────┴──────────────────────────────┴───────────────────┤
│              Microsoft ModelContextProtocol SDK                  │
├─────────────────────────────────────────────────────────────────┤
│                      .NET 10 Runtime                            │
└─────────────────────────────────────────────────────────────────┘

Packages

Package What it does
FsMcp.Core Domain types, smart constructors, JSON serialization
FsMcp.Server mcpServer { } CE, cancellable typed handlers, stdio, DI composition
FsMcp.Server.Http Streamable HTTP transport and ASP.NET Core composition
FsMcp.Client Typed client plus enterprise-managed authorization
FsMcp.Testing TestServer.callTool, Expect.mcp*, FsCheck generators
FsMcp.TaskApi taskResult { } pipeline via FsToolkit.ErrorHandling
FsMcp.Sampling Sampling domain types and explicit test helpers; legacy transport wiring fails closed

Features

  • Typed tool handlersTypedTool.define<'T> with TypeShape-powered JSON Schema + caching
  • Nested CEmcpTool { toolName "..."; typedHandler ... }
  • Streaming toolsStreamingTool.define with IAsyncEnumerable<Content>
  • Explicit request cancellation — every primary handler receives the SDK token
  • Secure hosting composition — register FsMcp into caller-owned SDK/ASP.NET builders
  • Enterprise authorization — stable ID-JAG client profile with bounded security defaults
  • Error handlingFsToolkit.ErrorHandling integration via FsMcp.TaskApi

Build & Test

dotnet build       # 7 packages
dotnet test        # Expecto + FsCheck + real transport tests

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. Honest boundaries — no authentication, transport, or cancellation behavior is implied unless it is wired

Contributing

See CONTRIBUTING.md. Issues and PRs welcome.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on FsMcp.Client:

Package Downloads
FsMcp.TaskApi

Ergonomic task-based pipeline API for FsMcp, powered by FsToolkit.ErrorHandling

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 746 8/15/2026
1.2.2 227 8/1/2026
1.2.1 125 8/1/2026
1.2.0 119 8/1/2026
1.1.1 285 5/6/2026
1.1.0 114 5/6/2026
1.0.1 298 4/3/2026
1.0.0 129 4/3/2026