mcp-server-0.1.0.0: src/MCP/Server/JsonRpc.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module MCP.Server.JsonRpc
( -- * JSON-RPC Types
JsonRpcRequest(..)
, JsonRpcResponse(..)
, JsonRpcError(..)
, JsonRpcNotification(..)
, JsonRpcMessage(..)
, RequestId(..)
-- * JSON-RPC Functions
, makeSuccessResponse
, makeErrorResponse
, makeNotification
, parseJsonRpcMessage
, encodeJsonRpcMessage
) where
import Data.Text (Text)
import Data.Aeson
import Data.Aeson.Types (parseEither)
import GHC.Generics (Generic)
import Control.Applicative ((<|>))
-- | JSON-RPC request ID
data RequestId
= RequestIdText Text
| RequestIdNumber Int
| RequestIdNull
deriving (Show, Eq, Generic)
instance ToJSON RequestId where
toJSON (RequestIdText t) = toJSON t
toJSON (RequestIdNumber n) = toJSON n
toJSON RequestIdNull = Null
instance FromJSON RequestId where
parseJSON (String t) = return $ RequestIdText t
parseJSON (Number n) = return $ RequestIdNumber (round n)
parseJSON Null = return RequestIdNull
parseJSON _ = fail "Invalid request ID"
-- | JSON-RPC request
data JsonRpcRequest = JsonRpcRequest
{ requestJsonrpc :: Text
, requestId :: RequestId
, requestMethod :: Text
, requestParams :: Maybe Value
} deriving (Show, Eq, Generic)
instance ToJSON JsonRpcRequest where
toJSON req = object $
[ "jsonrpc" .= requestJsonrpc req
, "id" .= requestId req
, "method" .= requestMethod req
] ++ maybe [] (\p -> ["params" .= p]) (requestParams req)
instance FromJSON JsonRpcRequest where
parseJSON = withObject "JsonRpcRequest" $ \o -> JsonRpcRequest
<$> o .: "jsonrpc"
<*> o .: "id"
<*> o .: "method"
<*> o .:? "params"
-- | JSON-RPC error
data JsonRpcError = JsonRpcError
{ errorCode :: Int
, errorMessage :: Text
, errorData :: Maybe Value
} deriving (Show, Eq, Generic)
instance ToJSON JsonRpcError where
toJSON err = object $
[ "code" .= errorCode err
, "message" .= errorMessage err
] ++ maybe [] (\d -> ["data" .= d]) (errorData err)
instance FromJSON JsonRpcError where
parseJSON = withObject "JsonRpcError" $ \o -> JsonRpcError
<$> o .: "code"
<*> o .: "message"
<*> o .:? "data"
-- | JSON-RPC response
data JsonRpcResponse = JsonRpcResponse
{ responseJsonrpc :: Text
, responseId :: RequestId
, responseResult :: Maybe Value
, responseError :: Maybe JsonRpcError
} deriving (Show, Eq, Generic)
instance ToJSON JsonRpcResponse where
toJSON resp = object $
[ "jsonrpc" .= responseJsonrpc resp
, "id" .= responseId resp
] ++
maybe [] (\r -> ["result" .= r]) (responseResult resp) ++
maybe [] (\e -> ["error" .= e]) (responseError resp)
instance FromJSON JsonRpcResponse where
parseJSON = withObject "JsonRpcResponse" $ \o -> JsonRpcResponse
<$> o .: "jsonrpc"
<*> o .: "id"
<*> o .:? "result"
<*> o .:? "error"
-- | JSON-RPC notification
data JsonRpcNotification = JsonRpcNotification
{ notificationJsonrpc :: Text
, notificationMethod :: Text
, notificationParams :: Maybe Value
} deriving (Show, Eq, Generic)
instance ToJSON JsonRpcNotification where
toJSON notif = object $
[ "jsonrpc" .= notificationJsonrpc notif
, "method" .= notificationMethod notif
] ++ maybe [] (\p -> ["params" .= p]) (notificationParams notif)
instance FromJSON JsonRpcNotification where
parseJSON = withObject "JsonRpcNotification" $ \o -> JsonRpcNotification
<$> o .: "jsonrpc"
<*> o .: "method"
<*> o .:? "params"
-- | Union type for all JSON-RPC messages
data JsonRpcMessage
= JsonRpcMessageRequest JsonRpcRequest
| JsonRpcMessageResponse JsonRpcResponse
| JsonRpcMessageNotification JsonRpcNotification
deriving (Show, Eq, Generic)
instance ToJSON JsonRpcMessage where
toJSON (JsonRpcMessageRequest req) = toJSON req
toJSON (JsonRpcMessageResponse resp) = toJSON resp
toJSON (JsonRpcMessageNotification notif) = toJSON notif
instance FromJSON JsonRpcMessage where
parseJSON v = parseRequest v <|> parseResponse v <|> parseNotification v
where
parseRequest = fmap JsonRpcMessageRequest . parseJSON
parseResponse = fmap JsonRpcMessageResponse . parseJSON
parseNotification = fmap JsonRpcMessageNotification . parseJSON
-- | Create a successful JSON-RPC response
makeSuccessResponse :: RequestId -> Value -> JsonRpcResponse
makeSuccessResponse reqId result = JsonRpcResponse
{ responseJsonrpc = "2.0"
, responseId = reqId
, responseResult = Just result
, responseError = Nothing
}
-- | Create an error JSON-RPC response
makeErrorResponse :: RequestId -> JsonRpcError -> JsonRpcResponse
makeErrorResponse reqId err = JsonRpcResponse
{ responseJsonrpc = "2.0"
, responseId = reqId
, responseResult = Nothing
, responseError = Just err
}
-- | Create a JSON-RPC notification
makeNotification :: Text -> Maybe Value -> JsonRpcNotification
makeNotification method params = JsonRpcNotification
{ notificationJsonrpc = "2.0"
, notificationMethod = method
, notificationParams = params
}
-- | Parse a JSON-RPC message from bytes
parseJsonRpcMessage :: Value -> Either String JsonRpcMessage
parseJsonRpcMessage = parseEither parseJSON
-- | Encode a JSON-RPC message to bytes
encodeJsonRpcMessage :: JsonRpcMessage -> Value
encodeJsonRpcMessage = toJSON