Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/proofs/formatting.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6beb1be8064da30b4f87469f52846b0068bad6d736c0da0205153641723d03ed ab5370655049c955d122d644eb26598258e7998bf91ebc7433f068470a966a9e pass
d3571a0bf0f29900e33ca925391598e5dbfc6dbfcd77a37cd1ae905c4fb920f0 ab5370655049c955d122d644eb26598258e7998bf91ebc7433f068470a966a9e pass
2 changes: 1 addition & 1 deletion .github/workflows/proofs/tests.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
16576458607acdc2215ac80de3c245fc8979e8dd046af802cf9352b193deee13 7a39b8df88ef160e852c6aac30ee2a8bfdb8074b060d5afc8c72bf5f03d1f0a7 pass
6b3af2ea0e22d965075f5678144a56927a6c5d9f550c2053b4a1ac46bff3404c 7a39b8df88ef160e852c6aac30ee2a8bfdb8074b060d5afc8c72bf5f03d1f0a7 pass
2 changes: 1 addition & 1 deletion .github/workflows/proofs/transcripts.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
f467012b1820e6b37674b199955b991946843b73acdce1b7d1bc41338e7adf9e 72c0b9bfd651515ecb3d3f4981a6bdb8c6d52eba2308cd11fea004d31941aeef pass
8389db50ad367587e05badcf09152052c3980d7884ec5b4d335594e9b3407e41 72c0b9bfd651515ecb3d3f4981a6bdb8c6d52eba2308cd11fea004d31941aeef pass
1 change: 1 addition & 0 deletions nix/unison-project.nix
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ in
conduit-extra = pkgs.haskell.lib.dontCheck hprev.conduit-extra;
data-clist = pkgs.haskell.lib.dontCheck hprev.data-clist;
fsnotify = pkgs.haskell.lib.dontCheck hprev.fsnotify;
filelock = pkgs.haskell.lib.dontCheck hprev.filelock;
doctest-discover = pkgs.haskell.lib.dontCheck hprev.doctest-discover;
haskeline = pkgs.haskell.lib.dontCheck hprev.haskeline;
hs-mcp = pkgs.haskell.lib.dontCheck hprev.hs-mcp;
Expand Down
61 changes: 55 additions & 6 deletions unison-cli/src/Unison/MCP/Tools.hs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ tools =
deleteNamespaceTool,
reflogTool,
historyTool,
createBranchTool
createBranchTool,
compileTool,
libUpgradeTool
]

currentProjectContext :: (MonadIO m, MonadReader Env m) => m ProjectContext
Expand Down Expand Up @@ -258,7 +260,7 @@ runTool :: Tool MCP
runTool =
Tool
{ toolName = toToolName RunTool,
toolDescription = "Execute/Run a given definition.",
toolDescription = "Execute/Run a given definition. If `code` is provided, it will be typechecked first and the definition will be run from the typechecked file without updating the codebase.",
toolAnnotations =
ToolAnnotations
{ title = Just "Run",
Expand All @@ -268,11 +270,15 @@ runTool =
openWorldHint = Just False
},
toolArgType = Proxy,
toolHandler = \(RunToolArguments {mainFunctionName, projectContext, args}) -> handleToolError $ do
toolHandler = \(RunToolArguments {mainFunctionName, projectContext, args, code}) -> handleToolError $ do
let input = ExecuteI NoProf (HQ.NameOnly mainFunctionName) (Text.unpack <$> args)
output <- handleInputMCP projectContext [Right input]
let outputJSON = Text.decodeUtf8 . BL.toStrict $ Aeson.encode output
pure $ textToolResult outputJSON
case code of
Nothing -> do
output <- handleInputMCP projectContext [Right input]
let outputJSON = Text.decodeUtf8 . BL.toStrict $ Aeson.encode output
pure $ textToolResult outputJSON
Just source ->
withCode source [input] projectContext
}

shareProjectReadmeTool :: Tool MCP
Expand Down Expand Up @@ -842,3 +848,46 @@ createBranchTool =
let outputJSON = Text.decodeUtf8 . BL.toStrict $ Aeson.encode output
pure $ textToolResult outputJSON
}

