diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -3,6 +3,8 @@
 import McpHoogle (runServer, runServerWithDb)
 import Hoogle (defaultDatabaseLocation, hoogle)
 import Options.Applicative
+import Data.Version (showVersion)
+import Paths_mcp_hoogle (version)
 
 data Command
   = Generate GenerateOpts
@@ -41,7 +43,7 @@
     ))
 
 opts :: ParserInfo Command
-opts = info (commandParser <**> helper)
+opts = info (commandParser <**> helper <**> simpleVersioner ("mcp-hoogle " <> showVersion version))
   ( fullDesc
   <> progDesc "MCP server exposing Hoogle search over local Haskell dependencies"
   <> header "mcp-hoogle - Hoogle search via Model Context Protocol"
diff --git a/mcp-hoogle.cabal b/mcp-hoogle.cabal
--- a/mcp-hoogle.cabal
+++ b/mcp-hoogle.cabal
@@ -1,7 +1,7 @@
 cabal-version:      3.0
 
 name:           mcp-hoogle
-version:        0.1.0
+version:        0.2.0
 synopsis:       MCP server exposing Hoogle search over local project dependencies
 description:    An MCP (Model Context Protocol) server that exposes Hoogle search
                 over your project's local Haskell dependencies. Run from within a
@@ -77,19 +77,31 @@
       McpHoogle
       McpHoogle.Tools
       McpHoogle.Format
+  autogen-modules:
+      Paths_mcp_hoogle
+  other-modules:
+      Paths_mcp_hoogle
   hs-source-dirs:
       src
   build-depends:
       hoogle >=5.0 && <5.1
+    -- 0.1.0.17 added protocol version negotiation; older versions reject
+    -- Claude Code's 2024-11-05 handshake and tools silently don't appear.
     , mcp-server >=0.1.0.17 && <0.2
     , text >=1.2 && <2.2
     , directory >=1.3 && <1.4
+    , stm >=2.5 && <2.6
+    , time >=1.9 && <1.15
 
 executable mcp-hoogle
   import: common-options
   main-is: Main.hs
   hs-source-dirs:
       app
+  autogen-modules:
+      Paths_mcp_hoogle
+  other-modules:
+      Paths_mcp_hoogle
   build-depends:
       mcp-hoogle
     , hoogle >=5.0 && <5.1
@@ -110,3 +122,5 @@
     , hoogle >=5.0 && <5.1
     , text >=1.2 && <2.2
     , directory >=1.3 && <1.4
+    , stm >=2.5 && <2.6
+    , time >=1.9 && <1.15
diff --git a/src/McpHoogle.hs b/src/McpHoogle.hs
--- a/src/McpHoogle.hs
+++ b/src/McpHoogle.hs
@@ -4,16 +4,23 @@
 -- This module wires together the Hoogle database, the tool definitions from
 -- "McpHoogle.Tools", and the @mcp-server@ library's stdio transport.
 -- It loads the Hoogle database into an 'IORef' so it can be swapped at
--- runtime (via the @regenerate_database@ tool), then enters the MCP
--- request\/response loop reading JSON-RPC from stdin and writing to stdout.
+-- runtime (via the @regenerate_database@ or @reload_database@ tools),
+-- then enters the MCP request\/response loop reading JSON-RPC from stdin
+-- and writing to stdout.
+--
+-- The server starts even without an existing database — searches return
+-- "No database loaded" until one is generated or reloaded.
 module McpHoogle
   ( runServer
   , runServerWithDb
   )
 where
 
-import Data.IORef (newIORef)
+import Control.Concurrent.STM (TVar, newTVarIO)
+import Data.IORef (IORef, newIORef)
 import Data.Text qualified as Text
+import Data.Version (showVersion)
+import Hoogle qualified (Database)
 import Hoogle (withDatabase, defaultDatabaseLocation)
 import MCP.Server (runMcpServerStdio)
 import MCP.Server.Types
@@ -22,22 +29,23 @@
   , Content(..)
   )
 import MCP.Server.Derive (deriveToolHandlerWithDescription)
