# mcp-server A fully-featured Haskell library for building [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers. ## Features - **Complete MCP Implementation**: Supports MCP 2025-06-18 specification - **Type-Safe API**: Leverage Haskell's type system for robust MCP servers - **Multiple Abstractions**: Both low-level fine-grained control and high-level derived interfaces - **Template Haskell Support**: Automatic handler derivation from data types - **Multiple Transports**: STDIO and HTTP Streaming transport (MCP 2025-06-18 Streamable HTTP) ## Supported MCP Features - ✅ **Prompts**: User-controlled prompt templates with arguments - ✅ **Resources**: Application-controlled readable resources - ✅ **Tools**: Model-controlled callable functions - ✅ **Initialization Flow**: Complete protocol lifecycle with version negotiation - ✅ **Error Handling**: Comprehensive error types and JSON-RPC error responses ## Quick Start Add the library `mcp-server` to your cabal file: ```cabal build-depends: mcp-server ``` Create a simple module, such as this example below: ```haskell {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-} import MCP.Server import MCP.Server.Derive -- Define your data types data MyPrompt = Recipe { idea :: Text } | Shopping { items :: Text } data MyResource = Menu | Specials data MyTool = Search { query :: Text } | Order { item :: Text } -- Implement handlers handlePrompt :: MyPrompt -> IO Content handlePrompt (Recipe idea) = pure $ ContentText $ "Recipe for " <> idea handlePrompt (Shopping items) = pure $ ContentText $ "Shopping list: " <> items handleResource :: MyResource -> IO Content handleResource Menu = pure $ ContentText "Today's menu..." handleResource Specials = pure $ ContentText "Daily specials..." handleTool :: MyTool -> IO Content handleTool (Search query) = pure $ ContentText $ "Search results for " <> query handleTool (Order item) = pure $ ContentText $ "Ordered " <> item -- Derive handlers automatically main :: IO () main = runMcpServerStdio serverInfo handlers where serverInfo = McpServerInfo { serverName = "My MCP Server" , serverVersion = "1.0.0" , serverInstructions = "A sample MCP server" } handlers = McpServerHandlers { prompts = Just $(derivePromptHandler ''MyPrompt 'handlePrompt) , resources = Just $(deriveResourceHandler ''MyResource 'handleResource) , tools = Just $(deriveToolHandler ''MyTool 'handleTool) } ``` ### Advanced Template Haskell Features #### Automatic Naming Conventions Constructor names are automatically converted to snake_case for MCP names: ```haskell data MyTool = GetValue | SetValue | SearchItems -- Becomes: "get_value", "set_value", "search_items" ``` #### Automatic Type Conversion The derivation system automatically converts Text arguments to appropriate Haskell types: ```haskell data MyTool = Calculate { number :: Int, factor :: Double, enabled :: Bool } -- Text "42" -> Int 42 -- Text "3.14" -> Double 3.14 -- Text "true" -> Bool True ``` Supported conversions: `Int`, `Integer`, `Double`, `Float`, `Bool`, and `Text` (no conversion). #### Nested Parameter Types You can nest parameter types with automatic unwrapping: ```haskell -- Parameter record types data GetValueParams = GetValueParams { _gvpKey :: Text } data SetValueParams = SetValueParams { _svpKey :: Text, _svpValue :: Text } -- Main tool type data SimpleTool = GetValue GetValueParams | SetValue SetValueParams deriving (Show, Eq) ``` The Template Haskell derivation recursively unwraps single-parameter constructors until it reaches a record type, then extracts all fields for the MCP schema. #### Resource URI Generation Resources automatically get `resource://` URIs based on constructor names: ```haskell data MyResource = Menu | Specials -- Generates: "resource://menu", "resource://specials" ``` #### Unsupported Patterns We do not support positional (unnamed) parameters: ```haskell -- ❌ This won't work - no field names data SimpleTool = GetValue Int | SetValue Int Text ``` All parameter types must ultimately resolve to records with named fields to generate proper MCP schemas. ## Custom Descriptions You can provide custom descriptions for constructors and fields using the `*WithDescription` variants: ```haskell -- Define descriptions for constructors and fields descriptions :: [(String, String)] descriptions = [ ("Recipe", "Generate a recipe for a specific dish") -- Constructor description , ("Search", "Search our menu database") -- Constructor description , ("idea", "The dish you want a recipe for") -- Field description , ("query", "Search terms to find menu items") -- Field description ] -- Use in derivation handlers = McpServerHandlers { prompts = Just $(derivePromptHandlerWithDescription ''MyPrompt 'handlePrompt descriptions) , tools = Just $(deriveToolHandlerWithDescription ''MyTool 'handleTool descriptions) , resources = Just $(deriveResourceHandlerWithDescription ''MyResource 'handleResource descriptions) } ``` ## Manual Handler Implementation For fine-grained control, implement handlers manually: ```haskell import MCP.Server -- Manual handler implementation promptListHandler :: IO [PromptDefinition] promptGetHandler :: PromptName -> [(ArgumentName, ArgumentValue)] -> IO (Either Error Content) -- ... implement your custom logic main :: IO () main = runMcpServerStdio serverInfo handlers where handlers = McpServerHandlers { prompts = Just (promptListHandler, promptGetHandler) , resources = Nothing -- Not supported , tools = Nothing -- Not supported } ``` ## HTTP Transport (NEW!) The library now supports MCP 2025-06-18 Streamable HTTP transport: ```haskell import MCP.Server.Transport.Http -- Simple HTTP server (localhost:3000/mcp) main = runMcpServerHttp serverInfo handlers -- Custom configuration main = runMcpServerHttpWithConfig customConfig serverInfo handlers where customConfig = HttpConfig { httpPort = 8080 , httpHost = "0.0.0.0" , httpEndpoint = "/api/mcp" , httpVerbose = True -- Enable detailed logging } ``` **Features:** - CORS enabled for web clients - GET `/mcp` for server discovery - POST `/mcp` for JSON-RPC messages - Full MCP 2025-06-18 compliance ## Examples The library includes several examples: - **`examples/Simple/`**: Basic key-value store using Template Haskell derivation (STDIO) - **`examples/Complete/`**: Full-featured example with prompts, resources, and tools (STDIO) - **`examples/HttpSimple/`**: HTTP version of the simple key-value store ## Docker Usage I like to build and publish my MCP servers to Docker - which means that it's much easier to configure assistants such as Claude Desktop to run them. ```bash # Build the image docker build -t haskell-mcp-server . # Run different examples docker run -i --entrypoint="/usr/local/bin/haskell-mcp-server" haskell-mcp-server ``` And then configure Claude by editing `claude_desktop_config.json`: ```json { "mcpServers": { "haskell-mcp-server-example": { "command": "docker", "args": [ "run", "-i", "--entrypoint=/usr/local/bin/haskell-mcp-server", "haskell-mcp-server" ] } } } ``` ## Documentation - [MCP Specification](https://modelcontextprotocol.io/specification/2025-06-18/) - [API Documentation](https://hackage.haskell.org/package/mcp-server) - [Examples](examples/) ## Contributing Contributions are welcome! Please see the issue tracker for open issues and feature requests. ## Disclaimer - AI Assistance I am not sure whether there is any stigma associated with this but Claude helped me write a lot of this library. I started with a very specific specification of what I wanted to achieve and worked shoulder-to-shoulder with Claude to implement and refactor the library until I was happy with it. A few of the features such as the Derive functions are a little out of my comfort zone to have manually written, so I appreciated having an expert guide me here - however I do suspect that this implementation may be sub-par and I do intend to refactor and rewrite large pieces of this through regular maintenance. ## License BSD-3-Clause