compileTool :: Tool MCP
compileTool =
Tool
{ toolName = toToolName CompileTool,
toolDescription = "Compile a Unison definition to a standalone .uc file. The file is written relative to the codebase directory. Run it with: ucm run.compiled <outputPath>.uc",
toolAnnotations =
ToolAnnotations
{ title = Just "Compile",
readOnlyHint = Just False,
destructiveHint = Just False,
idempotentHint = Just True,
openWorldHint = Just False
},
toolArgType = Proxy,
toolHandler = \(CompileToolArguments {projectContext, mainFunctionName, outputPath}) -> handleToolError $ do
let input = MakeStandaloneI (Text.unpack outputPath) (HQ.NameOnly mainFunctionName)
output <- handleInputMCP projectContext [Right input]
let outputJSON = Text.decodeUtf8 . BL.toStrict $ Aeson.encode output
pure $ textToolResult outputJSON
}

libUpgradeTool :: Tool MCP
libUpgradeTool =
Tool
{ toolName = toToolName LibUpgradeTool,
toolDescription = "Upgrade a library dependency from one version to another. Equivalent to `lib.upgrade old new` in UCM.",
toolAnnotations =
ToolAnnotations
{ title = Just "Lib Upgrade",
readOnlyHint = Just False,
destructiveHint = Just True,
idempotentHint = Just False,
openWorldHint = Just False
},
toolArgType = Proxy,
toolHandler = \(LibUpgradeToolArguments {projectContext, oldLibName, newLibName}) -> handleToolError $ do
let segs = map NameSegment.unsafeParseText [oldLibName, newLibName]
input = UpgradeI segs
output <- handleInputMCP projectContext [Right input]
let outputJSON = Text.decodeUtf8 . BL.toStrict $ Aeson.encode output
pure $ textToolResult outputJSON
}
114 changes: 111 additions & 3 deletions unison-cli/src/Unison/MCP/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ module Unison.MCP.Types
ReflogToolArguments (..),
HistoryToolArguments (..),
CreateBranchToolArguments (..),
CompileToolArguments (..),
LibUpgradeToolArguments (..),
toToolName,
fromToolName,
)
Expand Down Expand Up @@ -106,6 +108,8 @@ data ToolKind
| ReflogTool
| HistoryTool
| CreateBranchTool
| CompileTool
| LibUpgradeTool
deriving (Eq, Ord, Show, Bounded, Enum)

kindNameMapping :: Map ToolKind Text
Expand Down Expand Up @@ -140,7 +144,9 @@ kindNameMapping =
(DiffUpdateTool, "diff-update"),
(ReflogTool, "reflog"),
(HistoryTool, "history"),
(CreateBranchTool, "create-branch")
(CreateBranchTool, "create-branch"),
(CompileTool, "compile"),
(LibUpgradeTool, "lib-upgrade")
]

data ProjectDefinitionNameArgument = ProjectDefinitionNameArgument
Expand Down Expand Up @@ -568,7 +574,8 @@ instance FromJSON DocsToolArguments where
data RunToolArguments = RunToolArguments
{ projectContext :: ProjectContext,
mainFunctionName :: Name,
args :: [Text]
args :: [Text],
code :: Maybe (Either FilePath Text)
}
deriving (Eq, Show)