-import McpHoogle.Tools (HoogleTool(..), handleTool, toolDescriptions)
+import McpHoogle.Tools (HoogleTool(..), RegenState(..), handleTool, toolDescriptions)
+import Paths_mcp_hoogle (version)
 import System.Directory (doesFileExist)
 
 -- | Run the MCP server using the default Hoogle database location
 -- (@~\/.hoogle\/default-haskell-*.hoo@).
 --
--- Errors immediately if no database is found — the user should run
--- @mcp-hoogle generate@ first.
+-- If no database exists, the server starts anyway with an empty database
+-- ref — the agent can call @regenerate_database@ or @reload_database@ to
+-- populate it without restarting.
 runServer :: IO ()
 runServer = do
   defaultPath <- defaultDatabaseLocation
   exists <- doesFileExist defaultPath
   if exists
     then runServerWithDb defaultPath
-    else error $ "Hoogle database not found at: " <> defaultPath
-      <> "\nRun 'mcp-hoogle generate' to create it, or pass --database PATH."
+    else runServerEmpty
 
 -- | Run the MCP server with an explicit database path.
 --
@@ -47,46 +55,57 @@
 runServerWithDb :: FilePath -> IO ()
 runServerWithDb databasePath =
   withDatabase databasePath $ \database -> do
-    databaseRef <- newIORef database
-    let serverInfo :: McpServerInfo
-        serverInfo = McpServerInfo
-          { serverName = "mcp-hoogle"
-          , serverVersion = "0.1.0"
-          , serverInstructions = Text.unlines
-              [ "Hoogle search for Haskell types, functions, and modules."
-              , ""
-              , "USE THESE TOOLS INSTEAD OF:"
-              , "- Web searching for Haskell documentation"
-              , "- Running `hoogle` or `mcp-hoogle` CLI commands"
-              , "- Fetching Hackage pages with curl/w3m"
-              , ""
-              , "AVAILABLE TOOLS:"
-              , "- search: Find functions by name, keyword, or type signature (e.g. \"map\", \"[a] -> Int\")"
-              , "- search_type: Search specifically by type signature"
-              , "- lookup_module: Browse all exports of a module (e.g. \"Data.Map\")"
-              , "- regenerate_database: Re-index after entering a different project's nix-shell"
-              , ""
-              , "DATABASE SETUP:"
-              , "If no database exists, run `mcp-hoogle generate` from an"
-              , "environment where `ghc-pkg` is on PATH (so it can discover"
-              , "installed packages). For nix-based projects this means running"
-              , "from inside the project's nix-shell:"
-              , "  nix-shell --run 'mcp-hoogle generate'"
-              , "For cabal/stack projects, just run `mcp-hoogle generate` directly"
-              , "(GHC tools are already on PATH)."
-              , "This only needs to be done once per project. The database persists"
-              , "at ~/.hoogle/ and is reused across sessions."
-              ]
-          }
+    databaseRef <- newIORef (Just database)
+    regenStateVar <- newTVarIO RegenIdle
+    runMcpServerStdio serverInfo (handlers databaseRef regenStateVar)
 
-        toolHandler :: HoogleTool -> IO Content
-        toolHandler tool = ContentText <$> handleTool databaseRef tool
+-- | Run the MCP server without a database.
+--
+-- The server starts and exposes tools, but searches return "No database
+-- loaded" until the agent calls @regenerate_database@ or @reload_database@.
+runServerEmpty :: IO ()
+runServerEmpty = do
+  databaseRef <- newIORef Nothing
+  regenStateVar <- newTVarIO RegenIdle
+  runMcpServerStdio serverInfo (handlers databaseRef regenStateVar)
 
