mcp/cmd/mcpcmd/cmd_mcp.go
2026-04-02 18:42:03 +00:00

120 lines
3 KiB
Go

// Package mcpcmd provides the MCP server command.
//
// Commands:
// - mcp serve: Start the MCP server for AI tool integration
package mcpcmd
import (
"context"
"os"
"os/signal"
"syscall"
"dappco.re/go/mcp/pkg/mcp"
"dappco.re/go/mcp/pkg/mcp/agentic"
"dappco.re/go/mcp/pkg/mcp/brain"
"forge.lthn.ai/core/cli/pkg/cli"
)
var workspaceFlag string
var unrestrictedFlag bool
var newMCPService = mcp.New
var runMCPService = func(svc *mcp.Service, ctx context.Context) error {
return svc.Run(ctx)
}
var shutdownMCPService = func(svc *mcp.Service, ctx context.Context) error {
return svc.Shutdown(ctx)
}
var mcpCmd = &cli.Command{
Use: "mcp",
Short: "MCP server for AI tool integration",
Long: "Model Context Protocol (MCP) server providing file operations, RAG, and metrics tools.",
}
var serveCmd = &cli.Command{
Use: "serve",
Short: "Start the MCP server",
Long: `Start the MCP server on stdio (default), TCP, Unix socket, or HTTP.
The server provides file operations plus the brain and agentic subsystems
registered by this command.
Environment variables:
MCP_ADDR TCP address to listen on (e.g., "localhost:9999")
MCP_UNIX_SOCKET
Unix socket path to listen on (e.g., "/tmp/core-mcp.sock")
Selected after MCP_ADDR and before stdio.
MCP_HTTP_ADDR
HTTP address to listen on (e.g., "127.0.0.1:9101")
Selected before MCP_ADDR and stdio.
Examples:
# Start with stdio transport (for Claude Code integration)
core mcp serve
# Start with workspace restriction
core mcp serve --workspace /path/to/project
# Start unrestricted (explicit opt-in)
core mcp serve --unrestricted
# Start TCP server
MCP_ADDR=localhost:9999 core mcp serve`,
RunE: func(cmd *cli.Command, args []string) error {
return runServe()
},
}
func initFlags() {
cli.StringFlag(serveCmd, &workspaceFlag, "workspace", "w", "", "Restrict file operations to this directory")
cli.BoolFlag(serveCmd, &unrestrictedFlag, "unrestricted", "", false, "Disable filesystem sandboxing entirely")
}
// AddMCPCommands registers the 'mcp' command and all subcommands.
func AddMCPCommands(root *cli.Command) {
initFlags()
mcpCmd.AddCommand(serveCmd)
root.AddCommand(mcpCmd)
}
func runServe() error {
opts := mcp.Options{}
if unrestrictedFlag {
opts.Unrestricted = true
} else if workspaceFlag != "" {
opts.WorkspaceRoot = workspaceFlag
}
// Register OpenBrain and agentic subsystems
opts.Subsystems = []mcp.Subsystem{
brain.NewDirect(),
agentic.NewPrep(),
}
// Create the MCP service
svc, err := newMCPService(opts)
if err != nil {
return cli.Wrap(err, "create MCP service")
}
defer func() {
_ = shutdownMCPService(svc, context.Background())
}()
// Set up signal handling for clean shutdown
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigCh
cancel()
}()
// Run the server (blocks until context cancelled or error)
return runMCPService(svc, ctx)
}