packages feed

mcp-server-0.2.0.0: test/TestHelpers.hs

{-# LANGUAGE OverloadedStrings #-}

-- | Shared helpers for exercising the 0.2 handler API from tests.
module TestHelpers
  ( -- * Building argument maps
    stringArgs
  , valueArgs
  , promptArgsMap
    -- * Result expectations
  , shouldBeTextResult
  , shouldBeTextResultContaining
  , shouldBePromptText
    -- * Schema access
  , schemaTypeName
  , schemaProps
  , schemaRequired
  , lookupProp
  ) where

import           Data.Aeson (Value (..))
import           Data.Map   (Map)
import qualified Data.Map   as Map
import           Data.Text  (Text)
import qualified Data.Text  as T
import           MCP.Server
import           Test.Hspec

-- | Tool arguments where every value is a JSON string (exercises the lenient
-- string-fallback parsing that pre-0.2 clients rely on).
stringArgs :: [(Text, Text)] -> Map Text Value
stringArgs = Map.fromList . map (fmap String)

-- | Tool arguments with native JSON values.
valueArgs :: [(Text, Value)] -> Map Text Value
valueArgs = Map.fromList

-- | Prompt arguments (string-valued per the MCP spec).
promptArgsMap :: [(Text, Text)] -> Map Text Text
promptArgsMap = Map.fromList

-- | Expect a successful, non-error tool result with exactly one text block.
shouldBeTextResult :: Either Error ToolResult -> Text -> Expectation
shouldBeTextResult (Right tr) expected
  | not (toolResultIsError tr)
  , [ContentText content] <- toolResultContent tr = content `shouldBe` expected
shouldBeTextResult other expected =
  expectationFailure $ "Expected text result '" ++ T.unpack expected ++ "' but got: " ++ show other

-- | Like 'shouldBeTextResult' but with substring matching.
shouldBeTextResultContaining :: Either Error ToolResult -> Text -> Expectation
shouldBeTextResultContaining (Right tr) expected
  | not (toolResultIsError tr)
  , [ContentText content] <- toolResultContent tr =
      content `shouldSatisfy` T.isInfixOf expected
shouldBeTextResultContaining other expected =
  expectationFailure $ "Expected text result containing '" ++ T.unpack expected ++ "' but got: " ++ show other

-- | Expect a prompt result of a single user message with the given text.
shouldBePromptText :: Either Error PromptResult -> Text -> Expectation
shouldBePromptText (Right (PromptResult _ [PromptMessage RoleUser (ContentText content)])) expected =
  content `shouldBe` expected
shouldBePromptText other expected =
  expectationFailure $ "Expected prompt text '" ++ T.unpack expected ++ "' but got: " ++ show other

-- | The JSON Schema type keyword for a schema.
schemaTypeName :: Schema -> Text
schemaTypeName s = case schemaShape s of
  SchemaString _   -> "string"
  SchemaInteger    -> "integer"
  SchemaNumber     -> "number"
  SchemaBoolean    -> "boolean"
  SchemaArray _    -> "array"
  SchemaObject _ _ -> "object"

-- | The properties of an object schema (empty for non-objects).
schemaProps :: Schema -> [(Text, Schema)]
schemaProps s = case schemaShape s of
  SchemaObject props _ -> props
  _                    -> []

-- | The required field names of an object schema.
schemaRequired :: Schema -> [Text]
schemaRequired s = case schemaShape s of
  SchemaObject _ req -> req
  _                  -> []

-- | Look up a property schema in a tool's input schema.
lookupProp :: Text -> ToolDefinition -> Maybe Schema
lookupProp name def = lookup name (schemaProps (toolDefinitionInputSchema def))