diff --git a/crush-schema.json b/crush-schema.json index 680ba31196e276c290fd5040b36b23c26cb12414..ea356c0e585b8a243ee1110d68264c0f2301752f 100644 --- a/crush-schema.json +++ b/crush-schema.json @@ -154,7 +154,8 @@ "stdio", "sse", "stdio", - "sse" + "sse", + "http" ], "title": "Type", "description": "Type of MCP connection", @@ -176,7 +177,6 @@ }, "type": "object", "required": [ - "command", "type" ] }, @@ -253,13 +253,8 @@ "required": [ "id", "name", - "cost_per_1m_out_cached", "context_window", - "default_max_tokens", - "can_reason", - "reasoning_effort", - "has_reasoning_effort", - "supports_attachments" + "default_max_tokens" ] }, "Options": { diff --git a/crush.json b/crush.json index 4937665c513258840f1efb4f88fb2bdd73f6ff68..6d8a7e97dd55fcc6e27dda4f15c01a2e172cc4cc 100644 --- a/crush.json +++ b/crush.json @@ -4,5 +4,12 @@ "go": { "command": "gopls" } + }, + "mcp": { + "context7": { + "command": "", + "url": "https://mcp.context7.com/mcp", + "type": "http" + } } } diff --git a/go.mod b/go.mod index 3f558e064ccd7f6808a4c7496becfe880422bcb3..b8f1c54c5c044650fdd549f96ccd286c60570f79 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/go-logfmt/logfmt v0.6.0 github.com/google/uuid v1.6.0 github.com/invopop/jsonschema v0.13.0 - github.com/mark3labs/mcp-go v0.17.0 + github.com/mark3labs/mcp-go v0.32.0 github.com/muesli/termenv v0.16.0 github.com/ncruces/go-sqlite3 v0.25.0 github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 @@ -40,6 +40,8 @@ require ( mvdan.cc/sh/v3 v3.11.0 ) +require github.com/spf13/cast v1.7.1 // indirect + require ( cloud.google.com/go v0.116.0 // indirect cloud.google.com/go/auth v0.13.0 // indirect diff --git a/go.sum b/go.sum index c2f15bc692d36e414451377711dc087e3c8d8a7e..4cc2f707d3d13fc6bb844a3383fe52a676b90dd9 100644 --- a/go.sum +++ b/go.sum @@ -116,6 +116,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= @@ -165,8 +167,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mark3labs/mcp-go v0.17.0 h1:5Ps6T7qXr7De/2QTqs9h6BKeZ/qdeUeGrgM5lPzi930= -github.com/mark3labs/mcp-go v0.17.0/go.mod h1:KmJndYv7GIgcPVwEKJjNcbhVQ+hJGJhrCCB/9xITzpE= +github.com/mark3labs/mcp-go v0.32.0 h1:fgwmbfL2gbd67obg57OfV2Dnrhs1HtSdlY/i5fn7MU8= +github.com/mark3labs/mcp-go v0.32.0/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= @@ -224,6 +226,8 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= diff --git a/internal/config/config.go b/internal/config/config.go index 589cd5c0ca30811d2fa47ae527e2880d82ccedcd..f3238e57a3895cb234e82722fecdf322da850efa 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -124,13 +124,14 @@ type MCPType string const ( MCPStdio MCPType = "stdio" MCPSse MCPType = "sse" + MCPHttp MCPType = "http" ) type MCP struct { - Command string `json:"command" jsonschema:"title=Command,description=Command to execute for stdio MCP servers"` + Command string `json:"command,omitempty" jsonschema:"title=Command,description=Command to execute for stdio MCP servers"` Env []string `json:"env,omitempty" jsonschema:"title=Environment,description=Environment variables for the MCP server"` Args []string `json:"args,omitempty" jsonschema:"title=Arguments,description=Command line arguments for the MCP server"` - Type MCPType `json:"type" jsonschema:"title=Type,description=Type of MCP connection,enum=stdio,enum=sse,default=stdio"` + Type MCPType `json:"type" jsonschema:"title=Type,description=Type of MCP connection,enum=stdio,enum=sse,enum=http,default=stdio"` URL string `json:"url,omitempty" jsonschema:"title=URL,description=URL for SSE MCP servers"` // TODO: maybe make it possible to get the value from the env Headers map[string]string `json:"headers,omitempty" jsonschema:"title=Headers,description=HTTP headers for SSE MCP servers"` @@ -1407,8 +1408,8 @@ func (c *Config) validateMCPs(errors *ValidationErrors) { fieldPrefix := fmt.Sprintf("mcp.%s", mcpName) // Validate MCP type - if mcpConfig.Type != MCPStdio && mcpConfig.Type != MCPSse { - errors.Add(fieldPrefix+".type", fmt.Sprintf("invalid MCP type: %s (must be 'stdio' or 'sse')", mcpConfig.Type)) + if mcpConfig.Type != MCPStdio && mcpConfig.Type != MCPSse && mcpConfig.Type != MCPHttp { + errors.Add(fieldPrefix+".type", fmt.Sprintf("invalid MCP type: %s (must be 'stdio' or 'sse' or 'http')", mcpConfig.Type)) } // Validate based on type diff --git a/internal/llm/agent/mcp-tools.go b/internal/llm/agent/mcp-tools.go index fed0c06196c600bb5ecc06d1f92a1f3a07f14b38..3fa4e778e9df09f1728641ca578cb7382d9c87b0 100644 --- a/internal/llm/agent/mcp-tools.go +++ b/internal/llm/agent/mcp-tools.go @@ -12,6 +12,7 @@ import ( "github.com/charmbracelet/crush/internal/version" "github.com/mark3labs/mcp-go/client" + "github.com/mark3labs/mcp-go/client/transport" "github.com/mark3labs/mcp-go/mcp" ) @@ -118,6 +119,15 @@ func (b *mcpTool) Run(ctx context.Context, params tools.ToolCall) (tools.ToolRes return tools.NewTextErrorResponse(err.Error()), nil } return runTool(ctx, c, b.tool.Name, params.Input) + case config.MCPHttp: + c, err := client.NewStreamableHttpClient( + b.mcpConfig.URL, + transport.WithHTTPHeaders(b.mcpConfig.Headers), + ) + if err != nil { + return tools.NewTextErrorResponse(err.Error()), nil + } + return runTool(ctx, c, b.tool.Name, params.Input) case config.MCPSse: c, err := client.NewSSEMCPClient( b.mcpConfig.URL, @@ -187,6 +197,16 @@ func GetMcpTools(ctx context.Context, permissions permission.Service) []tools.Ba continue } + mcpTools = append(mcpTools, getTools(ctx, name, m, permissions, c)...) + case config.MCPHttp: + c, err := client.NewStreamableHttpClient( + m.URL, + transport.WithHTTPHeaders(m.Headers), + ) + if err != nil { + logging.Error("error creating mcp client", "error", err) + continue + } mcpTools = append(mcpTools, getTools(ctx, name, m, permissions, c)...) case config.MCPSse: c, err := client.NewSSEMCPClient(