packages feed

mcp-server-0.1.0.0: examples/HighLevel.hs

{-# LANGUAGE OverloadedStrings #-}

module Main where

import qualified Data.Map    as Map
import           Data.Text   (Text)
import qualified Data.Text   as T
import           MCP.Server
import           Network.URI (URI)
import           System.IO   (hPutStrLn, stderr)

-- High-level data type definitions from SPEC.md

data MyPrompt
    = Recipe { idea :: Text }
    | Shopping { description :: Text }
    deriving (Show, Eq)

data MyResource
    = ProductCategories
    | SaleItems
    | HeadlineBannerAd
    deriving (Show, Eq)

data MyTool
    = SearchForProduct { q :: Text, category :: Maybe Text }
    | AddToCart { sku :: Text }
    | Checkout
    deriving (Show, Eq)

-- High-level handler functions

handlePrompt :: MyPrompt -> IO Content
handlePrompt (Recipe idea) =
    pure $ ContentText $ "Recipe prompt for " <> idea <> ": Start by gathering fresh ingredients..."
handlePrompt (Shopping description) =
    pure $ ContentText $ "Shopping prompt for " <> description <> ": Create a detailed shopping list..."

handleResource :: MyResource -> IO Content
handleResource ProductCategories = pure $ ContentText "Fresh Produce, Dairy, Bakery, Meat & Seafood, Frozen Foods"
handleResource SaleItems = pure $ ContentText "Organic Apples $2.99/lb, Free Range Eggs $4.50/dozen, Artisan Bread $3.25/loaf"
handleResource HeadlineBannerAd = pure $ ContentText "🛒 Weekly Special: 20% off all organic produce! 🥕🥬🍎"

handleTool :: MyTool -> IO Content
handleTool (SearchForProduct q category) =
    case category of
        Nothing -> pure $ ContentText $ "Search results for '" <> q <> "': Found 15 products across all categories"
        Just cat -> pure $ ContentText $ "Search results for '" <> q <> "' in " <> cat <> ": Found 8 products"
handleTool (AddToCart sku) = pure $ ContentText $ "Added item " <> sku <> " to your cart. Cart total: 3 items"
handleTool Checkout = pure $ ContentText "Checkout completed! Order #12345 confirmed. Thank you for shopping with us!"

-- Manual derivation (we'll replace this with Template Haskell later)

promptListHandler :: Maybe Cursor -> IO (PaginatedResult [PromptDefinition])
promptListHandler _ = pure $ PaginatedResult
    { paginatedItems =
        [ PromptDefinition
            { promptDefinitionName = "recipe"
            , promptDefinitionDescription = "Generate a detailed recipe for a particular idea"
            , promptDefinitionArguments =
                [ ArgumentDefinition
                    { argumentDefinitionName = "idea"
                    , argumentDefinitionDescription = "inspiring idea for the recipe"
                    , argumentDefinitionRequired = True
                    }
                ]
            }
        , PromptDefinition
            { promptDefinitionName = "shopping"
            , promptDefinitionDescription = "Create a shopping list"
            , promptDefinitionArguments =
                [ ArgumentDefinition
                    { argumentDefinitionName = "description"
                    , argumentDefinitionDescription = "description of what to shop for"
                    , argumentDefinitionRequired = True
                    }
                ]
            }
        ]
    , paginatedNextCursor = Nothing
    }

promptGetHandler :: PromptName -> [(ArgumentName, ArgumentValue)] -> IO (Either Error Content)
promptGetHandler "recipe" args = do
    case Map.lookup "idea" (Map.fromList args) of
        Nothing -> pure $ Left $ MissingRequiredParams "field 'idea' is missing"
        Just idea -> do
            content <- handlePrompt (Recipe idea)
            pure $ Right content
promptGetHandler "shopping" args = do
    case Map.lookup "description" (Map.fromList args) of
        Nothing -> pure $ Left $ MissingRequiredParams "field 'description' is missing"
        Just desc -> do
            content <- handlePrompt (Shopping desc)
            pure $ Right content
promptGetHandler name _ = pure $ Left $ InvalidPromptName $ "Unknown prompt: " <> name

resourceListHandler :: Maybe Cursor -> IO (PaginatedResult [ResourceDefinition])
resourceListHandler _ = pure $ PaginatedResult
    { paginatedItems =
        [ ResourceDefinition
            { resourceDefinitionURI = "resource://productcategories"
            , resourceDefinitionName = "productcategories"
            , resourceDefinitionDescription = Just "Fresh produce, dairy, and other categories"
            , resourceDefinitionMimeType = Just "text/plain"
            }
        , ResourceDefinition
            { resourceDefinitionURI = "resource://saleitems"
            , resourceDefinitionName = "saleitems"
            , resourceDefinitionDescription = Just "Current sale items and discounts"
            , resourceDefinitionMimeType = Just "text/plain"
            }
        , ResourceDefinition
            { resourceDefinitionURI = "resource://headlinebannerad"
            , resourceDefinitionName = "headlinebannerad"
            , resourceDefinitionDescription = Just "Weekly promotional banner"
            , resourceDefinitionMimeType = Just "text/plain"
            }
        ]
    , paginatedNextCursor = Nothing
    }

resourceReadHandler :: URI -> IO (Either Error Content)
resourceReadHandler uri = do
    case show uri of
        "resource://productcategories" -> Right <$> handleResource ProductCategories
        "resource://saleitems" -> Right <$> handleResource SaleItems
        "resource://headlinebannerad" -> Right <$> handleResource HeadlineBannerAd
        unknown -> pure $ Left $ ResourceNotFound $ "Resource not found: " <> T.pack unknown

toolListHandler :: Maybe Cursor -> IO (PaginatedResult [ToolDefinition])
toolListHandler _ = pure $ PaginatedResult
    { paginatedItems =
        [ ToolDefinition
            { toolDefinitionName = "searchforproduct"
            , toolDefinitionDescription = "Search for products in the catalog"
            , toolDefinitionInputSchema =
                InputSchemaDefinitionObject
                    { properties =
                        [ ("q", InputSchemaDefinitionProperty
                            { propertyType = "string"
                            , propertyDescription = "Search query"
                            })
                        , ("category", InputSchemaDefinitionProperty
                            { propertyType = "string"
                            , propertyDescription = "Optional category filter"
                            })
                        ]
                    , required = ["q"]
                    }
            }
        , ToolDefinition
            { toolDefinitionName = "addtocart"
            , toolDefinitionDescription = "Add an item to shopping cart"
            , toolDefinitionInputSchema =
                InputSchemaDefinitionObject
                    { properties =
                        [ ("sku", InputSchemaDefinitionProperty
                            { propertyType = "string"
                            , propertyDescription = "Product SKU"
                            })
                        ]
                    , required = ["sku"]
                    }
            }
        , ToolDefinition
            { toolDefinitionName = "checkout"
            , toolDefinitionDescription = "Complete the checkout process"
            , toolDefinitionInputSchema =
                InputSchemaDefinitionObject
                    { properties = []
                    , required = []
                    }
            }
        ]
    , paginatedNextCursor = Nothing
    }

toolCallHandler :: ToolName -> [(ArgumentName, ArgumentValue)] -> IO (Either Error Content)
toolCallHandler "searchforproduct" args = do
    case Map.lookup "q" (Map.fromList args) of
        Nothing -> pure $ Left $ MissingRequiredParams "field 'q' is missing"
        Just q -> do
            let category = Map.lookup "category" (Map.fromList args)
            content <- handleTool (SearchForProduct q category)
            pure $ Right content
toolCallHandler "addtocart" args = do
    case Map.lookup "sku" (Map.fromList args) of
        Nothing -> pure $ Left $ MissingRequiredParams "field 'sku' is missing"
        Just sku -> do
            content <- handleTool (AddToCart sku)
            pure $ Right content
toolCallHandler "checkout" _ = do
    content <- handleTool Checkout
    pure $ Right content
toolCallHandler name _ = pure $ Left $ UnknownTool $ "Unknown tool: " <> name

main :: IO ()
main = do
    hPutStrLn stderr "Starting High-Level Grocery Store MCP Server..."
    hPutStrLn stderr "Using manually derived handlers (Template Haskell coming soon!)"
    hPutStrLn stderr "Ready for JSON-RPC communication"

    runMcpServerStdIn
        McpServerInfo
            { serverName = "High-Level Grocery Store MCP Server"
            , serverVersion = "0.2.0"
            , serverInstructions = "Advanced grocery store server with type-safe high-level handlers"
            }
        McpServerHandlers
            { prompts = Just (promptListHandler, promptGetHandler)
            , resources = Just (resourceListHandler, resourceReadHandler)
            , tools = Just (toolListHandler, toolCallHandler)
            }