Skip to content

Commit 0f1cf93

Browse files
committed
Support embed tool calling from mai
1 parent f83c279 commit 0f1cf93

25 files changed

Lines changed: 2914 additions & 2407 deletions

go.mod

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ go 1.20.0
44

55
require github.com/gorilla/mux v1.8.0
66

7-
require (
8-
gopkg.in/yaml.v3 v3.0.1
9-
mcplib v0.0.0
10-
)
7+
require gopkg.in/yaml.v3 v3.0.1
8+
9+
require wmcplib v0.0.0
1110

1211
replace mcplib => ./src/mcps/lib
12+
13+
replace wmcplib => ./src/wmcp/lib

src/repl/conf.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ func NewConfigOptions() *ConfigOptions {
150150
co.RegisterOption("mcp.config", StringOption, "Path to MCP configuration file", "")
151151
co.RegisterOption("mcp.args", StringOption, "Command-line arguments to pass to mai-wmcp", "")
152152
co.RegisterOption("mcp.daemon", BooleanOption, "Enable starting the mai-wmcp server", "true")
153+
co.RegisterOption("mcp.transport", StringOption, "MCP transport: http (default, spawns mai-wmcp) or embed (in-process via wmcplib)", "http")
154+
co.RegisterOption("mcp.yolo", BooleanOption, "Skip tool confirmation prompts when running in embed transport", "false")
153155
co.RegisterOption("mcp.debug", BooleanOption, "Enable debug output for MCP communication between agent, model, and servers", "false")
154156
co.RegisterOption("mcp.prompt", StringOption, "Custom text to be included in the instructions prompt for react loops", "")
155157
co.RegisterOption("mcp.toolformat", StringOption, "Tool list format: xml, markdown, simple, quiet, json (empty=auto)", "")

src/repl/go.mod

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ require (
77
golang.org/x/sys v0.35.0
88
golang.org/x/term v0.33.0
99
mai/src/mcps/lib v0.0.0
10+
mai/src/wmcp/lib v0.0.0
1011
)
1112

1213
require github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
1314

1415
replace mai/src/mcps/lib => ../mcps/lib
16+
17+
replace mai/src/wmcp/lib => ../wmcp/lib

src/repl/mcprompt.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import (
1212

1313
// GetAvailableMCPrompts runs 'mai-tool prompts list' and returns the output as a string
1414
func GetAvailableMCPrompts(f Format) (string, error) {
15+
if embedActiveRepl != nil {
16+
return embedListPromptsSimple(embedActiveRepl)
17+
}
1518
var cmd *exec.Cmd
1619
switch f {
1720
case Quiet:
@@ -33,6 +36,9 @@ func GetAvailableMCPrompts(f Format) (string, error) {
3336

3437
// GetMCPromptContent fetches a specific prompt's rendered content
3538
func GetMCPromptContent(fullName string) (string, error) {
39+
if embedActiveRepl != nil {
40+
return embedGetPrompt(embedActiveRepl, fullName, nil)
41+
}
3642
// fullName format: server/prompt or just prompt
3743
cmd := exec.Command("mai-tool", "prompts", "get", fullName)
3844
var out bytes.Buffer

src/repl/repl_core.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,8 +256,12 @@ func NewREPL(configOptions ConfigOptions, initialCommand string, quitAfterAction
256256
}
257257
}
258258

259-
// Only start mai-wmcp daemon if mcp.use is enabled
260-
if repl.configOptions.GetBool("mcp.use") && repl.configOptions.GetBool("mcp.daemon") {
259+
// Embed transport runs wmcp in-process instead of spawning mai-wmcp.
260+
if repl.configOptions.GetBool("mcp.use") && replEmbedActive(repl) {
261+
if _, err := embedGetService(repl); err != nil {
262+
fmt.Fprintf(os.Stderr, "Error starting embedded wmcp: %v\n", err)
263+
}
264+
} else if repl.configOptions.GetBool("mcp.use") && repl.configOptions.GetBool("mcp.daemon") {
261265
// Spawn mai-wmcp if mcp.config or mcp.args is set
262266
var wmcpArgs []string
263267
if v := repl.configOptions.Get("mcp.config"); v != "" {
@@ -367,6 +371,8 @@ func (r *REPL) Run() error {
367371
}
368372

369373
func (r *REPL) cleanup() {
374+
// Shut down the embedded wmcp service, if any. Harmless when not active.
375+
embedStop()
370376
// Kill wmcp process first to ensure it's stopped regardless of what happens next
371377
if r.wmcpProcess != nil && r.wmcpProcess.Process != nil {
372378
if err := r.wmcpProcess.Process.Kill(); err != nil {

src/repl/tools.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ func parseToolFormat(formatStr string) Format {
6666

6767
// GetAvailableTools runs the 'mai-tool list' command and returns the output as a string
6868
func GetAvailableTools(f Format) (string, error) {
69+
if embedActiveRepl != nil {
70+
return embedListToolsFormatted(embedActiveRepl, f)
71+
}
6972
var cmd *exec.Cmd
7073
switch f {
7174
case Quiet:
@@ -183,6 +186,10 @@ func callTool(tool *Tool, debug bool, format string, timeoutSeconds int) (string
183186
}
184187
}
185188

189+
if embedActiveRepl != nil {
190+
return embedCallToolStringArgs(embedActiveRepl, toolName, safeArgs, timeoutSeconds)
191+
}
192+
186193
var out bytes.Buffer
187194
var stderr bytes.Buffer
188195
cmdArgs := append([]string{"call", toolName}, safeArgs...)
@@ -256,6 +263,10 @@ func (r *REPL) executeToolNative(toolName string, args ...string) (string, error
256263
return "", fmt.Errorf("failed to parse tool arguments JSON: %v", err)
257264
}
258265

266+
if replEmbedActive(r) {
267+
return embedCallTool(r, toolName, params, 60)
268+
}
269+
259270
// Build mai-tool command arguments: mai-tool call <tool> [key=value ...]
260271
cmdArgs := []string{"call", toolName}
261272
for key, value := range params {

0 commit comments

Comments
 (0)