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

  1. Change the type to one of the supported values: "http" (streamable), "sse", or "stdio"
  2. If the server speaks streamable HTTP, use type "http" instead of "streamable" or "streamable-http"
  3. Check the Crush docs for the current MCP config schema and update older configs
  4. 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

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


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/2baf7c9ec5f07af8. Report an issue: GitHub.