-        handlers :: McpServerHandlers IO
-        handlers = McpServerHandlers
-          { prompts = Nothing
-          , resources = Nothing
-          , tools = Just $(deriveToolHandlerWithDescription ''HoogleTool 'toolHandler toolDescriptions)
-          }
+-- | Server metadata sent during MCP initialization.
+serverInfo :: McpServerInfo
+serverInfo = McpServerInfo
+  { serverName = "mcp-hoogle"
+  , serverVersion = Text.pack (showVersion version)
+  , serverInstructions = Text.unlines
+      [ "Hoogle search for Haskell types, functions, and modules."
+      , ""
+      , "USE THESE TOOLS INSTEAD OF:"
+      , "- Web searching for Haskell documentation"
+      , "- Running `hoogle` or `mcp-hoogle` CLI commands"
+      , "- Fetching Hackage pages with curl/w3m"
+      , ""
+      , "AVAILABLE TOOLS:"
+      , "- search: Find functions by name, keyword, or type signature (e.g. \"map\", \"[a] -> Int\")"
+      , "- search_type: Search specifically by type signature"
+      , "- lookup_module: Browse all exports of a module (e.g. \"Data.Map\")"
+      , "- regenerate_database: Re-index packages. Pass ghcBinPath (find it with: nix-shell --run 'dirname $(which ghc-pkg)')"
+      , "- reload_database: Reload database from disk after generating externally"
+      , ""
+      , "DATABASE SETUP:"
+      , "If searches return 'No database loaded', call regenerate_database with"
+      , "the ghcBinPath from the project's nix-shell. Find it by running:"
+      , "  nix-shell --run 'dirname $(which ghc-pkg)'"
+      , "Then pass that path to regenerate_database."
+      , "Alternatively, run `nix-shell --run 'mcp-hoogle generate'` as a bash"
+      , "command, then call reload_database to pick up the new file."
+      ]
+  }
 
-    runMcpServerStdio serverInfo handlers
+-- | Build MCP handlers from a database ref and regeneration state.
+handlers :: IORef (Maybe Hoogle.Database) -> TVar RegenState -> McpServerHandlers IO
+handlers databaseRef regenStateVar = McpServerHandlers
+  { prompts = Nothing
+  , resources = Nothing
+  , tools = Just $(deriveToolHandlerWithDescription ''HoogleTool 'toolHandler toolDescriptions)
+  }
+  where
+    toolHandler :: HoogleTool -> IO Content
+    toolHandler tool = ContentText <$> handleTool databaseRef regenStateVar tool
diff --git a/src/McpHoogle/Tools.hs b/src/McpHoogle/Tools.hs
--- a/src/McpHoogle/Tools.hs
+++ b/src/McpHoogle/Tools.hs
@@ -4,14 +4,20 @@
 -- MCP client) can invoke. The @mcp-server@ library's Template Haskell
 -- derivation turns the ADT into a tool list + dispatcher automatically.
 --
--- The handler reads from an 'IORef' 'Database' so the database can be
--- hot-swapped via 'RegenerateDatabase' without restarting the server.
+-- The handler reads from an 'IORef' holding a 'Maybe' 'Database'. The ref
+-- starts as 'Nothing' when no database exists on disk and is populated
+-- by 'RegenerateDatabase' or 'ReloadDatabase'.
+--
+-- 'RegenerateDatabase' runs asynchronously via 'forkIO' so the MCP response
+-- returns immediately. Use 'RegenerationStatus' to poll progress.
 module McpHoogle.Tools
   ( HoogleTool(..)
   , SearchParams(..)
   , SearchTypeParams(..)
   , LookupModuleParams(..)
   , RegenerateDatabaseParams(..)
+  , ReloadDatabaseParams(..)
+  , RegenState(..)
   , handleTool
   , toolDescriptions
   )
@@ -20,8 +26,15 @@
 import Data.Text (Text)
 import Data.Text qualified as Text
 import Data.IORef (IORef, readIORef, writeIORef)
-import Hoogle (Database, searchDatabase, withDatabase, hoogle)
+import Data.Time.Clock (UTCTime, getCurrentTime, diffUTCTime)
+import Control.Concurrent (forkIO)
+import Control.Concurrent.STM (TVar, readTVarIO, atomically, writeTVar)
+import Control.Exception (bracket, SomeException, try)
+import GHC.IO.Handle (hDuplicate, hDuplicateTo)
+import Hoogle (Database, searchDatabase, withDatabase, hoogle, defaultDatabaseLocation)
 import McpHoogle.Format (formatTargets)
+import System.Environment (setEnv, lookupEnv)
+import System.IO (stdout, stderr, hFlush)
 
 -- | Parameters for a general search (name, keyword, or type signature).
 data SearchParams = SearchParams
@@ -39,10 +52,28 @@
   }
 
 -- | Parameters for database regeneration.
