| Copyright | (c) 2026 The Polytropos Authors |
|---|---|
| License | EUPL-1.2 OR Apache-2.0 |
| Maintainer | nadia.yvette.chambers@ik.me |
| Stability | experimental |
| Portability | non-portable (GHC extensions) |
| Safe Haskell | None |
| Language | GHC2021 |
Polytropos.MCP
Description
Model Context Protocol (MCP) tool integration and JSON-RPC dispatch for
polytropos open records and variants.
Architectural Overview & Rationale
The MCP Schema Problem
In standard Model Context Protocol server implementations, defining tools requires tedious manual duplication: 1. Declaring a nominal parameter data type. 2. Deriving or writing an OpenAPI / JSON Schema definition. 3. Writing a parser that inspects the tool name string and runs the appropriate decoder. 4. Writing an error-prone dispatcher branch that invokes the handler.
The Polytropos Solution
polytropos-mcp collapses this entire pipeline into row types:
* Tool Schemas: A tool's parameters are just an open record row type r.
recordToTool reifies r into an OpenAPI schema directly via OpenApi.
* Tool Call Variant: The entire suite of registered tools forms an open sum
variant Variant s, where constructor tags match tool names and constructor
payloads match parameter rows.
* \(O(1)\) Dispatch: Tool calls are parsed into Variant s and dispatched
using eliminateUniform, compiling to an indirect function call
jump table without string comparisons or branch cascades.
* Dynamic Extensibility: New tools can be added to the server by simply widening
the variant with widenVariant.
When to Use This Module
- When exposing
polytroposdata pipelines, compilers, or database query engines to AI coding assistants (such as Claude, Gemini, or custom agent frameworks) via the standard Model Context Protocol.
Worked Example
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE TypeApplications #-}
import Data.Proxy (Proxy (..))
import Data.Text (Text)
import Polytropos
import Polytropos.MCP
-- 1. Define parameter rows for tools:
type SearchArgs = '["query" ':= Text, "limit" ':= Int]
type EchoArgs = '["message" ':= Text]
-- 2. Define the tool registry as an open variant:
type MyTools = Variant
'[ "search" ':= SearchArgs
, "echo" := EchoArgs
]
-- 3. Generate tool manifests with OpenAPI schemas:
toolList :: [McpTool]
toolList =
[ recordToTool @"search" "Search indexed documents" (Proxy @SearchArgs)
, recordToTool @"echo" "Echo back a message" (Proxy @EchoArgs)
]
-- 4. Dispatch tool calls using O(1) jump tables:
handleCall :: MyTools -> IO [McpContent]
handleCall = eliminateUniform REC
{ search = \args -> pure [TextContent ("Found hits for: " <> args.query)]
, echo = \args -> pure [TextContent ("Echo: " <> args.message)]
}Synopsis
- data JsonRpcRequest = JsonRpcRequest {
- jsonrpcReqVersion :: !Text
- jsonrpcReqMethod :: !Text
- jsonrpcReqId :: !(Maybe Value)
- jsonrpcReqParams :: !(Maybe Value)
- data JsonRpcResponse = JsonRpcResponse {
- jsonrpcRespVersion :: !Text
- jsonrpcRespId :: !(Maybe Value)
- jsonrpcRespResult :: !(Maybe Value)
- jsonrpcRespError :: !(Maybe JsonRpcError)
- data JsonRpcError = JsonRpcError {
- jsonrpcErrorCode :: !Int
- jsonrpcErrorMessage :: !Text
- jsonrpcErrorData :: !(Maybe Value)
- data McpTool = McpTool {
- toolName :: !Text
- toolDescription :: !Text
- toolInputSchema :: !Value
- data CallToolParams = CallToolParams {
- callToolName :: !Text
- callToolArguments :: !(Maybe Value)
- data CallToolResult = CallToolResult {
- callToolContent :: ![McpContent]
- callToolIsError :: !(Maybe Bool)
- data McpContent
- = TextContent !Text
- | ImageContent !Text !Text
- | ResourceContent !Text !(Maybe Text)
- data ServerInfo = ServerInfo {
- serverName :: !Text
- serverVersion :: !Text
- data ServerCapabilities = ServerCapabilities {}
- data InitializeResult = InitializeResult {}
- recordToTool :: forall (name :: Symbol) (r :: Row). (KnownSymbol name, Typeable r, AllToSchema r) => Text -> Proxy r -> McpTool
- toolCallToVariant :: forall (s :: SumRow). AllCtorsFromJSON s => Text -> Value -> Either String (Variant s)
- dispatchToolCall :: forall (s :: SumRow). AllCtorsFromJSON s => (Variant s -> IO [McpContent]) -> Text -> Value -> IO CallToolResult
- handleJsonRpcMessage :: ServerInfo -> [McpTool] -> (Text -> Value -> IO CallToolResult) -> ByteString -> IO (Maybe ByteString)
- runStdioServer :: ServerInfo -> [McpTool] -> (Text -> Value -> IO CallToolResult) -> IO ()
MCP Protocol Data Types
data JsonRpcRequest Source #
A JSON-RPC 2.0 request or notification envelope.
Constructors
| JsonRpcRequest | |
Fields
| |
Instances
| FromJSON JsonRpcRequest Source # | |||||
Defined in Polytropos.MCP Methods parseJSON :: Value -> Parser JsonRpcRequest # parseJSONList :: Value -> Parser [JsonRpcRequest] # | |||||
| Eq JsonRpcRequest Source # | |||||
Defined in Polytropos.MCP Methods (==) :: JsonRpcRequest -> JsonRpcRequest -> Bool # (/=) :: JsonRpcRequest -> JsonRpcRequest -> Bool # | |||||
| Generic JsonRpcRequest Source # | |||||
Defined in Polytropos.MCP Associated Types
| |||||
| Show JsonRpcRequest Source # | |||||
Defined in Polytropos.MCP Methods showsPrec :: Int -> JsonRpcRequest -> ShowS # show :: JsonRpcRequest -> String # showList :: [JsonRpcRequest] -> ShowS # | |||||
| type Rep JsonRpcRequest Source # | |||||
Defined in Polytropos.MCP type Rep JsonRpcRequest = D1 ('MetaData "JsonRpcRequest" "Polytropos.MCP" "polytropos-mcp-0.1.0.0-inplace" 'False) (C1 ('MetaCons "JsonRpcRequest" 'PrefixI 'True) ((S1 ('MetaSel ('Just "jsonrpcReqVersion") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "jsonrpcReqMethod") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text)) :*: (S1 ('MetaSel ('Just "jsonrpcReqId") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Maybe Value)) :*: S1 ('MetaSel ('Just "jsonrpcReqParams") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Maybe Value))))) | |||||
data JsonRpcResponse Source #
A JSON-RPC 2.0 response envelope.
Constructors
| JsonRpcResponse | |
Fields
| |
Instances
data JsonRpcError Source #
A JSON-RPC 2.0 protocol error object.
Constructors
| JsonRpcError | |
Fields
| |
Instances
| FromJSON JsonRpcError Source # | |||||
Defined in Polytropos.MCP | |||||
| ToJSON JsonRpcError Source # | |||||
Defined in Polytropos.MCP Methods toJSON :: JsonRpcError -> Value # toEncoding :: JsonRpcError -> Encoding # toJSONList :: [JsonRpcError] -> Value # toEncodingList :: [JsonRpcError] -> Encoding # omitField :: JsonRpcError -> Bool # | |||||
| Eq JsonRpcError Source # | |||||
Defined in Polytropos.MCP | |||||
| Generic JsonRpcError Source # | |||||
Defined in Polytropos.MCP Associated Types
| |||||
| Show JsonRpcError Source # | |||||
Defined in Polytropos.MCP Methods showsPrec :: Int -> JsonRpcError -> ShowS # show :: JsonRpcError -> String # showList :: [JsonRpcError] -> ShowS # | |||||
| type Rep JsonRpcError Source # | |||||
Defined in Polytropos.MCP type Rep JsonRpcError = D1 ('MetaData "JsonRpcError" "Polytropos.MCP" "polytropos-mcp-0.1.0.0-inplace" 'False) (C1 ('MetaCons "JsonRpcError" 'PrefixI 'True) (S1 ('MetaSel ('Just "jsonrpcErrorCode") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Int) :*: (S1 ('MetaSel ('Just "jsonrpcErrorMessage") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "jsonrpcErrorData") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Maybe Value))))) | |||||
An MCP Tool specification describing a callable tool.
Constructors
| McpTool | |
Fields
| |
Instances
| FromJSON McpTool Source # | |||||
Defined in Polytropos.MCP | |||||
| ToJSON McpTool Source # | |||||
| Eq McpTool Source # | |||||
| Generic McpTool Source # | |||||
Defined in Polytropos.MCP Associated Types
| |||||
| Show McpTool Source # | |||||
| type Rep McpTool Source # | |||||
Defined in Polytropos.MCP type Rep McpTool = D1 ('MetaData "McpTool" "Polytropos.MCP" "polytropos-mcp-0.1.0.0-inplace" 'False) (C1 ('MetaCons "McpTool" 'PrefixI 'True) (S1 ('MetaSel ('Just "toolName") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: (S1 ('MetaSel ('Just "toolDescription") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "toolInputSchema") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Value)))) | |||||
data CallToolParams Source #
Parameters passed in an MCP tools/call request.
Constructors
| CallToolParams | |
Fields
| |
Instances
| FromJSON CallToolParams Source # | |||||
Defined in Polytropos.MCP Methods parseJSON :: Value -> Parser CallToolParams # parseJSONList :: Value -> Parser [CallToolParams] # | |||||
| Eq CallToolParams Source # | |||||
Defined in Polytropos.MCP Methods (==) :: CallToolParams -> CallToolParams -> Bool # (/=) :: CallToolParams -> CallToolParams -> Bool # | |||||
| Generic CallToolParams Source # | |||||
Defined in Polytropos.MCP Associated Types
| |||||
| Show CallToolParams Source # | |||||
Defined in Polytropos.MCP Methods showsPrec :: Int -> CallToolParams -> ShowS # show :: CallToolParams -> String # showList :: [CallToolParams] -> ShowS # | |||||
| type Rep CallToolParams Source # | |||||
Defined in Polytropos.MCP type Rep CallToolParams = D1 ('MetaData "CallToolParams" "Polytropos.MCP" "polytropos-mcp-0.1.0.0-inplace" 'False) (C1 ('MetaCons "CallToolParams" 'PrefixI 'True) (S1 ('MetaSel ('Just "callToolName") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "callToolArguments") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Maybe Value)))) | |||||
data CallToolResult Source #
Result of an MCP tools/call execution.
Constructors
| CallToolResult | |
Fields
| |
Instances
| FromJSON CallToolResult Source # | |||||
Defined in Polytropos.MCP Methods parseJSON :: Value -> Parser CallToolResult # parseJSONList :: Value -> Parser [CallToolResult] # | |||||
| ToJSON CallToolResult Source # | |||||
Defined in Polytropos.MCP Methods toJSON :: CallToolResult -> Value # toEncoding :: CallToolResult -> Encoding # toJSONList :: [CallToolResult] -> Value # toEncodingList :: [CallToolResult] -> Encoding # omitField :: CallToolResult -> Bool # | |||||
| Eq CallToolResult Source # | |||||
Defined in Polytropos.MCP Methods (==) :: CallToolResult -> CallToolResult -> Bool # (/=) :: CallToolResult -> CallToolResult -> Bool # | |||||
| Generic CallToolResult Source # | |||||
Defined in Polytropos.MCP Associated Types
| |||||
| Show CallToolResult Source # | |||||
Defined in Polytropos.MCP Methods showsPrec :: Int -> CallToolResult -> ShowS # show :: CallToolResult -> String # showList :: [CallToolResult] -> ShowS # | |||||
| type Rep CallToolResult Source # | |||||
Defined in Polytropos.MCP type Rep CallToolResult = D1 ('MetaData "CallToolResult" "Polytropos.MCP" "polytropos-mcp-0.1.0.0-inplace" 'False) (C1 ('MetaCons "CallToolResult" 'PrefixI 'True) (S1 ('MetaSel ('Just "callToolContent") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 [McpContent]) :*: S1 ('MetaSel ('Just "callToolIsError") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Maybe Bool)))) | |||||
data McpContent Source #
An MCP content item returned by a tool execution.
Constructors
| TextContent !Text | |
| ImageContent !Text !Text | base64 data, mimeType |
| ResourceContent !Text !(Maybe Text) | uri, optional text |
Instances
| FromJSON McpContent Source # | |||||
Defined in Polytropos.MCP | |||||
| ToJSON McpContent Source # | |||||
Defined in Polytropos.MCP Methods toJSON :: McpContent -> Value # toEncoding :: McpContent -> Encoding # toJSONList :: [McpContent] -> Value # toEncodingList :: [McpContent] -> Encoding # omitField :: McpContent -> Bool # | |||||
| Eq McpContent Source # | |||||
Defined in Polytropos.MCP | |||||
| Generic McpContent Source # | |||||
Defined in Polytropos.MCP Associated Types
| |||||
| Show McpContent Source # | |||||
Defined in Polytropos.MCP Methods showsPrec :: Int -> McpContent -> ShowS # show :: McpContent -> String # showList :: [McpContent] -> ShowS # | |||||
| type Rep McpContent Source # | |||||
Defined in Polytropos.MCP type Rep McpContent = D1 ('MetaData "McpContent" "Polytropos.MCP" "polytropos-mcp-0.1.0.0-inplace" 'False) (C1 ('MetaCons "TextContent" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text)) :+: (C1 ('MetaCons "ImageContent" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text)) :+: C1 ('MetaCons "ResourceContent" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Maybe Text))))) | |||||
data ServerInfo Source #
Server information reported during the initialize handshake.
Constructors
| ServerInfo | |
Fields
| |
Instances
| ToJSON ServerInfo Source # | |||||
Defined in Polytropos.MCP Methods toJSON :: ServerInfo -> Value # toEncoding :: ServerInfo -> Encoding # toJSONList :: [ServerInfo] -> Value # toEncodingList :: [ServerInfo] -> Encoding # omitField :: ServerInfo -> Bool # | |||||
| Eq ServerInfo Source # | |||||
Defined in Polytropos.MCP | |||||
| Generic ServerInfo Source # | |||||
Defined in Polytropos.MCP Associated Types
| |||||
| Show ServerInfo Source # | |||||
Defined in Polytropos.MCP Methods showsPrec :: Int -> ServerInfo -> ShowS # show :: ServerInfo -> String # showList :: [ServerInfo] -> ShowS # | |||||
| type Rep ServerInfo Source # | |||||
Defined in Polytropos.MCP type Rep ServerInfo = D1 ('MetaData "ServerInfo" "Polytropos.MCP" "polytropos-mcp-0.1.0.0-inplace" 'False) (C1 ('MetaCons "ServerInfo" 'PrefixI 'True) (S1 ('MetaSel ('Just "serverName") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "serverVersion") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text))) | |||||
data ServerCapabilities Source #
Server capabilities announced during initialization.
Constructors
| ServerCapabilities | |
Instances
| ToJSON ServerCapabilities Source # | |||||
Defined in Polytropos.MCP Methods toJSON :: ServerCapabilities -> Value # toEncoding :: ServerCapabilities -> Encoding # toJSONList :: [ServerCapabilities] -> Value # toEncodingList :: [ServerCapabilities] -> Encoding # omitField :: ServerCapabilities -> Bool # | |||||
| Eq ServerCapabilities Source # | |||||
Defined in Polytropos.MCP Methods (==) :: ServerCapabilities -> ServerCapabilities -> Bool # (/=) :: ServerCapabilities -> ServerCapabilities -> Bool # | |||||
| Generic ServerCapabilities Source # | |||||
Defined in Polytropos.MCP Associated Types
Methods from :: ServerCapabilities -> Rep ServerCapabilities x to :: Rep ServerCapabilities x -> ServerCapabilities | |||||
| Show ServerCapabilities Source # | |||||
Defined in Polytropos.MCP Methods showsPrec :: Int -> ServerCapabilities -> ShowS # show :: ServerCapabilities -> String # showList :: [ServerCapabilities] -> ShowS # | |||||
| type Rep ServerCapabilities Source # | |||||
Defined in Polytropos.MCP type Rep ServerCapabilities = D1 ('MetaData "ServerCapabilities" "Polytropos.MCP" "polytropos-mcp-0.1.0.0-inplace" 'False) (C1 ('MetaCons "ServerCapabilities" 'PrefixI 'True) (S1 ('MetaSel ('Just "capTools") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 (Maybe Value)))) | |||||
data InitializeResult Source #
The payload returned to the client upon a successful initialize handshake.
Constructors
| InitializeResult | |
Fields
| |
Instances
| ToJSON InitializeResult Source # | |||||
Defined in Polytropos.MCP Methods toJSON :: InitializeResult -> Value # toEncoding :: InitializeResult -> Encoding # toJSONList :: [InitializeResult] -> Value # toEncodingList :: [InitializeResult] -> Encoding # omitField :: InitializeResult -> Bool # | |||||
| Eq InitializeResult Source # | |||||
Defined in Polytropos.MCP Methods (==) :: InitializeResult -> InitializeResult -> Bool # (/=) :: InitializeResult -> InitializeResult -> Bool # | |||||
| Generic InitializeResult Source # | |||||
Defined in Polytropos.MCP Associated Types
Methods from :: InitializeResult -> Rep InitializeResult x to :: Rep InitializeResult x -> InitializeResult | |||||
| Show InitializeResult Source # | |||||
Defined in Polytropos.MCP Methods showsPrec :: Int -> InitializeResult -> ShowS # show :: InitializeResult -> String # showList :: [InitializeResult] -> ShowS # | |||||
| type Rep InitializeResult Source # | |||||
Defined in Polytropos.MCP type Rep InitializeResult = D1 ('MetaData "InitializeResult" "Polytropos.MCP" "polytropos-mcp-0.1.0.0-inplace" 'False) (C1 ('MetaCons "InitializeResult" 'PrefixI 'True) (S1 ('MetaSel ('Just "initProtocolVersion") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 Text) :*: (S1 ('MetaSel ('Just "initCapabilities") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 ServerCapabilities) :*: S1 ('MetaSel ('Just "initServerInfo") 'NoSourceUnpackedness 'SourceStrict 'DecidedStrict) (Rec0 ServerInfo)))) | |||||
Tool Schema Synthesis
Arguments
| :: forall (name :: Symbol) (r :: Row). (KnownSymbol name, Typeable r, AllToSchema r) | |
| => Text | Human-readable tool description |
| -> Proxy r | Parameter row proxy |
| -> McpTool |
Tool Call Parsing & Dispatch
Arguments
| :: forall (s :: SumRow). AllCtorsFromJSON s | |
| => Text | Invoked tool name |
| -> Value | JSON argument object |
| -> Either String (Variant s) |
Parses tool call arguments into a Polytropos Variant sum row s.
Constructs a tagged singleton object {"toolName": arguments} and decodes
via AllCtorsFromJSON.
dispatchToolCall :: forall (s :: SumRow). AllCtorsFromJSON s => (Variant s -> IO [McpContent]) -> Text -> Value -> IO CallToolResult Source #
Dispatches a tool execution request by decoding into Variant s and
invoking the provided handler (typically constructed via eliminateUniform).
handleJsonRpcMessage :: ServerInfo -> [McpTool] -> (Text -> Value -> IO CallToolResult) -> ByteString -> IO (Maybe ByteString) Source #
Handles a single incoming JSON-RPC raw message line, producing a response
(or Nothing for notifications that expect no reply).
runStdioServer :: ServerInfo -> [McpTool] -> (Text -> Value -> IO CallToolResult) -> IO () Source #
Runs an MCP server over standard input/output using newline-delimited JSON-RPC messages.