A complete implementation of the Model Context Protocol (MCP) for Haskell.
Available on Hackage
This project provides a type-safe, comprehensive implementation of the Model Context Protocol in Haskell. MCP is an open protocol that standardizes how applications provide context to Large Language Models (LLMs), enabling AI models to securely connect to data sources and tools.
- Latest Protocol Support: Implements MCP protocol version 2025-06-18 with full compatibility
- Complete MCP Protocol Implementation: All MCP message types, requests, responses, and notifications
- Type-Safe Design: Full Haskell type system integration with automatic JSON serialization
- Multiple Transport Options: Both StdIO and HTTP transport support
- MCP Transport Compliance: HTTP implementation follows the official MCP transport specification
- Production-Ready HTTP Server: Configurable OAuth, timing, and security parameters
- Extensible Server Interface: Clean typeclass-based API for implementing custom servers
- Working Example Server: Demonstrates basic MCP functionality
The implementation is organized into five main modules:
- Core MCP data types (Content, Resource, Tool, Prompt, etc.)
- Automatic JSON serialization/deserialization via Aeson
- Type-safe mapping of the complete MCP schema
- JSON-RPC message wrappers
- All client and server request/response types
- Notification types for bidirectional communication
- Union types for organizing related messages
- Core server infrastructure with
MCPServerM
monad stack MCPServer
typeclass for implementing custom servers- Shared types and utilities for both transport methods
- StdIO transport implementation
- JSON-RPC communication over stdin/stdout
- Suitable for process-based MCP clients
- HTTP transport implementation following MCP specification
- RESTful JSON-RPC API at
/mcp
endpoint - Built with Servant and Warp for production use
- OAuth 2.0 authentication support (optional)
cabal build
StdIO Mode (default):
cabal run mcp
The server will start and listen for MCP messages on stdin, responding on stdout.
HTTP Mode: Create a simple HTTP server runner:
{-# LANGUAGE OverloadedStrings #-}
import MCP.Server.HTTP
import MCP.Types
main :: IO ()
main = do
let serverInfo = Implementation "mcp-haskell-http" "0.1.0"
let capabilities = ServerCapabilities
{ resources = Just $ ResourcesCapability Nothing Nothing
, tools = Just $ ToolsCapability Nothing
, prompts = Just $ PromptsCapability Nothing
, completions = Nothing
, logging = Nothing
, experimental = Nothing
}
let config = HTTPServerConfig
{ httpPort = 8080
, httpBaseUrl = "http://localhost:8080" -- Configure base URL
, httpServerInfo = serverInfo
, httpCapabilities = capabilities
, httpEnableLogging = False
, httpOAuthConfig = Nothing -- No OAuth
, httpJWK = Nothing -- Auto-generated
, httpProtocolVersion = "2025-06-18" -- MCP protocol version
}
runServerHTTP config
Then compile and run:
cabal build mcp-http
cabal run mcp-http
Or compile directly:
ghc -o mcp-http MyHTTPServer.hs
./mcp-http
The HTTP server will start on port 8080 with the MCP endpoint available at POST /mcp
.
The HTTP transport supports MCP-compliant OAuth 2.1 authentication with mandatory PKCE:
MCP OAuth Requirements:
- PKCE Required: All OAuth flows MUST use PKCE (Proof Key for Code Exchange)
- HTTPS Required: OAuth endpoints must use HTTPS in production
- Grant Types: Supports Authorization Code (user flows) and Client Credentials (app-to-app)
- Token Format: Bearer tokens in Authorization header only (never in query strings)
OAuth Endpoints:
- Metadata Discovery:
/.well-known/oauth-authorization-server
- Returns OAuth server metadata - Dynamic Registration:
/register
- Allows clients to register dynamically - Authorization:
/authorize
- Initiates authorization flow with PKCE - Token Exchange:
/token
- Exchanges authorization code for access token
Token Format: The server generates proper JWT tokens using servant-auth-server, preventing authentication loops that can occur with simple UUID-based tokens.
Complete OAuth Flow:
-
Discovery: Client discovers OAuth metadata
curl http://localhost:8080/.well-known/oauth-authorization-server
-
Registration: Client registers dynamically
curl -X POST http://localhost:8080/register \ -H "Content-Type: application/json" \ -d '{ "client_name": "My MCP Client", "redirect_uris": ["http://localhost:3000/callback"], "grant_types": ["authorization_code", "refresh_token"], "response_types": ["code"], "token_endpoint_auth_method": "none" }'
-
Authorization: User authorizes with PKCE
http://localhost:8080/authorize? response_type=code& client_id=CLIENT_ID& redirect_uri=http://localhost:3000/callback& code_challenge=CHALLENGE& code_challenge_method=S256& scope=mcp:read%20mcp:write
-
Token Exchange: Exchange code for token
curl -X POST http://localhost:8080/token \ -H "Content-Type: application/x-www-form-urlencoded" \ -d "grant_type=authorization_code&code=AUTH_CODE&code_verifier=VERIFIER"
-
API Access: Use token for authenticated requests
curl -X POST http://localhost:8080/mcp \ -H "Authorization: Bearer ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"ping"}'
Error Responses (MCP-compliant):
401 Unauthorized
: Invalid or expired token403 Forbidden
: Insufficient permissions400 Bad Request
: Malformed request
The same server implementation works for both transport methods:
{-# LANGUAGE OverloadedStrings #-}
import MCP.Server
import MCP.Server.StdIO -- For StdIO transport
import MCP.Server.HTTP -- For HTTP transport
import MCP.Types
import MCP.Protocol
instance MCPServer MCPServerM where
handleListTools _params = do
let tool = Tool
{ name = "calculator"
, description = Just "A simple calculator"
, inputSchema = -- JSON schema for tool parameters
, annotations = Nothing
}
return $ ListToolsResult [tool] Nothing
handleCallTool params = do
-- Implement your tool logic here
let result = TextContentType $ TextContent "Result: 42"
return $ CallToolResult [result] Nothing
-- Implement other required methods...
-- StdIO version
runStdIO :: IO ()
runStdIO = do
let config = ServerConfig
{ configInput = stdin
, configOutput = stdout
, configServerInfo = Implementation "my-server" "1.0.0"
, configCapabilities = serverCapabilities
}
MCP.Server.StdIO.runServer config
-- HTTP version
runHTTP :: IO ()
runHTTP = do
let config = HTTPServerConfig
{ httpPort = 8080
, httpServerInfo = Implementation "my-server" "1.0.0"
, httpCapabilities = serverCapabilities
, httpEnableLogging = False
, httpOAuthConfig = Nothing -- or Just oauthConfig for OAuth
}
runServerHTTP config
This implementation provides complete support for MCP protocol version 2025-06-18 with full schema compliance:
- Initialization: Protocol version negotiation and capability exchange with server instructions
- Resources: Expose data and content that can be read by LLMs with enhanced metadata
- Resource Links: Reference resources in prompts and tool results without embedding content
- Tools: Functions that LLMs can execute with input/output schema validation
- Prompts: Pre-written prompt templates with arguments and rich content blocks
- Completion: Auto-completion for resource URIs, prompt arguments, etc. with context support
- Sampling: LLM sampling requests with model preferences and content restrictions
- Elicitation: Interactive user input forms with primitive schema validation
- Logging: Configurable logging levels with structured data
- Notifications: Bidirectional event notifications for real-time updates
- Enhanced Content System: ContentBlock type supporting text, image, audio, embedded resources, and resource links
- Schema Validation: Tool input/output schemas and elicitation form schemas
- Metadata Fields: Comprehensive _meta field support with lastModified timestamps
- Base Metadata: Consistent name/title distinction across all types
- Sampling Restrictions: SamplingMessage limited to text, image, and audio content only
- Context-Aware Completions: Additional context parameters for better autocompletion
Operation | Description | Status |
---|---|---|
initialize |
Start session and negotiate capabilities | β |
ping |
Health check | β |
resources/list |
List available resources | β |
resources/templates/list |
List available resource templates | β |
resources/read |
Read resource contents | β |
resources/subscribe |
Subscribe to resource updates | β |
resources/unsubscribe |
Unsubscribe from resource updates | β |
prompts/list |
List available prompts | β |
prompts/get |
Get prompt with arguments | β |
tools/list |
List available tools | β |
tools/call |
Execute a tool | β |
completion/complete |
Auto-completion with context | β |
logging/setLevel |
Set logging level | β |
sampling/createMessage |
Request LLM sampling | β |
roots/list |
List client root directories | β |
elicitation/create |
Request user input via forms | β |
src/
βββ MCP/
β βββ Types.hs # Core MCP data types
β βββ Protocol.hs # JSON-RPC protocol messages
β βββ Server.hs # Core server infrastructure
β βββ Server/
β βββ StdIO.hs # StdIO transport implementation
β βββ HTTP.hs # HTTP transport implementation
app/
βββ Main.hs # Example MCP server (StdIO mode)
test/
βββ Main.hs # Test suite (placeholder)
Core Dependencies:
- aeson: JSON serialization/deserialization
- text: Text processing
- containers: Map and other container types
- bytestring: Binary data handling
- mtl/transformers: Monad transformers for MCPServerM
HTTP Server Dependencies:
- warp: High-performance HTTP server
- servant-server: Type-safe web API framework
- wai: Web application interface
- http-types: HTTP status codes and headers
- servant-auth: JWT authentication for Servant
- servant-auth-server: Server-side JWT implementation
- jose: JSON Object Signing and Encryption
- cryptonite: Cryptographic primitives for PKCE
- base64-bytestring: Base64 encoding for PKCE challenges
- http-conduit: HTTP client for OAuth metadata discovery
StdIO Transport (for process-based clients):
- Build the server:
cabal build
- Configure your MCP client to use:
cabal run mcp
- The server communicates via JSON-RPC over stdin/stdout
HTTP Transport (for web-based clients):
- Build and run HTTP server:
runServerHTTP config
- Client connects to
POST http://localhost:8080/mcp
- Send JSON-RPC requests with
Content-Type: application/json
- Supports both single responses and future SSE streaming
For Claude Desktop, add to your config file (location varies by OS):
{
"mcpServers": {
"haskell-mcp": {
"command": "cabal",
"args": ["run", "mcp"],
"cwd": "/absolute/path/to/mcp-haskell"
}
}
}
Configuration file locations:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
- Windows:
%APPDATA%\Claude\claude_desktop_config.json
- Linux:
~/.config/claude/claude_desktop_config.json
See the examples/
directory for more detailed configuration examples and setup instructions.
The implementation leverages Haskell's type system to ensure protocol compliance:
- All MCP message types are statically typed
- JSON schema validation through type structure
- Compile-time guarantees for message format correctness
MCP 2025-06-18 supports rich content blocks with proper type wrappers:
-- Text content
TextContentType $ TextContent { textType = "text", text = "Hello world!", annotations = Nothing, _meta = Nothing }
-- Image content
ImageContentType $ ImageContent { imageType = "image", data' = "data:image/png;base64,iVBOR...", mimeType = "image/png", annotations = Nothing, _meta = Nothing }
-- Resource link (new in 2025-06-18)
ResourceLinkType $ ResourceLink { resourceLinkType = "resource_link", uri = "file:///example.txt", name = "example", title = Just "Example File", description = Just "Sample text file", mimeType = Just "text/plain", size = Just 1024, annotations = Nothing, _meta = Nothing }
-- Embedded resource content
EmbeddedResourceType $ EmbeddedResource { resourceType = "resource", resource = TextResource $ TextResourceContents { uri = "file:///example.txt", text = "content", mimeType = Just "text/plain", _meta = Nothing }, annotations = Nothing, _meta = Nothing }
-- Sampling content (restricted for LLM APIs)
SamplingTextContent $ TextContent { textType = "text", text = "Hello LLM!", annotations = Nothing, _meta = Nothing }
The server includes comprehensive error handling:
- JSON parsing errors
- Invalid method names
- Missing or malformed parameters
- Server initialization requirements
This is the first known implementation of MCP for Haskell, now fully compliant with the latest MCP protocol version 2025-06-18. Contributions are welcome!
Areas for improvement:
- Server-Sent Events (SSE) support for HTTP transport
- WebSocket transport implementation
- More comprehensive example servers demonstrating new features (elicitation, resource links, etc.)
- Performance optimizations for large-scale deployments
- Enhanced error messages with better debugging information
- Additional documentation and tutorials covering new 2025-06-18 features
- Integration examples with popular Haskell web frameworks
- Benchmarking and profiling tools
MIT License - see LICENSE file for details.