mcp-server
A fully-featured Haskell library for building Model Context Protocol (MCP) servers.
Features
- Complete MCP Implementation: Supports MCP 2025-03-26 specification (with backward compatibility for 2024-11-05)
- 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-03-26 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:
build-depends:
mcp-server
Create a simple module, such as this example below:
import MCP.Server
import MCP.Server.Derive
data MyPrompt = Recipe { idea :: Text } | Shopping { items :: Text }
data MyResource = Menu | Specials
data MyTool = Search { query :: Text } | Order { item :: Text }
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
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)
}
Nestable data types
You can also nest your data types, but MUST always end in a record with named fields:
data GetValueParams = GetValueParams { _gvpKey :: Text }
data SetValueParams = SetValueParams { _svpKey :: Text, _svpValue :: Text }
data SimpleTool
= GetValue GetValueParams
| SetValue SetValueParams
deriving (Show, Eq)
Which is probably nicer for using things like Lenses etc. However we do not support positional (and unnamed) parameters such as:
data SimpleTool
= GetValue Int
| SetValue Int Text
deriving (Show, Eq)
Because we don't want to be generating names when returning details in MCP definitions.
Custom Descriptions
You can provide custom descriptions for constructors and fields using the *WithDescription
variants:
descriptions :: [(String, String)]
descriptions =
[ ("Recipe", "Generate a recipe for a specific dish")
, ("Search", "Search our menu database")
, ("idea", "The dish you want a recipe for")
, ("query", "Search terms to find menu items")
]
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:
import MCP.Server
promptListHandler :: IO [PromptDefinition]
promptGetHandler :: PromptName -> [(ArgumentName, ArgumentValue)] -> IO (Either Error Content)
main :: IO ()
main = runMcpServerStdio serverInfo handlers
where
handlers = McpServerHandlers
{ prompts = Just (promptListHandler, promptGetHandler)
, resources = Nothing
, tools = Nothing
}
HTTP Transport (NEW!)
The library now supports MCP 2025-03-26 Streamable HTTP transport:
import MCP.Server.Transport.Http
main = runMcpServerHttp serverInfo handlers
main = runMcpServerHttpWithConfig customConfig serverInfo handlers
where
customConfig = HttpConfig
{ httpPort = 8080
, httpHost = "0.0.0.0"
, httpEndpoint = "/api/mcp"
, httpVerbose = True
}
Features:
- JSON-RPC batching support
- CORS enabled for web clients
- GET
/mcp
for server discovery
- POST
/mcp
for JSON-RPC messages
- Full MCP 2025-03-26 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.
docker build -t haskell-mcp-server .
docker run -i --entrypoint="/usr/local/bin/haskell-mcp-server" haskell-mcp-server
And then configure Claude by editing claude_desktop_config.json
:
{
"mcpServers": {
"haskell-mcp-server-example": {
"command": "docker",
"args": [
"run",
"-i",
"--entrypoint=/usr/local/bin/haskell-mcp-server",
"haskell-mcp-server"
]
}
}
}
Documentation
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