Expand All @@ -593,6 +600,27 @@ instance HasInputSchema RunToolArguments where
"description" .= ("An argument to pass to the main function." :: Text)
],
"description" .= ("The arguments to pass to the main function." :: Text)
],
"code"
.= object
[ "description" .= ("Optional source code to typecheck before running. Allows running definitions without updating the codebase. Either the `sourceCode` key or the `filePath`, but not both." :: Text),
"type" .= ("object" :: Text),
"properties"
.= object
[ "sourceCode"
.= object
[ "type" .= ("string" :: Text),
"description" .= ("The source code to typecheck." :: Text)
],
"filePath"
.= object
[ "type" .= ("string" :: Text),
"description" .= ("An absolute file path to the source code." :: Text)
]
],
"additionalProperties" .= False,
"minProperties" .= (1 :: Int),
"maxProperties" .= (1 :: Int)
]
],
"required" .= ["projectContext", "mainFunctionName", "args" :: Text]
Expand All @@ -606,7 +634,16 @@ instance FromJSON RunToolArguments where
Left err -> fail $ "Invalid main function name: " ++ show err
Right name -> pure name
args <- o .: "args"
pure $ RunToolArguments {projectContext, mainFunctionName, args}
code <-
o .:? "code" >>= \case
Nothing -> pure Nothing
Just source ->
source .:? "filePath" >>= \case
Just filePath -> pure $ Just (Left filePath)
Nothing -> do
text <- source .: "sourceCode"
pure $ Just (Right text)
pure $ RunToolArguments {projectContext, mainFunctionName, args, code}

data ProjectCodeToolArguments = ProjectCodeToolArguments
{ projectContext :: ProjectContext
Expand Down Expand Up @@ -1101,6 +1138,77 @@ instance FromJSON CreateBranchToolArguments where
sourceBranchName <- fmap UnsafeProjectBranchName <$> o .:? "sourceBranchName"
pure $ CreateBranchToolArguments {projectName, newBranchName, sourceType, sourceBranchProject, sourceBranchName}

data CompileToolArguments = CompileToolArguments
{ projectContext :: ProjectContext,
mainFunctionName :: Name,
outputPath :: Text
}
deriving (Eq, Show)

instance HasInputSchema CompileToolArguments where
toInputSchema _ =
object
[ "type" .= ("object" :: Text),
"properties"
.= object
[ "projectContext" .= toInputSchema (Proxy :: Proxy ProjectContext),
"mainFunctionName"
.= object
[ "type" .= ("string" :: Text),
"description" .= ("The main function to compile, e.g. `myMain` or `mynamespace.myprogram`." :: Text)
],
"outputPath"
.= object
[ "type" .= ("string" :: Text),
"description" .= ("Output file path (without .uc extension). UCM writes the .uc file relative to the codebase directory." :: Text)
]
],
"required" .= ["projectContext", "mainFunctionName", "outputPath" :: Text]
]

instance FromJSON CompileToolArguments where
parseJSON = withObject "CompileToolArguments" $ \o -> do
projectContext <- o .: "projectContext"
mainFunctionName <- Name.unsafeParseText <$> o .: "mainFunctionName"
outputPath <- o .: "outputPath"
pure $ CompileToolArguments {projectContext, mainFunctionName, outputPath}

-- | Each element is a pair (old, new); the list is flattened: [old1, new1, old2, new2, ...]
data LibUpgradeToolArguments = LibUpgradeToolArguments
{ projectContext :: ProjectContext,
oldLibName :: Text,
newLibName :: Text
}
deriving (Eq, Show)

instance HasInputSchema LibUpgradeToolArguments where
toInputSchema _ =
object
[ "type" .= ("object" :: Text),
"properties"
.= object
[ "projectContext" .= toInputSchema (Proxy :: Proxy ProjectContext),
"oldLibName"
.= object
[ "type" .= ("string" :: Text),
"description" .= ("The current library name segment to upgrade from, e.g. `unison_base_1_0_0`." :: Text)
],
"newLibName"
.= object
[ "type" .= ("string" :: Text),
"description" .= ("The new library name segment to upgrade to, e.g. `unison_base_2_0_0`." :: Text)
]
],
"required" .= ["projectContext", "oldLibName", "newLibName" :: Text]
]

instance FromJSON LibUpgradeToolArguments where
parseJSON = withObject "LibUpgradeToolArguments" $ \o -> do
projectContext <- o .: "projectContext"
oldLibName <- o .: "oldLibName"
newLibName <- o .: "newLibName"
pure $ LibUpgradeToolArguments {projectContext, oldLibName, newLibName}

nameKindMapping :: Map Text ToolKind
nameKindMapping =
(Map.toList kindNameMapping)
Expand Down
Loading
Loading