tricorder-mcp 0.2.0.0 → 1.0.0.1
raw patch · 4 files changed
+140/−114 lines, 4 filesdep ~atelier-coredep ~tricorder-typesPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: atelier-core, tricorder-types
API changes (from Hackage documentation)
- Tricorder.MCP.Tools: [force] :: RestartOptions -> Maybe Bool
+ Tricorder.MCP.Tools: [ignorePendingQueries] :: RestartOptions -> Maybe Bool
Files
- CHANGELOG.md +15/−0
- src/Tricorder/MCP/Tools.hs +96/−99
- test/Unit/Tricorder/MCP/ToolsSpec.hs +25/−11
- tricorder-mcp.cabal +4/−4
CHANGELOG.md view
@@ -7,6 +7,21 @@ ## [Unreleased] +## [1.0.0.1] - 2026-10-30++### Fixed++- `start_daemon`, `stop_daemon` and `restart_daemon` now invoke `tricorder daemon {start,stop,restart}` instead of the removed top-level `start`, `stop` and `restart` subcommands. Non-breaking. (Thanks @michael-xavier-well)++## [1.0.0.0] - 2026-10-29++### Changed++- Uses the new flags for Tricorder's `status`, `daemon stop` and+ `daemon restart` commands.+- Support new `IfBuildIsPending` option.+- Rename `start` and `restart` tools' `force` option to `ignorePendingQueries`.+ ## [0.2.0.0] - 2026-10-23 ### Changed
src/Tricorder/MCP/Tools.hs view
@@ -24,10 +24,10 @@ import System.Exit (ExitCode (..)) import System.Posix (getWorkingDirectory) import System.Process.Typed (proc, readProcess, setWorkingDir)-import Tricorder.API.Force (Force)+import Tricorder.API.IfBuildIsPending (IfBuildIsPending)+import Tricorder.API.IfThereArePendingQueries (IfThereArePendingQueries) import Tricorder.API.OutputFormat (OutputFormat (..)) import Tricorder.API.TestFilter (TestFilter)-import Tricorder.API.WaitMode (WaitMode) import Tricorder.SourceLookup.SourceQuery (parseSourceQuery) import Prelude hiding (force) @@ -39,13 +39,13 @@ import Tricorder.API.FieldMask qualified as FieldMask import Tricorder.API.FieldMask.Args qualified as FieldMask.Args import Tricorder.API.FieldMask.Schema qualified as FieldMask.Schema-import Tricorder.API.Force qualified as Force+import Tricorder.API.IfBuildIsPending qualified as IfBuildIsPending import Tricorder.API.IfDaemonIsStopped qualified as IfDaemonIsStopped+import Tricorder.API.IfThereArePendingQueries qualified as IfThereArePendingQueries import Tricorder.API.OutputFormat qualified as OutputFormat import Tricorder.API.Test qualified as Test import Tricorder.API.Test.SuiteView.Schema qualified as Test.SuiteView.Schema import Tricorder.API.TestFilter qualified as TestFilter-import Tricorder.API.WaitMode qualified as WaitMode data Tool@@ -64,13 +64,13 @@ data StopOptions = StopOptions- { force :: Maybe Bool+ { ignorePendingQueries :: Maybe Bool , projectRoot :: Maybe Text } data RestartOptions = RestartOptions- { force :: Maybe Bool+ { ignorePendingQueries :: Maybe Bool , projectRoot :: Maybe Text } @@ -130,7 +130,7 @@ { optDescription = Just "Stop the tricorder daemon." , optTitle = Just "Stop daemon" , optFieldDescriptions =- [ forceDesc+ [ ignorePendingQueriesDesc , projectRootDesc ] }@@ -141,7 +141,7 @@ { optDescription = Just "Restart the tricorder daemon." , optTitle = Just "Restart daemon" , optFieldDescriptions =- [ forceDesc+ [ ignorePendingQueriesDesc , projectRootDesc ] }@@ -245,7 +245,7 @@ ( "wait" , "Block and wait until the current build cycle finishes to provide the most up-to-date result." )- forceDesc = ("force", "Ignore pending queries instead of waiting for them to finish.")+ ignorePendingQueriesDesc = ("ignorePendingQueries", "Do not wait for pending queries to finish.") -- | The @tricorder@ invocation for a tool call: the project directory to run@@ -253,100 +253,97 @@ -- and renders it via 'CLI.commandToArgs' so the flags stay in sync with -- "Tricorder.CLI.Arguments" instead of being duplicated here. toolCommand :: Tool -> (Maybe Text, Either Text [String])-toolCommand = \case- StartDaemon (StartOptions {projectRoot}) ->- ( projectRoot- , Right $ Daemon.Command.toArgs Daemon.Command.Start- )- StopDaemon (StopOptions {force, projectRoot}) ->- ( projectRoot- , Right- $ Daemon.Command.toArgs- $ Daemon.Command.Stop- $ toForce force- )- RestartDaemon (RestartOptions {force, projectRoot}) ->- ( projectRoot- , Right- $ Daemon.Command.toArgs- $ Daemon.Command.Restart- $ toForce force- )- Status (StatusOptions {wait, verbose, expand, projectRoot}) ->- ( projectRoot- , Right- $ Command.toArgs- $ Command.Status- Command.StatusOptions- { wait = toWaitMode wait- , format = JsonOutput- , verbosity = toVerbosity verbose- , expand- , ifDaemonIsStopped = IfDaemonIsStopped.StartDaemon- }- )- TestSuites opts ->- ( opts.projectRoot- , do- fields <- traverse FieldMask.Args.parse opts.fields- Right- $ Command.toArgs- $ Command.Test- $ Test.Suites- $ Test.SuitesOptions- { testFilter = fromMaybe TestFilter.All opts.testFilter- , wait = toWaitMode opts.wait- , suiteNames = fromMaybe [] opts.suiteNames- , format = OutputFormat.JsonOutput- , ifDaemonIsStopped = IfDaemonIsStopped.StartDaemon- , fields- }- )- Source (SourceOptions {modules, projectRoot}) ->- ( projectRoot- , Right- $ Command.toArgs- $ Command.Source- Command.SourceOptions- { queries = parseSourceQuery <$> modules- , maxDepth = Nothing- , maxModules = Nothing- }- )- EvalComments (EvalCommentsOptions {wait, projectRoot}) ->- ( projectRoot- , Right- $ Command.toArgs- $ Command.EvalComments- Command.EvalCommentsOptions- { wait = toWaitMode wait- , format = JsonOutput- , ifDaemonIsStopped = IfDaemonIsStopped.StartDaemon- }- )- LogPath (LogPathOptions {projectRoot}) ->- ( projectRoot- , Right- $ Command.toArgs- $ Command.Log Command.ShowLogPath- )- LogContents (LogContentsOptions {projectRoot}) ->- ( projectRoot- , Right- $ Command.toArgs- $ Command.Log Command.ShowLog- )+toolCommand =+ second (fmap Command.toArgs) . \case+ StartDaemon (StartOptions {projectRoot}) ->+ ( projectRoot+ , Right+ $ Command.Daemon Daemon.Command.Start+ )+ StopDaemon (StopOptions {ignorePendingQueries, projectRoot}) ->+ ( projectRoot+ , Right+ $ Command.Daemon+ $ Daemon.Command.Stop+ $ toIfThereArePendingQueries ignorePendingQueries+ )+ RestartDaemon (RestartOptions {ignorePendingQueries, projectRoot}) ->+ ( projectRoot+ , Right+ $ Command.Daemon+ $ Daemon.Command.Restart+ $ toIfThereArePendingQueries ignorePendingQueries+ )+ Status (StatusOptions {wait, verbose, expand, projectRoot}) ->+ ( projectRoot+ , Right+ $ Command.Status+ Command.StatusOptions+ { wait = toIfBuildIsPending wait+ , format = JsonOutput+ , verbosity = toVerbosity verbose+ , expand+ , ifDaemonIsStopped = IfDaemonIsStopped.StartDaemon+ }+ )+ TestSuites opts ->+ ( opts.projectRoot+ , do+ fields <- traverse FieldMask.Args.parse opts.fields+ Right+ $ Command.Test+ $ Test.Suites+ $ Test.SuitesOptions+ { testFilter = fromMaybe TestFilter.All opts.testFilter+ , ifBuildIsPending = toIfBuildIsPending opts.wait+ , suiteNames = fromMaybe [] opts.suiteNames+ , format = OutputFormat.JsonOutput+ , ifDaemonIsStopped = IfDaemonIsStopped.StartDaemon+ , fields+ }+ )+ Source (SourceOptions {modules, projectRoot}) ->+ ( projectRoot+ , Right+ $ Command.Source+ Command.SourceOptions+ { queries = parseSourceQuery <$> modules+ , maxDepth = Nothing+ , maxModules = Nothing+ }+ )+ EvalComments (EvalCommentsOptions {wait, projectRoot}) ->+ ( projectRoot+ , Right+ $ Command.EvalComments+ Command.EvalCommentsOptions+ { wait = toIfBuildIsPending wait+ , format = JsonOutput+ , ifDaemonIsStopped = IfDaemonIsStopped.StartDaemon+ }+ )+ LogPath (LogPathOptions {projectRoot}) ->+ ( projectRoot+ , Right+ $ Command.Log Command.ShowLogPath+ )+ LogContents (LogContentsOptions {projectRoot}) ->+ ( projectRoot+ , Right+ $ Command.Log Command.ShowLog+ ) -toForce :: Maybe Bool -> Force-toForce = maybe Force.NoForce (\enabled -> if enabled then Force.Force else Force.NoForce)+toIfThereArePendingQueries :: Maybe Bool -> IfThereArePendingQueries+toIfThereArePendingQueries ignorePendingQueries+ | ignorePendingQueries == Just True = IfThereArePendingQueries.DontWait+ | otherwise = IfThereArePendingQueries.Wait -toWaitMode :: Maybe Bool -> WaitMode-toWaitMode =- maybe- WaitMode.ShowCurrent- (\enabled -> if enabled then WaitMode.WaitForBuild else WaitMode.ShowCurrent)+toIfBuildIsPending :: Maybe Bool -> IfBuildIsPending+toIfBuildIsPending =+ maybe IfBuildIsPending.UseCurrent+ $ bool IfBuildIsPending.UseCurrent IfBuildIsPending.WaitForBuild toVerbosity :: Maybe Bool -> Command.Verbosity
test/Unit/Tricorder/MCP/ToolsSpec.hs view
@@ -27,23 +27,37 @@ "toolCommand" [ testCase "starts with just the directory" $ toolCommand (StartDaemon StartOptions {projectRoot = Nothing})- @?= (Nothing, Right ["start"])- , testCase "omits --force when unset"- $ toolCommand (StopDaemon (StopOptions {force = Nothing, projectRoot = Nothing}))- @?= (Nothing, Right ["stop"])- , testCase "omits --force when explicitly false"- $ toolCommand (RestartDaemon (RestartOptions {force = Just False, projectRoot = Nothing}))- @?= (Nothing, Right ["restart"])- , testCase "includes --force when true"+ @?= (Nothing, Right ["daemon", "start"])+ , testCase "omits --ignore-pending-queries when unset" $ toolCommand ( StopDaemon ( StopOptions- { force = Just True+ { ignorePendingQueries = Nothing , projectRoot = Nothing } ) )- @?= (Nothing, Right ["stop", "--force"])+ @?= (Nothing, Right ["daemon", "stop"])+ , testCase "omits --ignore-pending-queries when explicitly false"+ $ toolCommand+ ( RestartDaemon+ ( RestartOptions+ { ignorePendingQueries = Just False+ , projectRoot = Nothing+ }+ )+ )+ @?= (Nothing, Right ["daemon", "restart"])+ , testCase "includes --ignore-pending-queries when true"+ $ toolCommand+ ( StopDaemon+ ( StopOptions+ { ignorePendingQueries = Just True+ , projectRoot = Nothing+ }+ )+ )+ @?= (Nothing, Right ["daemon", "stop", "--ignore-pending-queries"]) , testCase "combines status flags in order, with --expand carrying its argument" $ toolCommand ( Status@@ -54,7 +68,7 @@ , projectRoot = Nothing } )- @?= (Nothing, Right ["status", "--wait", "--json", "--expand", "3"])+ @?= (Nothing, Right ["status", "--json", "--expand", "3"]) , testCase "turns modules into positional arguments" $ toolCommand ( Source
tricorder-mcp.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: tricorder-mcp-version: 0.2.0.0+version: 1.0.0.1 synopsis: MCP server for Tricorder description: Model Context Protocol server for Tricorder. category: AI,@@ -61,7 +61,7 @@ TypeFamilies ghc-options: -Weverything -Wno-unsafe -Wno-missing-safe-haskell-mode -Wno-monomorphism-restriction -Wno-missing-kind-signatures -Wno-missing-local-signatures -Wno-missing-import-lists -Wno-implicit-prelude -Wno-unticked-promoted-constructors -Wno-unused-packages -Wno-all-missed-specialisations -Wno-missed-specialisations -fplugin=Effectful.Plugin -threaded build-depends:- atelier-core ==0.8.*+ atelier-core ==1.0.* , atelier-prelude ==0.4.* , base >=4.18 && <4.23 , bytestring >=0.11 && <0.13@@ -72,7 +72,7 @@ , optparse-applicative ==0.19.* , process ==1.6.* , template-haskell >=2.20 && <2.25- , tricorder-types ==0.6.*+ , tricorder-types ==1.0.* , typed-process ==0.2.* , unix ==2.8.* mixins:@@ -150,7 +150,7 @@ build-tool-depends: tasty-discover:tasty-discover build-depends:- atelier-core ==0.8.*+ atelier-core ==1.0.* , atelier-prelude ==0.4.* , base >=4.18 && <4.23 , effectful-core ==2.7.*