charmbracelet/crush · error
unsupported mcp type: %s
Error message
unsupported mcp type: %s
What it means
The transport factory switches on the configured MCP server type (http/stdio/sse). Any type value outside the known set falls to the default branch and aborts client creation. This is a config-validation guard against typo'd or unsupported `type` fields.
Source
Thrown at internal/agent/tools/mcp/init.go:1193
// Normalize trailing slash for PRM discovery compatibility.
normalizedURL := strings.TrimSuffix(url, "/")
handler, oauthErr := mcpoauth.NewHandler(name, normalizedURL, m.OAuthToken, preregistered, tokenSaver, mcpoauth.IsInteractive(ctx), m.OAuthCallbackPort)
if oauthErr != nil {
return nil, nil, fmt.Errorf("failed to create OAuth handler for mcp %q: %w", name, oauthErr)
}
oauthHandler = handler
authURLs.Set(name, handler)
transport = newOAuthRoundTripper(handler, transport)
}
client := &http.Client{Transport: transport}
return &mcp.SSEClientTransport{
Endpoint: url,
HTTPClient: client,
}, oauthHandler, nil
default:
return nil, nil, fmt.Errorf("unsupported mcp type: %s", m.Type)
}
}
type headerRoundTripper struct {
headers map[string]string
}
func (rt headerRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
for k, v := range rt.headers {
req.Header.Set(k, v)
}
return http.DefaultTransport.RoundTrip(req)
}
// oauthRoundTripper wraps an HTTP transport with OAuth bearer token
// injection and 401-triggered authorization. Used for SSE transports
// that don't support the SDK's OAuthHandler natively. Based on Bruno
// Krugel's implementation from PR #3396.View on GitHub (pinned to 7944b8e522)
Solutions
- Change the type to one of the supported values: "http" (streamable), "sse", or "stdio"
- If the server speaks streamable HTTP, use type "http" instead of "streamable" or "streamable-http"
- Check the Crush docs for the current MCP config schema and update older configs
- Validate your crushrc/crush.json against an example MCP block to catch typos
Example fix
// before (crushrc)
mcp fs {
type "streamable-http"
url "http://localhost:3000/mcp"
}
// after
mcp fs {
type "http"
url "http://localhost:3000/mcp"
} Defensive patterns
Strategy: validation
Validate before calling
// validate the type field before writing config
valid := map[string]bool{"http": true, "sse": true, "stdio": true}
if !valid[mcpType] {
return fmt.Errorf("mcp type %q not supported; use http, sse, or stdio", mcpType)
} Type guard
func isKnownMCPType(t string) bool {
switch t { case "http", "sse", "stdio": return true }
return false
} Prevention
- Copy MCP type names only from current Crush docs, not from other tools' configs
- Use lowercase type strings exactly as documented
- Add a config lint step that checks known type values
When it happens
Trigger: `mcp.<name>.type` set to something other than http, stdio, or sse — e.g. "streamable", "websocket", "SSE" if the config parser is case-sensitive, or a JSON config with a stale type name from an older schema.
Common situations: Copying an MCP config from Claude Code or another tool that uses different type names ("sse" vs "http" semantics, "streamable-http"); manual edit typos; migrating configs across Crush versions where type names changed.
Related errors
- mcp stdio config requires a non-empty 'command' field
- mcp http config requires a non-empty 'url' field
- mcp sse config requires a non-empty 'url' field
- not a valid bedrock api key
- not a valid vercel api key
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/2baf7c9ec5f07af8.
Report an issue: GitHub.