packages feed

ollama-haskell-0.4.0.0: test/Test/Ollama/Unit/MCP.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}

module Test.Ollama.Unit.MCP (tests) where

import Data.Map.Strict qualified as Map
import MCP.Server.Types (
  Content (..),
  ContentImageData (..),
  ResourceContent (..),
  ToolDefinition (..),
  describedSchema,
  mkToolDefinition,
  parseURI,
  schema,
 )
import Ollama.MCP (
  SchemaType (..),
  mcpContentToToolOutput,
  mcpDefinitionToTool,
  schemaDescription,
  schemaShape,
  toolCallToMcpArgs,
  toolToMcpDefinition,
 )
import Ollama.Types.Tool (
  FunctionDef (..),
  FunctionParameters (..),
  Tool (..),
  ToolCall (..),
  ToolCallFunction (..),
 )
import Test.Tasty
import Test.Tasty.HUnit

tests :: TestTree
tests =
  testGroup
    "Unit MCP Integration Tests (mcp-server)"
    [ testCase "toolToMcpDefinition converts Ollama Tool to mcp-server ToolDefinition" $ do
        let propMap =
              Map.fromList
                [ ("query", FunctionParameters "string" Nothing Nothing Nothing (Just "Search query") Nothing)
                , ("limit", FunctionParameters "integer" Nothing Nothing Nothing (Just "Max results") Nothing)
                ]
            params = FunctionParameters "object" (Just propMap) (Just ["query"]) Nothing Nothing Nothing
            ollamaTool = Tool "function" $ FunctionDef "search" (Just "Search codebase") (Just params) Nothing
            mcpDef = toolToMcpDefinition ollamaTool

        toolDefinitionName mcpDef @?= "search"
        toolDefinitionDescription mcpDef @?= "Search codebase"
        case schemaShape (toolDefinitionInputSchema mcpDef) of
          SchemaObject props req -> do
            req @?= ["query"]
            length props @?= 2
            case lookup "query" props of
              Just s -> do
                schemaDescription s @?= Just "Search query"
                schemaShape s @?= SchemaString Nothing
              Nothing -> assertFailure "Expected query in props"
          _ -> assertFailure "Expected SchemaObject"
    , testCase "mcpDefinitionToTool converts mcp-server ToolDefinition to Ollama Tool" $ do
        let props =
              [ ("path", describedSchema "File path" (SchemaString Nothing))
              , ("content", describedSchema "File content" (SchemaString Nothing))
              ]
            s = schema (SchemaObject props ["path", "content"])
            mcpDef = mkToolDefinition "write_file" "Write file to disk" s
            ollamaTool = mcpDefinitionToTool mcpDef

        toolType ollamaTool @?= "function"
        let fn = toolFunction ollamaTool
        fnName fn @?= "write_file"
        fnDescription fn @?= Just "Write file to disk"
        case fnParameters fn of
          Just FunctionParameters {..} -> do
            fpType @?= "object"
            fpRequired @?= Just ["path", "content"]
            case fpProperties of
              Just pm -> Map.member "path" pm @?= True
              Nothing -> assertFailure "Expected properties"
          Nothing -> assertFailure "Expected parameters"
    , testCase "toolCallToMcpArgs converts Ollama ToolCall to mcp-server argument pairs" $ do
        let call = ToolCall (ToolCallFunction "greet" (Map.fromList [("name", "Alice")]))
            (fn, args) = toolCallToMcpArgs call
        fn @?= "greet"
        lookup "name" args @?= Just "Alice"
    , testCase "mcpContentToToolOutput extracts text from mcp-server Content" $ do
        let textContent = ContentText "Execution output"
        mcpContentToToolOutput textContent @?= "Execution output"

        let imgContent = ContentImage (ContentImageData "base64..." "image/png")
        mcpContentToToolOutput imgContent @?= "[Image: image/png]"

        case parseURI "file:///workspace/test.txt" of
          Just uri -> do
            let resContent = ContentEmbeddedResource (ResourceText uri "text/plain" "file contents")
            mcpContentToToolOutput resContent @?= "[Resource: file:///workspace/test.txt]"
          Nothing -> assertFailure "Failed to parse URI"
    ]