+--
+-- Requires @ghcBinPath@ so that @ghc-pkg@ can be found even when the
+-- MCP server was started outside a nix-shell.
 data RegenerateDatabaseParams = RegenerateDatabaseParams
-  { databasePath :: Text
+  { ghcBinPath :: Text
   }
 
+-- | Parameters for reloading the database from disk without regenerating.
+data ReloadDatabaseParams = ReloadDatabaseParams
+  { reloadPath :: Text
+  }
+
+-- | Tracks the state of an asynchronous database regeneration.
+--
+-- Stored in a 'TVar' for thread-safe access between the MCP handler
+-- thread and the background regeneration thread.
+data RegenState
+  = RegenIdle
+  | RegenRunning UTCTime     -- ^ Started at this time
+  | RegenDone UTCTime Text   -- ^ Finished at, database path
+  | RegenFailed UTCTime Text -- ^ Finished at, error message
+
 -- | The set of MCP tools this server exposes.
 --
 -- Each constructor maps to one callable tool. The nested parameter records
@@ -53,6 +84,8 @@
   | SearchType SearchTypeParams
   | LookupModule LookupModuleParams
   | RegenerateDatabase RegenerateDatabaseParams
+  | ReloadDatabase ReloadDatabaseParams
+  | RegenerationStatus
 
 -- | Human-readable descriptions for each tool and its arguments.
 -- Fed to 'deriveToolHandlerWithDescription' so MCP clients know what
@@ -62,39 +95,116 @@
   [ ("Search", "Search Hoogle by function name, type signature, or keyword. Returns matching functions with their types, packages, and documentation.")
   , ("SearchType", "Search Hoogle specifically by type signature. Example: 'a -> [a]' or '[a] -> Int'")
   , ("LookupModule", "Search for all exports of a given module name. Example: 'Data.Map'")
-  , ("RegenerateDatabase", "Regenerate the Hoogle database from the current GHC package database. Call this after switching projects or nix-shells to re-index available packages. The databasePath should be the path to write the .hoo file.")
+  , ("RegenerateDatabase", "Regenerate the Hoogle database asynchronously. Returns immediately — use regeneration_status to poll progress. Requires ghcBinPath so ghc-pkg can be found. Find it with: nix-shell --run 'dirname $(which ghc-pkg)'")
+  , ("ReloadDatabase", "Reload the Hoogle database from disk without regenerating. Use after running 'mcp-hoogle generate' externally via a bash command.")
+  , ("RegenerationStatus", "Check the status of an asynchronous database regeneration. Reports idle, in-progress (with elapsed time), completed, or failed.")
   , ("query", "The search query: a function name, keyword, or type signature")
   , ("typeSignature", "A Haskell type signature to search for, e.g. '[a] -> Int' or 'Map k v -> [(k,v)]'")
   , ("moduleName", "A Haskell module name to look up, e.g. 'Data.Map' or 'Control.Monad'")
-  , ("databasePath", "Path to the Hoogle database file to regenerate and reload")
+  , ("ghcBinPath", "Path to GHC's bin directory containing ghc-pkg. Find with: nix-shell --run 'dirname $(which ghc-pkg)'")
+  , ("reloadPath", "Path to the .hoo database file to reload. Use empty string for default (~/.hoogle/default-haskell-5.0.18.hoo).")
   ]
 
 -- | Dispatch a tool call to the appropriate Hoogle operation.
 --
 -- Reads the current database from the 'IORef'. For 'RegenerateDatabase',
--- shells out to @hoogle generate --local@, reloads the resulting file,
--- and swaps the 'IORef' contents so subsequent searches use the new data.
-handleTool :: IORef Database -> HoogleTool -> IO Text
-handleTool databaseRef (Search (SearchParams searchQuery)) = do
-  database <- readIORef databaseRef
-  let results = take 20 (searchDatabase database (Text.unpack searchQuery))
-  pure (formatTargets results)
-handleTool databaseRef (SearchType (SearchTypeParams sig)) = do
-  database <- readIORef databaseRef
-  let results = take 20 (searchDatabase database (Text.unpack sig))
-  pure (formatTargets results)
-handleTool databaseRef (LookupModule (LookupModuleParams modName)) = do
-  database <- readIORef databaseRef
-  let queryString = "module:" <> Text.unpack modName
-      results = take 30 (searchDatabase database queryString)
-  -- If module: prefix doesn't work well, fall back to plain search
-  let finalResults = if null results
-        then take 30 (searchDatabase database (Text.unpack modName))
-        else results
-  pure (formatTargets finalResults)
-handleTool databaseRef (RegenerateDatabase (RegenerateDatabaseParams path)) = do
-  let pathStr = Text.unpack path
-  hoogle ["generate", "--local", "--database=" <> pathStr]
-  withDatabase pathStr $ \newDb -> do
-    writeIORef databaseRef newDb
-    pure "Database regenerated and reloaded successfully."
+-- spawns a background thread and returns immediately — poll with
+-- 'RegenerationStatus'.
+handleTool :: IORef (Maybe Database) -> TVar RegenState -> HoogleTool -> IO Text
+handleTool databaseRef _regenStateVar (Search (SearchParams searchQuery)) = do
+  mDatabase <- readIORef databaseRef
+  case mDatabase of
+    Nothing -> pure "No database loaded. Call regenerate_database with your project's ghcBinPath first."
+    Just database -> do
+      let results = take 20 (searchDatabase database (Text.unpack searchQuery))
+      pure (formatTargets results)
+handleTool databaseRef _regenStateVar (SearchType (SearchTypeParams sig)) = do
+  mDatabase <- readIORef databaseRef
+  case mDatabase of
+    Nothing -> pure "No database loaded. Call regenerate_database with your project's ghcBinPath first."
+    Just database -> do
+      let results = take 20 (searchDatabase database (Text.unpack sig))
+      pure (formatTargets results)
+handleTool databaseRef _regenStateVar (LookupModule (LookupModuleParams modName)) = do
+  mDatabase <- readIORef databaseRef
+  case mDatabase of
+    Nothing -> pure "No database loaded. Call regenerate_database with your project's ghcBinPath first."
+    Just database -> do
+      let queryString = "module:" <> Text.unpack modName
+          results = take 30 (searchDatabase database queryString)
+      -- If module: prefix doesn't work well, fall back to plain search
+      let finalResults = if null results
+            then take 30 (searchDatabase database (Text.unpack modName))
+            else results
+      pure (formatTargets finalResults)
+handleTool databaseRef regenStateVar (RegenerateDatabase (RegenerateDatabaseParams ghcBin)) = do
+  currentState <- readTVarIO regenStateVar
+  case currentState of
+    RegenRunning _ -> pure "Database regeneration already in progress. Use regeneration_status to check progress."
+    _ -> do
+      startTime <- getCurrentTime
+      atomically (writeTVar regenStateVar (RegenRunning startTime))
+      dbPath <- defaultDatabaseLocation
+      let ghcBinStr = Text.unpack ghcBin
+      _ <- forkIO $ do
+        result <- try $ do
+          withPrependedPath ghcBinStr $
+            withSilencedStdout $
+              hoogle ["generate", "--local", "--database=" <> dbPath]
+          withDatabase dbPath $ \newDb ->
+            writeIORef databaseRef (Just newDb)
+        finishTime <- getCurrentTime
+        case result of
+          Right () ->
+            atomically (writeTVar regenStateVar (RegenDone finishTime (Text.pack dbPath)))
+          Left someException ->
+            atomically (writeTVar regenStateVar (RegenFailed finishTime (Text.pack (show (someException :: SomeException)))))
+      pure "Database regeneration started. Use regeneration_status to check progress."
+handleTool databaseRef _regenStateVar (ReloadDatabase (ReloadDatabaseParams path)) = do
+  dbPath <- if Text.null path
+    then defaultDatabaseLocation
+    else pure (Text.unpack path)
+  withDatabase dbPath $ \newDb -> do
+    writeIORef databaseRef (Just newDb)
+    pure (Text.pack ("Database reloaded from: " <> dbPath))
+handleTool _databaseRef regenStateVar RegenerationStatus = do
+  currentState <- readTVarIO regenStateVar
+  now <- getCurrentTime
+  case currentState of
+    RegenIdle -> pure "No regeneration in progress or completed."
+    RegenRunning startTime ->
+      let elapsed = floor (diffUTCTime now startTime) :: Int
+      in pure (Text.pack ("Regeneration in progress (started " <> show elapsed <> "s ago)."))
+    RegenDone finishTime dbPathText ->
+      let ago = floor (diffUTCTime now finishTime) :: Int
+      in pure (Text.pack ("Regeneration completed " <> show ago <> "s ago. Database loaded from: " <> Text.unpack dbPathText))
+    RegenFailed finishTime errorText ->
+      let ago = floor (diffUTCTime now finishTime) :: Int
+      in pure (Text.pack ("Regeneration failed " <> show ago <> "s ago: " <> Text.unpack errorText))
+
+-- | Temporarily prepend a directory to PATH, run an action, then restore.
+withPrependedPath :: FilePath -> IO a -> IO a
+withPrependedPath dir action = do
+  oldPath <- lookupEnv "PATH"
+  let newPath = case oldPath of
+        Nothing -> dir
+        Just p  -> dir <> ":" <> p
+  bracket
+    (setEnv "PATH" newPath)
+    (\_ -> maybe (setEnv "PATH" "") (setEnv "PATH") oldPath)
+    (\_ -> action)
+
+-- | Redirect stdout to \/dev\/null during an action.
+--
+-- Hoogle's library API writes progress text to stdout which would corrupt
+-- the MCP JSON-RPC stream. We redirect stdout to stderr (so it's still
+-- visible for debugging) and restore it after.
+withSilencedStdout :: IO a -> IO a
+withSilencedStdout action = do
+  hFlush stdout
+  savedStdout <- hDuplicate stdout
+  hDuplicateTo stderr stdout
+  result <- action
+  hFlush stdout
+  hDuplicateTo savedStdout stdout
+  pure result
diff --git a/test/Test.hs b/test/Test.hs
--- a/test/Test.hs
+++ b/test/Test.hs
@@ -3,12 +3,14 @@
 import Test.Tasty
 import Test.Tasty.HUnit
 
+import Control.Concurrent.STM (TVar, newTVarIO, atomically, writeTVar)
+import Data.Time.Clock (getCurrentTime)
 import Control.Exception (try, SomeException)
 import Data.IORef (IORef, newIORef)
 import Data.Text qualified as Text
 import Hoogle (Database, Target(..), defaultDatabaseLocation, withDatabase, hoogle)
 import McpHoogle.Format (formatTarget, formatTargets, stripHtmlTags)
-import McpHoogle.Tools (HoogleTool(..), SearchParams(..), SearchTypeParams(..), LookupModuleParams(..), handleTool)
+import McpHoogle.Tools (HoogleTool(..), SearchParams(..), SearchTypeParams(..), LookupModuleParams(..), RegenState(..), handleTool)
 import System.Directory (doesFileExist, createDirectoryIfMissing, getTemporaryDirectory)
 
 main :: IO ()
@@ -18,6 +20,7 @@
 tests = testGroup "McpHoogle"
   [ formatTests
   , stripHtmlTests
+  , regenStateTests
   , withResource acquireDb releaseDb hoogleSearchTests
   ]
 
@@ -77,6 +80,41 @@
       stripHtmlTags "<a><b>deep</b></a>" @?= "deep"
   ]
 
