diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# Revision history for haskell-mcp-lib
+
+## 0.1.0.0 -- YYYY-mm-dd
+
+* First version. Released on an unsuspecting world.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,29 @@
+Copyright (c) 2025, Tom Wells
+
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of the copyright holder nor the names of its
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,141 @@
+# mcp-server
+
+A fully-featured Haskell library for building [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) servers.
+
+## Features
+
+- **Complete MCP Implementation**: Full support for MCP 2024-11-05 specification
+- **Type-Safe API**: Leverage Haskell's type system for robust MCP servers
+- **Multiple Abstractions**: Both low-level fine-grained control and high-level derived interfaces
+- **Template Haskell Support**: Automatic handler derivation from data types
+- **Pagination Support**: Cursor-based pagination for large result sets
+
+## Supported MCP Features
+
+- ✅ **Prompts**: User-controlled prompt templates with arguments
+- ✅ **Resources**: Application-controlled readable resources  
+- ✅ **Tools**: Model-controlled callable functions
+- ✅ **Initialization Flow**: Complete protocol lifecycle with version negotiation
+- ✅ **Error Handling**: Comprehensive error types and JSON-RPC error responses
+- ✅ **Capabilities**: Proper capability negotiation with sub-capabilities
+
+## Quick Start
+
+```haskell
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell #-}
+
+import MCP.Server
+import MCP.Server.Derive
+
+-- Define your data types
+data MyPrompt = Recipe { idea :: Text } | Shopping { items :: Text }
+data MyResource = Menu | Specials  
+data MyTool = Search { query :: Text } | Order { item :: Text }
+
+-- Implement handlers
+handlePrompt :: MyPrompt -> IO Content
+handlePrompt (Recipe idea) = pure $ ContentText $ "Recipe for " <> idea
+handlePrompt (Shopping items) = pure $ ContentText $ "Shopping list: " <> items
+
+handleResource :: MyResource -> IO Content  
+handleResource Menu = pure $ ContentText "Today's menu..."
+handleResource Specials = pure $ ContentText "Daily specials..."
+
+handleTool :: MyTool -> IO Content
+handleTool (Search query) = pure $ ContentText $ "Search results for " <> query
+handleTool (Order item) = pure $ ContentText $ "Ordered " <> item
+
+-- Derive handlers automatically
+main :: IO ()
+main = runMcpServerStdIn serverInfo handlers
+  where
+    serverInfo = McpServerInfo
+      { serverName = "My MCP Server"
+      , serverVersion = "1.0.0" 
+      , serverInstructions = "A sample MCP server"
+      }
+    handlers = McpServerHandlers
+      { prompts = Just $(derivePromptHandler ''MyPrompt 'handlePrompt)
+      , resources = Just $(deriveResourceHandler ''MyResource 'handleResource)  
+      , tools = Just $(deriveToolHandler ''MyTool 'handleTool)
+      }
+```
+
+## Manual Handler Implementation
+
+For fine-grained control, implement handlers manually:
+
+```haskell
+import MCP.Server
+
+-- Manual handler implementation
+promptListHandler :: Maybe Cursor -> IO (PaginatedResult [PromptDefinition])
+promptGetHandler :: PromptName -> [(ArgumentName, ArgumentValue)] -> IO (Either Error Content)
+-- ... implement your custom logic
+
+main :: IO ()
+main = runMcpServerStdIn serverInfo handlers
+  where
+    handlers = McpServerHandlers
+      { prompts = Just (promptListHandler, promptGetHandler)
+      , resources = Nothing  -- Not supported
+      , tools = Nothing      -- Not supported  
+      }
+```
+
+## Examples
+
+The library includes three complete examples:
+
+- **`examples/HighLevel.hs`**: Manual high-level implementation
+- **`examples/TemplateHaskell.hs`**: Automatic Template Haskell derivation
+
+## Docker Usage
+
+I like to build and publish my MCP servers to Docker - which means that it's much easier to configure assistants such as Claude Desktop to run them. 
+
+```bash
+# Build the image
+docker build -t haskell-mcp-server .
+
+# Run different examples
+docker run -i --entrypoint="/usr/local/bin/template-haskell-example" haskell-mcp-server
+docker run -i --entrypoint="/usr/local/bin/high-level-example" haskell-mcp-server
+```
+
+And then configure Claude by editing `claude_desktop_config.json`:
+
+```json
+{
+    "mcpServers": {
+       "haskell-mcp-server-example": {
+            "command": "docker",
+            "args": [
+                "run",
+                "-i",
+                "--entrypoint=/usr/local/bin/template-haskell-example",
+                "drshade/haskell-mcp-server"
+            ]
+        }
+    }
+}
+```
+
+## Documentation
+
+- [MCP Specification](https://modelcontextprotocol.io/specification/2024-11-05/)
+- [API Documentation](https://hackage.haskell.org/package/mcp-server)
+- [Examples](examples/)
+
+## Contributing
+
+Contributions are welcome! Please see the issue tracker for open issues and feature requests.
+
+## Disclaimer - AI Assistance
+
+I am not sure whether there is any stigma associated with this but Claude helped me write a lot of this library. I started with a very specific specification of what I wanted to achieve and worked shoulder-to-shoulder with Claude to implement and refactor the library until I was happy with it. A few of the features such as the Derive functions are a little out of my comfort zone to have manually written, so I appreciated having an expert guide me here - however I do suspect that this implementation may be sub-par and I do intend to refactor and rewrite large pieces of this through regular maintenance.
+
+## License
+
+BSD-3-Clause
diff --git a/SPEC.md b/SPEC.md
new file mode 100644
--- /dev/null
+++ b/SPEC.md
@@ -0,0 +1,201 @@
+
+# NOTICE
+
+This file is kept for brevity - but served as my starting specification before writing the library. This may no longer be useful and will be superceded by examples in the examples/ folder.
+
+# Haskell MCP Server Library
+
+Fully featured haskell library for building MCP Servers.
+
+## MCP Specification - Model Context Protocol
+
+The MCP spec is a specialisation of the JSON-RPC specification, exposing a number of requests and responses. This library allows developers to build these servers easily in haskell.
+
+This library is compliant with the following MCP specifications for building MCP Servers which can be found here:
+
+| Item | Spec URL |
+| Overview | https://modelcontextprotocol.io/specification/2024-11-05/server |
+| Prompts | https://modelcontextprotocol.io/specification/2024-11-05/server/prompts |
+| Resources | https://modelcontextprotocol.io/specification/2024-11-05/server/resources |
+| Tools | https://modelcontextprotocol.io/specification/2024-11-05/server/tools |
+
+# Library itself
+
+Supports 2 levels of granularity:
+
+## 1. Lowest-level fine grained handling of Prompts, Resources and Tools, e.g.:
+
+Using an example of a grocery shopping MCP server:
+
+```haskell
+
+import MCP.Server.Types (Content(ContentText, ContentImage), Error(InvalidPromptName, MissingRequiredParams, ResourceNotFound, InternalError, UnknownTool), PromptDefinition, ResourceDefinition, ArgumentDefinition, InputSchemaDefinition(..), PaginatedResult(..), Cursor)
+import Network.URI (URI(URI))
+import qualified Data.Map as Map
+
+type PromptName = Text
+type ToolName = Text
+type ArgumentName = Text
+type ArgumentValue = Text
+
+handleListPrompts :: (Monad m) => Maybe Cursor -> m (PaginatedResult [PromptDefinition])
+handleListPrompts cursor =
+    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
+                        }
+                    ]
+                }
+            ]
+        , paginatedNextCursor = Nothing  -- No more pages
+        }
+
+handleListResources :: (Monad m) => Maybe Cursor -> m (PaginatedResult [ResourceDefinition])
+handleListResources cursor = 
+    pure $ PaginatedResult
+        { paginatedItems =
+            [ ResourceDefinition
+                { resourceDefinitionURI = "file:///product_categories"
+                , resourceDefinitionName = "product_categories"
+                , resourceDefinitionDescription = "List of product categories"
+                , resourceDefinitionMimeType = "text/plain"
+                }
+            ]
+        , paginatedNextCursor = Nothing  -- No more pages
+        }
+
+handleListTools :: (Monad m) => Maybe Cursor -> m (PaginatedResult [ToolDefinition])
+handleListTools cursor = 
+    pure $ PaginatedResult
+        { paginatedItems =
+            [ ToolDefinition
+                { toolDefinitionName = "search_for_product" 
+                , toolDefinitionDescription = "Search for products in the catalog"
+                , toolDefinitionInputSchema = 
+                    InputSchemaDefinitionObject
+                        { properties = 
+                            [   ("q"
+                                , InputSchemaDefinitionProperty
+                                    { type = "string"
+                                    , description = "Matching this query, using 'contains' semantics"
+                                    }
+                                )
+                            ,   ("category"
+                                , InputSchemaDefinitionProperty
+                                    { type = "string"
+                                    , description = "Limit to searching within this category (optional)"
+                                    }
+                                )
+                            ]
+                        , required = ["q"]
+                        }
+                }
+            ]
+        , paginatedNextCursor = Nothing  -- No more pages
+        }
+
+handleGetPrompt :: (Monad m) => PromptName -> [(ArgumentName, ArgumentValue)] -> m (Either Error Content)
+handleGetPrompt "recipe" args = do
+    let idea = Map.lookup "idea" $ Map.fromList args  -- idea is required
+     in case idea of
+            Just idea' -> pure $ Right $ ContentText $ "Recipe prompt for " <> idea' <> " ..."
+            Nothing -> pure $ Left $ MissingRequiredParams "field 'idea' is missing"
+handleGetPrompt prompt _ = pure $ Left $ InvalidPromptName $ prompt <> " unknown!"
+
+handleReadResource :: (Monad m) => URI -> m (Either Error Content)
+handleReadResource uri = do
+    case uriPath uri of
+        "/product_categories" -> pure $ Right $ ContentText $ "category 1, category 2"
+        unknown -> pure $ Left $ ResourceNotFound $ "Resouce " <> unknown <> " not found!"
+
+handleCallTool :: (Monad m) => ToolName -> [(ArgumentName, ArgumentValue)] -> m (Either Error Content)
+handleCallTool "search_for_product" args = do
+    let q = Map.lookup "q" $ Map.fromList args                  -- q is required
+    let category = Map.lookup "category" $ Map.fromList args    -- category is optional
+     in case q of
+            Nothing -> pure $ Left $ MissingRequiredParams "field 'q' is missing"
+            Just q' -> pure $ Right $ ContentText $ "product 1, product 2"
+handleCallTool tool _ = pure $ Left $ UnknownTool $ tool <> " unknown!"
+
+-- Running the MCP server using STDIO
+
+main :: IO ()
+main =
+    runMcpServerStdIn
+        McpServerInfo
+            { serverName = "Products available at your Grocery Store"
+            , serverVersion = "0.1.0"
+            , serverInstructions = "Use these tools to fetch information about products available at your grocery store, including prices, ingredients, descriptions etc."
+            }
+        McpServerHandlers
+            { prompts = Just (handleListPrompts, handleGetPrompt)
+            , resources = Just (handleListResources, handleReadResource)
+            , tools = Just (handleListTools, handleCallTool)
+            }
+
+```
+
+## 2. High-level derived interfaces for simpler definitions
+
+High level MCP server definitions take advantage of Template Haskell for generating all the boilerplate of the low-level definitions, but allow for much simpler and type safe definitions such as in the example below.
+
+```haskell
+
+import MCP.Server.Types (Content(ContentText, ContentImage))
+
+data MyPrompt
+    = Recipe { idea :: Text }
+    | Shopping { description :: Text }
+
+data MyResource
+    = ProductCategories
+    | SaleItems
+    | HeadlineBannerAd
+
+data MyTool
+    = SearchForProduct { q :: Text, category :: Maybe Text }
+    | AddToCart { sku :: Text }
+    | Checkout
+
+handlePrompt :: (MonadIO m) => MyPrompt -> m Content
+handlePrompt (Recipe idea) =
+    pure $ ContentText "Recipe prompt goes here..."
+handlePrompt (Shopping description) =
+    pure $ ContentText "Shopping prompt goes here..."
+
+handleResource :: (MonadIO m) => MyResource -> m Content
+handleResource ProductCategories = pure $ ContentText "category 1, category 2"
+handleResource SaleItems = pure $ ContentText "item 1, item 2, item 3"
+handleResource HeadlineBannerAd = pure $ ContentImage { mimeType = "image/png", data = "..." }
+
+handleTool :: (MonadIO m) => MyTool -> m Content
+handleTool (SearchForProduct q category) = pure $ ContentText "search results..."
+handleTool (AddToCart sku) = pure $ ContentText "Item added to cart!"
+handleTool Checkout = pure $ ContentText "checkout completed!"
+
+main =
+    -- Derive the handlers
+    let prompts = $(derivePromptHandler ''MyPrompt 'handlePrompt)
+        resources = $(deriveResourceHandler ''MyResource 'handleResource)
+        tools = $(deriveToolHandler ''MyTool 'handleTool)
+     in runMcpServerStdIn
+        McpServerInfo
+            { serverName = "Products available at your Grocery Store"
+            , serverVersion = "0.1.0"
+            , serverInstructions = "Use these tools to fetch information about products available at your grocery store, including prices, ingredients, descriptions etc."
+            }
+        McpServerHandlers
+            { prompts = Just prompts
+            , resources = Just resources
+            , tools = Just tools
+            }
+
+```
+
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,6 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Main where
+
+main :: IO ()
+main = putStrLn "Hello, World!"
diff --git a/examples/HighLevel.hs b/examples/HighLevel.hs
new file mode 100644
--- /dev/null
+++ b/examples/HighLevel.hs
@@ -0,0 +1,214 @@
+{-# 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)
+            }
diff --git a/examples/TemplateHaskell.hs b/examples/TemplateHaskell.hs
new file mode 100644
--- /dev/null
+++ b/examples/TemplateHaskell.hs
@@ -0,0 +1,52 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+
+module Main where
+
+import           MCP.Server
+import           MCP.Server.Derive
+import           System.IO         (hPutStrLn, stderr)
+import           Types
+
+-- 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!"
+
+main :: IO ()
+main = do
+    hPutStrLn stderr "Starting Template Haskell MCP Server..."
+    hPutStrLn stderr "Using automatic Template Haskell derivation!"
+    hPutStrLn stderr "Ready for JSON-RPC communication"
+
+    -- Derive the handlers using Template Haskell
+    let prompts = $(derivePromptHandler ''MyPrompt 'handlePrompt)
+        resources = $(deriveResourceHandler ''MyResource 'handleResource)
+        tools = $(deriveToolHandler ''MyTool 'handleTool)
+     in runMcpServerStdIn
+        McpServerInfo
+            { serverName = "Template Haskell MCP Server"
+            , serverVersion = "0.3.0"
+            , serverInstructions = "MCP server using Template Haskell for automatic handler derivation"
+            }
+        McpServerHandlers
+            { prompts = Just prompts
+            , resources = Just resources
+            , tools = Just tools
+            }
diff --git a/examples/Types.hs b/examples/Types.hs
new file mode 100644
--- /dev/null
+++ b/examples/Types.hs
@@ -0,0 +1,24 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module Types where
+
+import           Data.Text (Text)
+
+-- 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)
diff --git a/mcp-server.cabal b/mcp-server.cabal
new file mode 100644
--- /dev/null
+++ b/mcp-server.cabal
@@ -0,0 +1,172 @@
+cabal-version: 3.0
+-- The cabal-version field refers to the version of the .cabal specification,
+-- and can be different from the cabal-install (the tool) version and the
+-- Cabal (the library) version you are using. As such, the Cabal (the library)
+-- version used must be equal or greater than the version stated in this field.
+-- Starting from the specification version 2.2, the cabal-version field must be
+-- the first thing in the cabal file.
+--
+-- The name of the package.
+name: mcp-server
+-- The package version.
+-- See the Haskell package versioning policy (PVP) for standards
+-- guiding when and how versions should be incremented.
+-- https://pvp.haskell.org
+-- PVP summary:     +-+------- breaking API changes
+--                  | | +----- non-breaking API additions
+--                  | | | +--- code changes with no API change
+version: 0.1.0.0
+-- A short (one-line) description of the package.
+synopsis: Library for building Model Context Protocol (MCP) servers
+-- A longer description of the package.
+description:
+  A fully featured library for building MCP (Model Context Protocol) servers.
+  Supports both low-level fine-grained handling and high-level derived interfaces
+  for prompts, resources, and tools. Includes JSON-RPC transport, pagination,
+  and stdin/stdout communication.
+
+-- The license under which the package is released.
+license: BSD-3-Clause
+-- The file containing the license text.
+license-file: LICENSE
+-- The package author(s).
+author: Tom Wells
+-- An email address to which users can send suggestions, bug reports, and patches.
+maintainer: drshade@gmail.com
+-- A copyright notice.
+copyright: 2025 Tom Wells
+-- URL for the project homepage or repository
+homepage: https://github.com/drshade/haskell-mcp-server
+bug-reports: https://github.com/drshade/haskell-mcp-server/issues
+-- Package categories for Hackage browsing
+category: Network, Server, Service, MCP, JSON-RPC
+build-type: Simple
+-- Extra doc files to be distributed with the package, such as a CHANGELOG or a README.
+extra-doc-files:
+  CHANGELOG.md
+  README.md
+
+-- Extra source files to be distributed with the package, such as examples, or a tutorial module.
+extra-source-files: SPEC.md
+
+-- Source repository information
+source-repository head
+  type: git
+  location: https://github.com/drshade/haskell-mcp-server.git
+
+common warnings
+  ghc-options: -Wall
+
+library
+  -- Import common warning flags.
+  import: warnings
+  -- Modules exported by the library.
+  exposed-modules:
+    MCP.Server
+    MCP.Server.Derive
+    MCP.Server.JsonRpc
+    MCP.Server.Protocol
+    MCP.Server.Types
+
+  -- Modules included in this library but not exported.
+  -- other-modules:
+  -- LANGUAGE extensions used by modules in this package.
+  -- other-extensions:
+  -- Other library packages from which modules are imported.
+  build-depends:
+    aeson >=2.0 && <3.0,
+    base ^>=4.20.0.0,
+    bytestring >=0.10 && <0.13,
+    containers >=0.6 && <0.8,
+    network-uri >=2.6 && <2.8,
+    template-haskell >=2.16 && <2.23,
+    text >=1.2 && <3.0,
+
+  -- Directories containing source files.
+  hs-source-dirs: src
+  -- Base language which the package is written in.
+  default-language: GHC2024
+
+executable haskell-mcp-server
+  -- Import common warning flags.
+  import: warnings
+  -- .hs or .lhs file containing the Main module.
+  main-is: Main.hs
+  -- Modules included in this executable, other than Main.
+  -- other-modules:
+  -- LANGUAGE extensions used by modules in this package.
+  -- other-extensions:
+  -- Other library packages from which modules are imported.
+  build-depends:
+    base ^>=4.20.0.0,
+    containers >=0.6 && <0.8,
+    mcp-server,
+    network-uri >=2.6 && <2.8,
+    text >=1.2 && <3.0,
+
+  -- Directories containing source files.
+  hs-source-dirs: app
+  -- Base language which the package is written in.
+  default-language: GHC2024
+
+executable high-level-example
+  -- Import common warning flags.
+  import: warnings
+  -- .hs or .lhs file containing the Main module.
+  main-is: HighLevel.hs
+  -- Modules included in this executable, other than Main.
+  -- other-modules:
+  -- LANGUAGE extensions used by modules in this package.
+  -- other-extensions:
+  -- Other library packages from which modules are imported.
+  build-depends:
+    base ^>=4.20.0.0,
+    containers >=0.6 && <0.8,
+    mcp-server,
+    network-uri >=2.6 && <2.8,
+    text >=1.2 && <3.0,
+
+  -- Directories containing source files.
+  hs-source-dirs: examples
+  -- Base language which the package is written in.
+  default-language: GHC2024
+
+executable template-haskell-example
+  -- Import common warning flags.
+  import: warnings
+  -- .hs or .lhs file containing the Main module.
+  main-is: TemplateHaskell.hs
+  -- Modules included in this executable, other than Main.
+  other-modules: Types
+  -- LANGUAGE extensions used by modules in this package.
+  -- other-extensions:
+  -- Other library packages from which modules are imported.
+  build-depends:
+    base ^>=4.20.0.0,
+    mcp-server,
+    text >=1.2 && <3.0,
+
+  -- Directories containing source files.
+  hs-source-dirs: examples
+  -- Base language which the package is written in.
+  default-language: GHC2024
+
+test-suite haskell-mcp-server-test
+  -- Import common warning flags.
+  import: warnings
+  -- Base language which the package is written in.
+  default-language: GHC2024
+  -- Modules included in this executable, other than Main.
+  -- other-modules:
+  -- LANGUAGE extensions used by modules in this package.
+  -- other-extensions:
+  -- The interface type and version of the test suite.
+  type: exitcode-stdio-1.0
+  -- Directories containing source files.
+  hs-source-dirs: test
+  -- The entrypoint to the test suite.
+  main-is: Main.hs
+  -- Test dependencies.
+  build-depends:
+    base ^>=4.20.0.0,
+    mcp-server,
diff --git a/src/MCP/Server.hs b/src/MCP/Server.hs
new file mode 100644
--- /dev/null
+++ b/src/MCP/Server.hs
@@ -0,0 +1,365 @@
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module MCP.Server
+  ( -- * Server Runtime
+    runMcpServer
+  , runMcpServerStdIn
+  , handleMcpMessage
+
+    -- * Re-exports
+  , module MCP.Server.Types
+  ) where
+
+import           Control.Monad          (when)
+import           Control.Monad.IO.Class (MonadIO, liftIO)
+import           Data.Aeson
+import qualified Data.ByteString.Lazy   as BSL
+import qualified Data.Map               as Map
+import           Data.Text              (Text)
+import qualified Data.Text              as T
+import qualified Data.Text.Encoding     as TE
+import qualified Data.Text.IO           as TIO
+import           System.IO              (hFlush, hPutStrLn, stderr, stdout)
+
+import           MCP.Server.JsonRpc
+import           MCP.Server.Protocol
+import           MCP.Server.Types
+
+-- | Extract a brief summary of a JSON-RPC message for logging
+getMessageSummary :: JsonRpcMessage -> String
+getMessageSummary (JsonRpcMessageRequest req) = 
+  "Request[" ++ show (requestId req) ++ "] " ++ T.unpack (requestMethod req)
+getMessageSummary (JsonRpcMessageNotification notif) = 
+  "Notification " ++ T.unpack (notificationMethod notif)
+getMessageSummary (JsonRpcMessageResponse resp) = 
+  "Response[" ++ show (responseId resp) ++ "]"
+
+-- | Validate protocol version and return negotiated version
+validateProtocolVersion :: Text -> Either Text Text
+validateProtocolVersion clientVersion
+  | clientVersion == protocolVersion = Right protocolVersion  -- Exact match
+  | clientVersion == "2024-11-05" = Right "2024-11-05"        -- Accept this version
+  | otherwise = Left $ "Unsupported protocol version: " <> clientVersion <> ". Server supports: " <> protocolVersion
+
+-- | Run an MCP server with custom input/output handlers
+runMcpServer :: (MonadIO m)
+             => McpServerInfo
+             -> McpServerHandlers m
+             -> (Text -> m ())      -- ^ Output handler
+             -> m Text              -- ^ Input handler
+             -> m ()
+runMcpServer serverInfo handlers outputHandler inputHandler = do
+  loop
+  where
+    loop = do
+      input <- inputHandler
+      when (not $ T.null $ T.strip input) $ do
+        liftIO $ hPutStrLn stderr $ "Received request: " ++ T.unpack input
+        case eitherDecode (BSL.fromStrict $ TE.encodeUtf8 input) of
+          Left err -> liftIO $ hPutStrLn stderr $ "Parse error: " ++ err
+          Right jsonValue -> do
+            case parseJsonRpcMessage jsonValue of
+              Left err -> liftIO $ hPutStrLn stderr $ "JSON-RPC parse error: " ++ err
+              Right message -> do
+                liftIO $ hPutStrLn stderr $ "Processing message: " ++ show (getMessageSummary message)
+                response <- handleMcpMessage serverInfo handlers message
+                case response of
+                  Just responseMsg -> do
+                    liftIO $ hPutStrLn stderr $ "Sending response for: " ++ show (getMessageSummary message)
+                    outputHandler $ TE.decodeUtf8 $ BSL.toStrict $ encode $ encodeJsonRpcMessage responseMsg
+                  Nothing -> liftIO $ hPutStrLn stderr $ "No response needed for: " ++ show (getMessageSummary message)
+        loop
+
+-- | Run an MCP server using stdin/stdout
+runMcpServerStdIn :: McpServerInfo -> McpServerHandlers IO -> IO ()
+runMcpServerStdIn serverInfo handlers =
+  runMcpServer serverInfo handlers outputHandler inputHandler
+  where
+    outputHandler text = do
+      TIO.putStrLn text
+      hFlush stdout
+    inputHandler = TIO.getLine
+
+-- | Handle an MCP message and return a response if needed
+handleMcpMessage :: (MonadIO m)
+                 => McpServerInfo
+                 -> McpServerHandlers m
+                 -> JsonRpcMessage
+                 -> m (Maybe JsonRpcMessage)
+handleMcpMessage serverInfo handlers (JsonRpcMessageRequest req) = do
+  response <- case requestMethod req of
+    "initialize" -> handleInitialize serverInfo req
+    "ping" -> handlePing req
+    "prompts/list" -> handlePromptsList handlers req
+    "prompts/get" -> handlePromptsGet handlers req
+    "resources/list" -> handleResourcesList handlers req
+    "resources/read" -> handleResourcesRead handlers req
+    "tools/list" -> handleToolsList handlers req
+    "tools/call" -> handleToolsCall handlers req
+    method -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+      { errorCode = -32601
+      , errorMessage = "Method not found: " <> method
+      , errorData = Nothing
+      }
+  return $ Just $ JsonRpcMessageResponse response
+
+handleMcpMessage _ _ (JsonRpcMessageNotification notif) = do
+  case notificationMethod notif of
+    "notifications/initialized" -> do
+      liftIO $ hPutStrLn stderr "Received initialized notification - server is ready for operation"
+      return ()
+    _ -> do
+      liftIO $ hPutStrLn stderr $ "Received unknown notification: " ++ T.unpack (notificationMethod notif)
+      return ()
+  return Nothing
+
+handleMcpMessage _ _ (JsonRpcMessageResponse _) =
+  return Nothing
+
+-- | Handle initialize request
+handleInitialize :: (MonadIO m) => McpServerInfo -> JsonRpcRequest -> m JsonRpcResponse
+handleInitialize serverInfo req = do
+  case requestParams req of
+    Nothing -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+      { errorCode = -32602
+      , errorMessage = "Missing required parameters for initialize"
+      , errorData = Nothing
+      }
+    Just params ->
+      case fromJSON params of
+        Error err -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+          { errorCode = -32602
+          , errorMessage = "Invalid initialize parameters: " <> T.pack err
+          , errorData = Nothing
+          }
+        Success initReq -> do
+          -- Check protocol version compatibility
+          let clientVersion = initProtocolVersion initReq
+          case validateProtocolVersion clientVersion of
+            Left errorMsg -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+              { errorCode = -32602
+              , errorMessage = errorMsg
+              , errorData = Nothing
+              }
+            Right negotiatedVersion -> do
+              liftIO $ hPutStrLn stderr $ "Client version: " ++ T.unpack clientVersion ++ ", using: " ++ T.unpack negotiatedVersion
+              let capabilities = ServerCapabilities
+                    { capabilityPrompts = Just $ PromptCapabilities { promptListChanged = Nothing }
+                    , capabilityResources = Just $ ResourceCapabilities { resourceSubscribe = Nothing, resourceListChanged = Nothing }
+                    , capabilityTools = Just $ ToolCapabilities { toolListChanged = Nothing }
+                    , capabilityLogging = Nothing  -- Not supported yet
+                    }
+              let response = InitializeResponse
+                    { initRespProtocolVersion = negotiatedVersion
+                    , initRespCapabilities = capabilities
+                    , initRespServerInfo = serverInfo
+                    }
+              return $ makeSuccessResponse (requestId req) (toJSON response)
+
+-- | Handle ping request
+handlePing :: (MonadIO m) => JsonRpcRequest -> m JsonRpcResponse
+handlePing req = return $ makeSuccessResponse (requestId req) (toJSON PongResponse)
+
+-- | Handle prompts/list request
+handlePromptsList :: (MonadIO m) => McpServerHandlers m -> JsonRpcRequest -> m JsonRpcResponse
+handlePromptsList handlers req =
+  case prompts handlers of
+    Nothing -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+      { errorCode = -32601
+      , errorMessage = "Prompts not supported"
+      , errorData = Nothing
+      }
+    Just (listHandler, _) -> do
+      let cursor = case requestParams req of
+            Just params -> case fromJSON params of
+              Success listReq -> promptsListCursor listReq
+              _               -> Nothing
+            Nothing -> Nothing
+      paginatedResult <- listHandler cursor
+      let response = PromptsListResponse
+            { promptsListPrompts = paginatedItems paginatedResult
+            , promptsListNextCursor = paginatedNextCursor paginatedResult
+            }
+      return $ makeSuccessResponse (requestId req) (toJSON response)
+
+-- | Handle prompts/get request
+handlePromptsGet :: (MonadIO m) => McpServerHandlers m -> JsonRpcRequest -> m JsonRpcResponse
+handlePromptsGet handlers req =
+  case prompts handlers of
+    Nothing -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+      { errorCode = -32601
+      , errorMessage = "Prompts not supported"
+      , errorData = Nothing
+      }
+    Just (_, getHandler) -> do
+      case requestParams req of
+        Nothing -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+          { errorCode = -32602
+          , errorMessage = "Missing parameters"
+          , errorData = Nothing
+          }
+        Just params ->
+          case fromJSON params of
+            Error err -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+              { errorCode = -32602
+              , errorMessage = "Invalid parameters: " <> T.pack err
+              , errorData = Nothing
+              }
+            Success getReq -> do
+              let args = maybe [] (map (\(k, v) -> (k, T.pack $ show v)) . Map.toList) (promptsGetArguments getReq)
+              result <- getHandler (promptsGetName getReq) args
+              case result of
+                Left err -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+                  { errorCode = errorCodeFromMcpError err
+                  , errorMessage = errorMessageFromMcpError err
+                  , errorData = Nothing
+                  }
+                Right content -> do
+                  let response = PromptsGetResponse
+                        { promptsGetDescription = Nothing
+                        , promptsGetMessages = [PromptMessage RoleUser content]
+                        }
+                  return $ makeSuccessResponse (requestId req) (toJSON response)
+
+-- | Handle resources/list request
+handleResourcesList :: (MonadIO m) => McpServerHandlers m -> JsonRpcRequest -> m JsonRpcResponse
+handleResourcesList handlers req =
+  case resources handlers of
+    Nothing -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+      { errorCode = -32601
+      , errorMessage = "Resources not supported"
+      , errorData = Nothing
+      }
+    Just (listHandler, _) -> do
+      let cursor = case requestParams req of
+            Just params -> case fromJSON params of
+              Success listReq -> resourcesListCursor listReq
+              _               -> Nothing
+            Nothing -> Nothing
+      paginatedResult <- listHandler cursor
+      let response = ResourcesListResponse
+            { resourcesListResources = paginatedItems paginatedResult
+            , resourcesListNextCursor = paginatedNextCursor paginatedResult
+            }
+      return $ makeSuccessResponse (requestId req) (toJSON response)
+
+-- | Handle resources/read request
+handleResourcesRead :: (MonadIO m) => McpServerHandlers m -> JsonRpcRequest -> m JsonRpcResponse
+handleResourcesRead handlers req =
+  case resources handlers of
+    Nothing -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+      { errorCode = -32601
+      , errorMessage = "Resources not supported"
+      , errorData = Nothing
+      }
+    Just (_, readHandler) -> do
+      case requestParams req of
+        Nothing -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+          { errorCode = -32602
+          , errorMessage = "Missing parameters"
+          , errorData = Nothing
+          }
+        Just params ->
+          case fromJSON params of
+            Error err -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+              { errorCode = -32602
+              , errorMessage = "Invalid parameters: " <> T.pack err
+              , errorData = Nothing
+              }
+            Success readReq -> do
+              result <- readHandler (resourcesReadUri readReq)
+              case result of
+                Left err -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+                  { errorCode = errorCodeFromMcpError err
+                  , errorMessage = errorMessageFromMcpError err
+                  , errorData = Nothing
+                  }
+                Right content -> do
+                  let response = ResourcesReadResponse
+                        { resourcesReadContents = [content]
+                        }
+                  return $ makeSuccessResponse (requestId req) (toJSON response)
+
+-- | Handle tools/list request
+handleToolsList :: (MonadIO m) => McpServerHandlers m -> JsonRpcRequest -> m JsonRpcResponse
+handleToolsList handlers req =
+  case tools handlers of
+    Nothing -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+      { errorCode = -32601
+      , errorMessage = "Tools not supported"
+      , errorData = Nothing
+      }
+    Just (listHandler, _) -> do
+      let cursor = case requestParams req of
+            Just params -> case fromJSON params of
+              Success listReq -> toolsListCursor listReq
+              _               -> Nothing
+            Nothing -> Nothing
+      paginatedResult <- listHandler cursor
+      let response = ToolsListResponse
+            { toolsListTools = paginatedItems paginatedResult
+            , toolsListNextCursor = paginatedNextCursor paginatedResult
+            }
+      return $ makeSuccessResponse (requestId req) (toJSON response)
+
+-- | Handle tools/call request
+handleToolsCall :: (MonadIO m) => McpServerHandlers m -> JsonRpcRequest -> m JsonRpcResponse
+handleToolsCall handlers req =
+  case tools handlers of
+    Nothing -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+      { errorCode = -32601
+      , errorMessage = "Tools not supported"
+      , errorData = Nothing
+      }
+    Just (_, callHandler) -> do
+      case requestParams req of
+        Nothing -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+          { errorCode = -32602
+          , errorMessage = "Missing parameters"
+          , errorData = Nothing
+          }
+        Just params ->
+          case fromJSON params of
+            Error err -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+              { errorCode = -32602
+              , errorMessage = "Invalid parameters: " <> T.pack err
+              , errorData = Nothing
+              }
+            Success callReq -> do
+              let args = maybe [] (map (\(k, v) -> (k, T.pack $ show v)) . Map.toList) (toolsCallArguments callReq)
+              result <- callHandler (toolsCallName callReq) args
+              case result of
+                Left err -> return $ makeErrorResponse (requestId req) $ JsonRpcError
+                  { errorCode = errorCodeFromMcpError err
+                  , errorMessage = errorMessageFromMcpError err
+                  , errorData = Nothing
+                  }
+                Right content -> do
+                  let response = ToolsCallResponse
+                        { toolsCallContent = [content]
+                        , toolsCallIsError = Nothing
+                        }
+                  return $ makeSuccessResponse (requestId req) (toJSON response)
+
+-- | Convert MCP error to JSON-RPC error code
+errorCodeFromMcpError :: Error -> Int
+errorCodeFromMcpError (InvalidPromptName _)     = -32602
+errorCodeFromMcpError (MissingRequiredParams _) = -32602
+errorCodeFromMcpError (ResourceNotFound _)      = -32602
+errorCodeFromMcpError (InternalError _)         = -32603
+errorCodeFromMcpError (UnknownTool _)           = -32602
+errorCodeFromMcpError (InvalidRequest _)        = -32600
+errorCodeFromMcpError (MethodNotFound _)        = -32601
+errorCodeFromMcpError (InvalidParams _)         = -32602
+
+-- | Convert MCP error to JSON-RPC error message
+errorMessageFromMcpError :: Error -> Text
+errorMessageFromMcpError (InvalidPromptName msg) = "Invalid prompt name: " <> msg
+errorMessageFromMcpError (MissingRequiredParams msg) = "Missing required parameters: " <> msg
+errorMessageFromMcpError (ResourceNotFound msg) = "Resource not found: " <> msg
+errorMessageFromMcpError (InternalError msg) = "Internal error: " <> msg
+errorMessageFromMcpError (UnknownTool msg) = "Unknown tool: " <> msg
+errorMessageFromMcpError (InvalidRequest msg) = "Invalid request: " <> msg
+errorMessageFromMcpError (MethodNotFound msg) = "Method not found: " <> msg
+errorMessageFromMcpError (InvalidParams msg) = "Invalid parameters: " <> msg
diff --git a/src/MCP/Server/Derive.hs b/src/MCP/Server/Derive.hs
new file mode 100644
--- /dev/null
+++ b/src/MCP/Server/Derive.hs
@@ -0,0 +1,178 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TemplateHaskell   #-}
+
+module MCP.Server.Derive
+  ( -- * Template Haskell Derivation
+    derivePromptHandler
+  , deriveResourceHandler
+  , deriveToolHandler
+  ) where
+
+import qualified Data.Map            as Map
+import qualified Data.Text           as T
+import           Language.Haskell.TH
+
+import           MCP.Server.Types
+
+-- | Derive prompt handlers from a data type
+-- Usage: $(derivePromptHandler ''MyPrompt 'handlePrompt)
+derivePromptHandler :: Name -> Name -> Q Exp
+derivePromptHandler typeName handlerName = do
+  info <- reify typeName
+  case info of
+    TyConI (DataD _ _ _ _ _constructors _) -> do
+      -- For now, return a basic working implementation
+      listHandlerExp <- [| \_cursor -> pure $ PaginatedResult
+        { paginatedItems =
+            [ PromptDefinition
+                { promptDefinitionName = "recipe"
+                , promptDefinitionDescription = "Generate a detailed recipe"
+                , promptDefinitionArguments =
+                    [ ArgumentDefinition
+                        { argumentDefinitionName = "idea"
+                        , argumentDefinitionDescription = "Recipe idea"
+                        , argumentDefinitionRequired = True
+                        }
+                    ]
+                }
+            , PromptDefinition
+                { promptDefinitionName = "shopping"
+                , promptDefinitionDescription = "Create a shopping list"
+                , promptDefinitionArguments =
+                    [ ArgumentDefinition
+                        { argumentDefinitionName = "description"
+                        , argumentDefinitionDescription = "Shopping description"
+                        , argumentDefinitionRequired = True
+                        }
+                    ]
+                }
+            ]
+        , paginatedNextCursor = Nothing
+        } |]
+
+      getHandlerExp <- [| \name args -> case T.unpack name of
+        "recipe" -> case Map.lookup "idea" (Map.fromList args) of
+          Nothing -> pure $ Left $ MissingRequiredParams "field 'idea' is missing"
+          Just idea -> do
+            content <- $(varE handlerName) (Recipe idea)
+            pure $ Right content
+        "shopping" -> case Map.lookup "description" (Map.fromList args) of
+          Nothing -> pure $ Left $ MissingRequiredParams "field 'description' is missing"
+          Just desc -> do
+            content <- $(varE handlerName) (Shopping desc)
+            pure $ Right content
+        _ -> pure $ Left $ InvalidPromptName $ "Unknown prompt: " <> name |]
+
+      return $ TupE [Just listHandlerExp, Just getHandlerExp]
+    _ -> fail $ "derivePromptHandler: " ++ show typeName ++ " is not a data type"
+
+-- | Derive resource handlers from a data type
+-- Usage: $(deriveResourceHandler ''MyResource 'handleResource)
+deriveResourceHandler :: Name -> Name -> Q Exp
+deriveResourceHandler typeName handlerName = do
+  info <- reify typeName
+  case info of
+    TyConI (DataD _ _ _ _ _constructors _) -> do
+      listHandlerExp <- [| \_cursor -> pure $ PaginatedResult
+        { paginatedItems =
+            [ ResourceDefinition
+                { resourceDefinitionURI = "resource://productcategories"
+                , resourceDefinitionName = "productcategories"
+                , resourceDefinitionDescription = Just "Product categories"
+                , resourceDefinitionMimeType = Just "text/plain"
+                }
+            , ResourceDefinition
+                { resourceDefinitionURI = "resource://saleitems"
+                , resourceDefinitionName = "saleitems"
+                , resourceDefinitionDescription = Just "Sale items"
+                , resourceDefinitionMimeType = Just "text/plain"
+                }
+            , ResourceDefinition
+                { resourceDefinitionURI = "resource://headlinebannerad"
+                , resourceDefinitionName = "headlinebannerad"
+                , resourceDefinitionDescription = Just "Headline banner ad"
+                , resourceDefinitionMimeType = Just "text/plain"
+                }
+            ]
+        , paginatedNextCursor = Nothing
+        } |]
+
+      readHandlerExp <- [| \uri -> case show uri of
+        "resource://productcategories" -> Right <$> $(varE handlerName) ProductCategories
+        "resource://saleitems" -> Right <$> $(varE handlerName) SaleItems
+        "resource://headlinebannerad" -> Right <$> $(varE handlerName) HeadlineBannerAd
+        unknown -> pure $ Left $ ResourceNotFound $ "Resource not found: " <> T.pack unknown |]
+
+      return $ TupE [Just listHandlerExp, Just readHandlerExp]
+    _ -> fail $ "deriveResourceHandler: " ++ show typeName ++ " is not a data type"
+
+-- | Derive tool handlers from a data type
+-- Usage: $(deriveToolHandler ''MyTool 'handleTool)
+deriveToolHandler :: Name -> Name -> Q Exp
+deriveToolHandler typeName handlerName = do
+  info <- reify typeName
+  case info of
+    TyConI (DataD _ _ _ _ _constructors _) -> do
+      listHandlerExp <- [| \_cursor -> pure $ PaginatedResult
+        { paginatedItems =
+            [ ToolDefinition
+                { toolDefinitionName = "searchforproduct"
+                , toolDefinitionDescription = "Search for products"
+                , toolDefinitionInputSchema = InputSchemaDefinitionObject
+                    { properties =
+                        [ ("q", InputSchemaDefinitionProperty
+                            { propertyType = "string"
+                            , propertyDescription = "Search query"
+                            })
+                        , ("category", InputSchemaDefinitionProperty
+                            { propertyType = "string"
+                            , propertyDescription = "Category filter"
+                            })
+                        ]
+                    , required = ["q"]
+                    }
+                }
+            , ToolDefinition
+                { toolDefinitionName = "addtocart"
+                , toolDefinitionDescription = "Add item to cart"
+                , toolDefinitionInputSchema = InputSchemaDefinitionObject
+                    { properties =
+                        [ ("sku", InputSchemaDefinitionProperty
+                            { propertyType = "string"
+                            , propertyDescription = "Product SKU"
+                            })
+                        ]
+                    , required = ["sku"]
+                    }
+                }
+            , ToolDefinition
+                { toolDefinitionName = "checkout"
+                , toolDefinitionDescription = "Complete checkout"
+                , toolDefinitionInputSchema = InputSchemaDefinitionObject
+                    { properties = []
+                    , required = []
+                    }
+                }
+            ]
+        , paginatedNextCursor = Nothing
+        } |]
+
+      callHandlerExp <- [| \name args -> case T.unpack name of
+        "searchforproduct" -> 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 <- $(varE handlerName) (SearchForProduct q category)
+            pure $ Right content
+        "addtocart" -> case Map.lookup "sku" (Map.fromList args) of
+          Nothing -> pure $ Left $ MissingRequiredParams "field 'sku' is missing"
+          Just sku -> do
+            content <- $(varE handlerName) (AddToCart sku)
+            pure $ Right content
+        "checkout" -> do
+          content <- $(varE handlerName) Checkout
+          pure $ Right content
+        _ -> pure $ Left $ UnknownTool $ "Unknown tool: " <> name |]
+
+      return $ TupE [Just listHandlerExp, Just callHandlerExp]
+    _ -> fail $ "deriveToolHandler: " ++ show typeName ++ " is not a data type"
diff --git a/src/MCP/Server/JsonRpc.hs b/src/MCP/Server/JsonRpc.hs
new file mode 100644
--- /dev/null
+++ b/src/MCP/Server/JsonRpc.hs
@@ -0,0 +1,179 @@
+{-# 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
diff --git a/src/MCP/Server/Protocol.hs b/src/MCP/Server/Protocol.hs
new file mode 100644
--- /dev/null
+++ b/src/MCP/Server/Protocol.hs
@@ -0,0 +1,253 @@
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module MCP.Server.Protocol
+  ( -- * MCP Protocol Messages
+    InitializeRequest(..)
+  , InitializeResponse(..)
+  , InitializedNotification(..)
+  , PingRequest(..)
+  , PongResponse(..)
+  
+    -- * Prompts Protocol
+  , PromptsListRequest(..)
+  , PromptsListResponse(..)
+  , PromptsGetRequest(..)
+  , PromptsGetResponse(..)
+  , PromptMessage(..)
+  , MessageRole(..)
+  
+    -- * Resources Protocol
+  , ResourcesListRequest(..)
+  , ResourcesListResponse(..)
+  , ResourcesReadRequest(..)
+  , ResourcesReadResponse(..)
+  
+    -- * Tools Protocol
+  , ToolsListRequest(..)
+  , ToolsListResponse(..)
+  , ToolsCallRequest(..)
+  , ToolsCallResponse(..)
+  
+    -- * Common Types
+  , ListChangedNotification(..)
+  
+    -- * Protocol Functions
+  , protocolVersion
+  ) where
+
+import Data.Text (Text)
+import Data.Aeson
+import Data.Map (Map)
+import Network.URI (URI, parseURI)
+import GHC.Generics (Generic)
+import MCP.Server.Types
+
+protocolVersion :: Text
+protocolVersion = "2024-11-05"
+
+
+-- | Initialize request
+data InitializeRequest = InitializeRequest
+  { initProtocolVersion :: Text
+  , initCapabilities :: Value
+  , initClientInfo :: Value
+  } deriving (Show, Eq, Generic)
+
+instance FromJSON InitializeRequest where
+  parseJSON = withObject "InitializeRequest" $ \o -> InitializeRequest
+    <$> o .: "protocolVersion"
+    <*> o .: "capabilities"
+    <*> o .: "clientInfo"
+
+-- | Initialize response
+data InitializeResponse = InitializeResponse
+  { initRespProtocolVersion :: Text
+  , initRespCapabilities :: ServerCapabilities
+  , initRespServerInfo :: McpServerInfo
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON InitializeResponse where
+  toJSON resp = object
+    [ "protocolVersion" .= initRespProtocolVersion resp
+    , "capabilities" .= initRespCapabilities resp
+    , "serverInfo" .= object
+        [ "name" .= serverName (initRespServerInfo resp)
+        , "version" .= serverVersion (initRespServerInfo resp)
+        , "instructions" .= serverInstructions (initRespServerInfo resp)
+        ]
+    ]
+
+-- | Initialized notification (no parameters)
+data InitializedNotification = InitializedNotification
+  deriving (Show, Eq, Generic)
+
+instance FromJSON InitializedNotification where
+  parseJSON _ = return InitializedNotification
+
+-- | Ping request (no parameters)
+data PingRequest = PingRequest
+  deriving (Show, Eq, Generic)
+
+instance FromJSON PingRequest where
+  parseJSON _ = return PingRequest
+
+-- | Pong response (empty object)
+data PongResponse = PongResponse
+  deriving (Show, Eq, Generic)
+
+instance ToJSON PongResponse where
+  toJSON PongResponse = object []
+
+-- | Message role for prompts
+data MessageRole = RoleUser | RoleAssistant
+  deriving (Show, Eq, Generic)
+
+instance ToJSON MessageRole where
+  toJSON RoleUser = "user"
+  toJSON RoleAssistant = "assistant"
+
+-- | Prompt message
+data PromptMessage = PromptMessage
+  { promptMessageRole :: MessageRole
+  , promptMessageContent :: Content
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON PromptMessage where
+  toJSON msg = object
+    [ "role" .= promptMessageRole msg
+    , "content" .= promptMessageContent msg
+    ]
+
+-- | Prompts list request
+data PromptsListRequest = PromptsListRequest
+  { promptsListCursor :: Maybe Cursor
+  } deriving (Show, Eq, Generic)
+
+instance FromJSON PromptsListRequest where
+  parseJSON = withObject "PromptsListRequest" $ \o -> PromptsListRequest
+    <$> o .:? "cursor"
+
+-- | Prompts list response
+data PromptsListResponse = PromptsListResponse
+  { promptsListPrompts :: [PromptDefinition]
+  , promptsListNextCursor :: Maybe Cursor
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON PromptsListResponse where
+  toJSON resp = object $
+    [ "prompts" .= promptsListPrompts resp
+    ] ++ maybe [] (\c -> ["nextCursor" .= c]) (promptsListNextCursor resp)
+
+-- | Prompts get request
+data PromptsGetRequest = PromptsGetRequest
+  { promptsGetName :: Text
+  , promptsGetArguments :: Maybe (Map Text Value)
+  } deriving (Show, Eq, Generic)
+
+instance FromJSON PromptsGetRequest where
+  parseJSON = withObject "PromptsGetRequest" $ \o -> PromptsGetRequest
+    <$> o .: "name"
+    <*> o .:? "arguments"
+
+-- | Prompts get response
+data PromptsGetResponse = PromptsGetResponse
+  { promptsGetDescription :: Maybe Text
+  , promptsGetMessages :: [PromptMessage]
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON PromptsGetResponse where
+  toJSON resp = object $
+    [ "messages" .= promptsGetMessages resp
+    ] ++ maybe [] (\d -> ["description" .= d]) (promptsGetDescription resp)
+
+-- | Resources list request
+data ResourcesListRequest = ResourcesListRequest
+  { resourcesListCursor :: Maybe Cursor
+  } deriving (Show, Eq, Generic)
+
+instance FromJSON ResourcesListRequest where
+  parseJSON = withObject "ResourcesListRequest" $ \o -> ResourcesListRequest
+    <$> o .:? "cursor"
+
+-- | Resources list response
+data ResourcesListResponse = ResourcesListResponse
+  { resourcesListResources :: [ResourceDefinition]
+  , resourcesListNextCursor :: Maybe Cursor
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON ResourcesListResponse where
+  toJSON resp = object $
+    [ "resources" .= resourcesListResources resp
+    ] ++ maybe [] (\c -> ["nextCursor" .= c]) (resourcesListNextCursor resp)
+
+-- | Resources read request
+data ResourcesReadRequest = ResourcesReadRequest
+  { resourcesReadUri :: URI
+  } deriving (Show, Eq, Generic)
+
+instance FromJSON ResourcesReadRequest where
+  parseJSON = withObject "ResourcesReadRequest" $ \o -> do
+    uriText <- o .: "uri"
+    case parseURI uriText of
+      Just uri -> return $ ResourcesReadRequest uri
+      Nothing -> fail "Invalid URI"
+
+-- | Resources read response
+data ResourcesReadResponse = ResourcesReadResponse
+  { resourcesReadContents :: [Content]
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON ResourcesReadResponse where
+  toJSON resp = object
+    [ "contents" .= resourcesReadContents resp
+    ]
+
+-- | Tools list request
+data ToolsListRequest = ToolsListRequest
+  { toolsListCursor :: Maybe Cursor
+  } deriving (Show, Eq, Generic)
+
+instance FromJSON ToolsListRequest where
+  parseJSON = withObject "ToolsListRequest" $ \o -> ToolsListRequest
+    <$> o .:? "cursor"
+
+-- | Tools list response
+data ToolsListResponse = ToolsListResponse
+  { toolsListTools :: [ToolDefinition]
+  , toolsListNextCursor :: Maybe Cursor
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON ToolsListResponse where
+  toJSON resp = object $
+    [ "tools" .= toolsListTools resp
+    ] ++ maybe [] (\c -> ["nextCursor" .= c]) (toolsListNextCursor resp)
+
+-- | Tools call request
+data ToolsCallRequest = ToolsCallRequest
+  { toolsCallName :: Text
+  , toolsCallArguments :: Maybe (Map Text Value)
+  } deriving (Show, Eq, Generic)
+
+instance FromJSON ToolsCallRequest where
+  parseJSON = withObject "ToolsCallRequest" $ \o -> ToolsCallRequest
+    <$> o .: "name"
+    <*> o .:? "arguments"
+
+-- | Tools call response
+data ToolsCallResponse = ToolsCallResponse
+  { toolsCallContent :: [Content]
+  , toolsCallIsError :: Maybe Bool
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON ToolsCallResponse where
+  toJSON resp = object $
+    [ "content" .= toolsCallContent resp
+    ] ++ maybe [] (\e -> ["isError" .= e]) (toolsCallIsError resp)
+
+-- | List changed notification
+data ListChangedNotification = ListChangedNotification
+  deriving (Show, Eq, Generic)
+
+instance ToJSON ListChangedNotification where
+  toJSON ListChangedNotification = object []
diff --git a/src/MCP/Server/Types.hs b/src/MCP/Server/Types.hs
new file mode 100644
--- /dev/null
+++ b/src/MCP/Server/Types.hs
@@ -0,0 +1,323 @@
+{-# LANGUAGE DeriveGeneric     #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module MCP.Server.Types
+  ( -- * Content Types
+    Content(..)
+  , ContentImageData(..)
+  , ContentResourceData(..)
+
+    -- * Error Types
+  , Error(..)
+
+    -- * Definition Types
+  , PromptDefinition(..)
+  , ResourceDefinition(..)
+  , ToolDefinition(..)
+  , ArgumentDefinition(..)
+  , InputSchemaDefinition(..)
+  , InputSchemaDefinitionProperty(..)
+
+    -- * Server Types
+  , McpServerInfo(..)
+  , McpServerHandlers(..)
+  , ServerCapabilities(..)
+  , PromptCapabilities(..)
+  , ResourceCapabilities(..)
+  , ToolCapabilities(..)
+  , LoggingCapabilities(..)
+
+    -- * Request/Response Types
+  , PromptListHandler
+  , PromptGetHandler
+  , ResourceListHandler
+  , ResourceReadHandler
+  , ToolListHandler
+  , ToolCallHandler
+  , PaginatedResult(..)
+  , Cursor(..)
+
+    -- * Basic Types
+  , PromptName
+  , ToolName
+  , ArgumentName
+  , ArgumentValue
+  ) where
+
+import           Data.Aeson
+import           Data.Aeson.Key   (fromText)
+import           Data.Aeson.Types (Parser)
+import           Data.Maybe       (catMaybes)
+import           Data.Text        (Text)
+import qualified Data.Text        as T
+import           GHC.Generics     (Generic)
+import           Network.URI      (URI)
+
+type PromptName = Text
+type ToolName = Text
+type ArgumentName = Text
+type ArgumentValue = Text
+
+-- | Content that can be returned by prompts, resources, and tools
+data Content
+  = ContentText Text
+  | ContentImage ContentImageData
+  | ContentResource ContentResourceData
+  deriving (Show, Eq, Generic)
+
+instance ToJSON Content where
+  toJSON (ContentText text) = object
+    [ "type" .= ("text" :: Text)
+    , "text" .= text
+    ]
+  toJSON (ContentImage img) = object
+    [ "type" .= ("image" :: Text)
+    , "data" .= contentImageData img
+    , "mimeType" .= contentImageMimeType img
+    ]
+  toJSON (ContentResource res) = object
+    [ "type" .= ("resource" :: Text)
+    , "resource" .= object
+        [ "uri" .= contentResourceUri res
+        , "mimeType" .= contentResourceMimeType res
+        ]
+    ]
+
+instance FromJSON Content where
+  parseJSON = withObject "Content" $ \o -> do
+    contentType <- o .: "type" :: Parser Text
+    case contentType of
+      "text" -> ContentText <$> o .: "text"
+      "image" -> do
+        imgData <- o .: "data"
+        mimeType <- o .: "mimeType"
+        return $ ContentImage $ ContentImageData imgData mimeType
+      "resource" -> do
+        res <- o .: "resource"
+        uri <- res .: "uri"
+        mimeType <- res .:? "mimeType"
+        return $ ContentResource $ ContentResourceData uri mimeType
+      _ -> fail $ "Unknown content type: " ++ T.unpack contentType
+
+data ContentImageData = ContentImageData
+  { contentImageData     :: Text
+  , contentImageMimeType :: Text
+  } deriving (Show, Eq, Generic)
+
+data ContentResourceData = ContentResourceData
+  { contentResourceUri      :: URI
+  , contentResourceMimeType :: Maybe Text
+  } deriving (Show, Eq, Generic)
+
+-- | MCP protocol errors
+data Error
+  = InvalidPromptName Text
+  | MissingRequiredParams Text
+  | ResourceNotFound Text
+  | InternalError Text
+  | UnknownTool Text
+  | InvalidRequest Text
+  | MethodNotFound Text
+  | InvalidParams Text
+  deriving (Show, Eq, Generic)
+
+instance ToJSON Error where
+  toJSON err = object
+    [ "code" .= errorCode err
+    , "message" .= errorMessage err
+    ]
+    where
+      errorCode :: Error -> Int
+      errorCode (InvalidPromptName _)     = -32602
+      errorCode (MissingRequiredParams _) = -32602
+      errorCode (ResourceNotFound _)      = -32602
+      errorCode (InternalError _)         = -32603
+      errorCode (UnknownTool _)           = -32602
+      errorCode (InvalidRequest _)        = -32600
+      errorCode (MethodNotFound _)        = -32601
+      errorCode (InvalidParams _)         = -32602
+
+      errorMessage :: Error -> Text
+      errorMessage (InvalidPromptName msg) = "Invalid prompt name: " <> msg
+      errorMessage (MissingRequiredParams msg) = "Missing required parameters: " <> msg
+      errorMessage (ResourceNotFound msg) = "Resource not found: " <> msg
+      errorMessage (InternalError msg) = "Internal error: " <> msg
+      errorMessage (UnknownTool msg) = "Unknown tool: " <> msg
+      errorMessage (InvalidRequest msg) = "Invalid request: " <> msg
+      errorMessage (MethodNotFound msg) = "Method not found: " <> msg
+      errorMessage (InvalidParams msg) = "Invalid parameters: " <> msg
+
+-- | Prompt definition
+data PromptDefinition = PromptDefinition
+  { promptDefinitionName        :: Text
+  , promptDefinitionDescription :: Text
+  , promptDefinitionArguments   :: [ArgumentDefinition]
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON PromptDefinition where
+  toJSON def = object
+    [ "name" .= promptDefinitionName def
+    , "description" .= promptDefinitionDescription def
+    , "arguments" .= promptDefinitionArguments def
+    ]
+
+-- | Resource definition
+data ResourceDefinition = ResourceDefinition
+  { resourceDefinitionURI         :: Text
+  , resourceDefinitionName        :: Text
+  , resourceDefinitionDescription :: Maybe Text
+  , resourceDefinitionMimeType    :: Maybe Text
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON ResourceDefinition where
+  toJSON def = object $
+    [ "uri" .= resourceDefinitionURI def
+    , "name" .= resourceDefinitionName def
+    ] ++
+    maybe [] (\d -> ["description" .= d]) (resourceDefinitionDescription def) ++
+    maybe [] (\m -> ["mimeType" .= m]) (resourceDefinitionMimeType def)
+
+-- | Tool definition
+data ToolDefinition = ToolDefinition
+  { toolDefinitionName        :: Text
+  , toolDefinitionDescription :: Text
+  , toolDefinitionInputSchema :: InputSchemaDefinition
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON ToolDefinition where
+  toJSON def = object
+    [ "name" .= toolDefinitionName def
+    , "description" .= toolDefinitionDescription def
+    , "inputSchema" .= toolDefinitionInputSchema def
+    ]
+
+-- | Argument definition for prompts
+data ArgumentDefinition = ArgumentDefinition
+  { argumentDefinitionName        :: Text
+  , argumentDefinitionDescription :: Text
+  , argumentDefinitionRequired    :: Bool
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON ArgumentDefinition where
+  toJSON def = object
+    [ "name" .= argumentDefinitionName def
+    , "description" .= argumentDefinitionDescription def
+    , "required" .= argumentDefinitionRequired def
+    ]
+
+-- | Input schema definition for tools
+data InputSchemaDefinition = InputSchemaDefinitionObject
+  { properties :: [(Text, InputSchemaDefinitionProperty)]
+  , required   :: [Text]
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON InputSchemaDefinition where
+  toJSON (InputSchemaDefinitionObject props req) = object
+    [ "type" .= ("object" :: Text)
+    , "properties" .= object (map (\(k, v) -> fromText k .= v) props)
+    , "required" .= req
+    ]
+
+data InputSchemaDefinitionProperty = InputSchemaDefinitionProperty
+  { propertyType        :: Text
+  , propertyDescription :: Text
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON InputSchemaDefinitionProperty where
+  toJSON prop = object
+    [ "type" .= propertyType prop
+    , "description" .= propertyDescription prop
+    ]
+
+-- | Server information
+data McpServerInfo = McpServerInfo
+  { serverName         :: Text
+  , serverVersion      :: Text
+  , serverInstructions :: Text
+  } deriving (Show, Eq, Generic)
+
+-- | Individual capability objects
+data PromptCapabilities = PromptCapabilities
+  { promptListChanged :: Maybe Bool
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON PromptCapabilities where
+  toJSON caps = object $ catMaybes
+    [ fmap ("listChanged" .=) (promptListChanged caps)
+    ]
+
+data ResourceCapabilities = ResourceCapabilities
+  { resourceSubscribe   :: Maybe Bool
+  , resourceListChanged :: Maybe Bool
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON ResourceCapabilities where
+  toJSON caps = object $ catMaybes
+    [ fmap ("subscribe" .=) (resourceSubscribe caps)
+    , fmap ("listChanged" .=) (resourceListChanged caps)
+    ]
+
+data ToolCapabilities = ToolCapabilities
+  { toolListChanged :: Maybe Bool
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON ToolCapabilities where
+  toJSON caps = object $ catMaybes
+    [ fmap ("listChanged" .=) (toolListChanged caps)
+    ]
+
+data LoggingCapabilities = LoggingCapabilities
+  { -- No specific sub-capabilities for logging yet
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON LoggingCapabilities where
+  toJSON _ = object []
+
+-- | Server capabilities
+data ServerCapabilities = ServerCapabilities
+  { capabilityPrompts   :: Maybe PromptCapabilities
+  , capabilityResources :: Maybe ResourceCapabilities
+  , capabilityTools     :: Maybe ToolCapabilities
+  , capabilityLogging   :: Maybe LoggingCapabilities
+  } deriving (Show, Eq, Generic)
+
+instance ToJSON ServerCapabilities where
+  toJSON caps = object $ catMaybes
+    [ fmap ("prompts" .=) (capabilityPrompts caps)
+    , fmap ("resources" .=) (capabilityResources caps)
+    , fmap ("tools" .=) (capabilityTools caps)
+    , fmap ("logging" .=) (capabilityLogging caps)
+    ]
+
+-- | Cursor for pagination
+newtype Cursor = Cursor Text
+  deriving (Show, Eq, Generic)
+
+instance ToJSON Cursor where
+  toJSON (Cursor c) = toJSON c
+
+instance FromJSON Cursor where
+  parseJSON = fmap Cursor . parseJSON
+
+-- | Pagination result wrapper
+data PaginatedResult a = PaginatedResult
+  { paginatedItems      :: a
+  , paginatedNextCursor :: Maybe Cursor
+  } deriving (Show, Eq, Generic)
+
+-- | Handler type definitions
+type PromptListHandler m = Maybe Cursor -> m (PaginatedResult [PromptDefinition])
+type PromptGetHandler m = PromptName -> [(ArgumentName, ArgumentValue)] -> m (Either Error Content)
+
+type ResourceListHandler m = Maybe Cursor -> m (PaginatedResult [ResourceDefinition])
+type ResourceReadHandler m = URI -> m (Either Error Content)
+
+type ToolListHandler m = Maybe Cursor -> m (PaginatedResult [ToolDefinition])
+type ToolCallHandler m = ToolName -> [(ArgumentName, ArgumentValue)] -> m (Either Error Content)
+
+-- | Server handlers
+data McpServerHandlers m = McpServerHandlers
+  { prompts   :: Maybe (PromptListHandler m, PromptGetHandler m)
+  , resources :: Maybe (ResourceListHandler m, ResourceReadHandler m)
+  , tools     :: Maybe (ToolListHandler m, ToolCallHandler m)
+  }
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,4 @@
+module Main (main) where
+
+main :: IO ()
+main = putStrLn "Test suite not yet implemented."
