An F# MCP Client

October 30, 2025    FSharp Podman DotNet AI Development

An F# MCP Client Example

I’m learning about MCP (Model Context Protocol) and Microsoft Agent Framework. I’m also a fan of F#, so I’m using this to practice my F# reading and letting Copilot help me write some code. I’m using .Net 10 rc2 (release will be in November as Microsoft Build) with VS 2026. To top it off, I’m using Podman to containerize the application. That’s a lot of fun technologies to put together.

I want to make it easier to figure out the Cost Basis for reporting Capital Gains on Bitcoin transactions. I downloaded a CSV file with historical Bitcoin prices and embedding that in the container for the data source.

The Code

The code is in my GitHub repo and I’m continuing to work on it.

Program.fs

This was a quick AI translation from the dotnet new install Microsoft.Extensions.AI.Templates, dotnet new mcp-server -n BitcoinHistoricalPriceMcp template.

open System

open Microsoft.Extensions.DependencyInjection
open Microsoft.Extensions.Hosting
open Microsoft.Extensions.Logging
open BitcoinHistoricalPriceMcp.Mcp

[<EntryPoint>]
let main argv =
    let builder = Host.CreateApplicationBuilder(argv)

    // Configure all logs to go to stderr (stdout is used for the MCP protocol messages).
    builder.Logging.AddConsole(fun o -> o.LogToStandardErrorThreshold <- LogLevel.Trace) |> ignore

    // Add the MCP services: the transport to use (stdio) and the tools to register.
    builder.Services
        .AddMcpServer()
        .WithStdioServerTransport()
        .WithTools<HistoricalBitcoinDataMcp>() |> ignore

    // Run the host (synchronously block until shutdown).
    builder.Build().RunAsync().GetAwaiter().GetResult()
    0

Using the Bitcoin Historical Price MCP Client in Visual Studio with Copilot

In the HistoricalPrice directory, build and run the MCP with: podman build -t bitcoin-historical-mcp . if it hangs on dotnet restore, try: podman build -t bitcoin-historical-mcp --network host .

add to the .mcp.json file:

{
  "inputs": [],
  "servers": {
    "BitcoinHistoricalPriceMcp": {
      "type": "stdio",
      "command": "podman",
      "args": [ "run", "--rm", "-i", "bitcoin-historical-mcp" ]
    }
  }
}

I did not have to do podman run, VS fired up it’s own container with a random image name.

run for MCP tool manually with: podman run --rm -it --name bitcoin-historical-mcp bitcoin-historical-mcp

Adding a new input didn’t show up? - I missed something simple

After adding the MCP tool in Visual Studio, running it then adding the new readHistoricalDataForDateRange method it didn’t show up as an option.

  1. add function
  2. add function to HistoricalBitcoinDataMcp as
 [<McpServerTool>]
 [<Description("Reads historical Bitcoin price data from a CSV file for the given date range.")>]
 member _.ReadHistoricalData(startDate: DateTime, endDate: DateTime) =
     // path in the container/image
     let defaultCsv = "./data/BitcoinHistoricalData.csv"
     HistoricalPriceReader.readHistoricalDataForDateRange(defaultCsv, startDate, endDate)
     |> Array.toList
  1. update .mcp/server.json
  2. rebuild image
  3. run container
  4. Restart MCP in Visual Studio a. Not there
  5. Restart Visual Studio a. not there
  6. Ask Copilot, it pointed out the obvious: a. I had 2 functions both named ReadHistoricalData b. Renamed one to ReadHistoricalDataForDateRange
  7. rebuild image
  8. run container
  9. It’s working

It was my mistake. I hope you can avoid that pitfall now.

via GIFER

Conclusion

After working through this, I found a F# orientated example and will incorporate some of those ideas in my next iteration.

This was a good excercise in using F#, .Net 10, Podman, and MCP. I plan to use Microsoft Agent Framework to create agents to use this MCP and share that in another post.