+-- | Tests for the async regeneration state machine.
+regenStateTests :: TestTree
+regenStateTests = testGroup "Regeneration state"
+  [ testCase "regeneration_status reports idle initially" $ do
+      databaseRef <- newIORef Nothing
+      regenStateVar <- newTVarIO RegenIdle
+      result <- handleTool databaseRef regenStateVar RegenerationStatus
+      assertBool "should report idle"
+        (Text.isInfixOf "No regeneration" result)
+  , testCase "regeneration_status reports running when state is RegenRunning" $ do
+      databaseRef <- newIORef Nothing
+      regenStateVar <- newTVarIO RegenIdle
+      now <- getCurrentTime
+      atomically (writeTVar regenStateVar (RegenRunning now))
+      result <- handleTool databaseRef regenStateVar RegenerationStatus
+      assertBool "should report in progress"
+        (Text.isInfixOf "in progress" result)
+  , testCase "regeneration_status reports failure" $ do
+      databaseRef <- newIORef Nothing
+      regenStateVar <- newTVarIO RegenIdle
+      now <- getCurrentTime
+      atomically (writeTVar regenStateVar (RegenFailed now "ghc-pkg not found"))
+      result <- handleTool databaseRef regenStateVar RegenerationStatus
+      assertBool "should report failure with error message"
+        (Text.isInfixOf "ghc-pkg not found" result)
+  , testCase "regeneration_status reports completion" $ do
+      databaseRef <- newIORef Nothing
+      regenStateVar <- newTVarIO RegenIdle
+      now <- getCurrentTime
+      atomically (writeTVar regenStateVar (RegenDone now "/tmp/test.hoo"))
+      result <- handleTool databaseRef regenStateVar RegenerationStatus
+      assertBool "should report completed with path"
+        (Text.isInfixOf "/tmp/test.hoo" result)
+  ]
+
 -- | Integration tests that exercise hoogle search through our handler.
 -- The database path is shared across all tests via withResource.
 -- Tests pass trivially when no database is available (e.g. cabal CI without haddocks).
@@ -86,8 +124,8 @@
       mDbPath <- getDbPath
       case mDbPath of
         Nothing -> pure () -- no DB available, skip
-        Just dbPath -> withHoogleDb dbPath $ \databaseRef -> do
-          result <- handleTool databaseRef (Search (SearchParams "map"))
+        Just dbPath -> withHoogleDb dbPath $ \databaseRef regenStateVar -> do
+          result <- handleTool databaseRef regenStateVar (Search (SearchParams "map"))
           assertBool "search should return results, not 'No results found'"
             (result /= "No results found.")
           assertBool "search for map should mention 'map' in results"
@@ -96,26 +134,27 @@
       mDbPath <- getDbPath
       case mDbPath of
         Nothing -> pure ()
-        Just dbPath -> withHoogleDb dbPath $ \databaseRef -> do
-          result <- handleTool databaseRef (SearchType (SearchTypeParams "[a] -> Int"))
+        Just dbPath -> withHoogleDb dbPath $ \databaseRef regenStateVar -> do
+          result <- handleTool databaseRef regenStateVar (SearchType (SearchTypeParams "[a] -> Int"))
           assertBool "type search should return results"
             (result /= "No results found.")
   , testCase "module lookup 'Data.Map' returns results" $ do
       mDbPath <- getDbPath
       case mDbPath of
         Nothing -> pure ()
-        Just dbPath -> withHoogleDb dbPath $ \databaseRef -> do
-          result <- handleTool databaseRef (LookupModule (LookupModuleParams "Data.Map"))
+        Just dbPath -> withHoogleDb dbPath $ \databaseRef regenStateVar -> do
+          result <- handleTool databaseRef regenStateVar (LookupModule (LookupModuleParams "Data.Map"))
           assertBool "module lookup should return results"
             (result /= "No results found.")
   ]
 
 -- | Load a hoogle database and run an action with it
-withHoogleDb :: FilePath -> (IORef Database -> IO ()) -> IO ()
+withHoogleDb :: FilePath -> (IORef (Maybe Database) -> TVar RegenState -> IO ()) -> IO ()
 withHoogleDb path action =
   withDatabase path $ \database -> do
-    databaseRef <- newIORef database
-    action databaseRef
+    databaseRef <- newIORef (Just database)
+    regenStateVar <- newTVarIO RegenIdle
+    action databaseRef regenStateVar
 
 -- | Helper to create a Target for testing
 mkTarget :: String -> Maybe (String, String) -> Maybe (String, String) -> Target
