{"record":{"id":"6a5b42d26d3843e7","repo":"charmbracelet/crush","slug":"mcp-s-not-available","errorCode":null,"errorMessage":"mcp '%s' not available","messagePattern":"mcp '(.+?)' not available","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/agent/tools/mcp/init.go","lineNumber":687,"sourceCode":"\n\t// Serialize renewals per server. Two concurrent tool calls can both\n\t// observe a dead session and race to rebuild it: one may close the\n\t// session the other just registered, or overwrite and leak a live\n\t// replacement. Under this lock only the first arrival rebuilds; later\n\t// arrivals re-check and reuse the healthy result.\n\tmu := renewLock(name)\n\tmu.Lock()\n\tdefer mu.Unlock()\n\n\t// Under the lock the map is stable: any in-flight renewal has finished and\n\t// either re-registered its session or failed and left none. A renewal\n\t// removes the session transiently (StateError takes it before rebuilding),\n\t// so this check must happen here rather than before the lock — otherwise a\n\t// caller arriving mid-renewal sees no session and wrongly reports the\n\t// server unavailable.\n\tsess, ok := sessions.Get(name)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"mcp '%s' not available\", name)\n\t}\n\n\t// A concurrent goroutine may have already renewed the session while we\n\t// waited for the lock. Reuse it if it is now healthy.\n\tpingErr := pingSession(ctx, sess, timeout)\n\tif pingErr == nil {\n\t\treturn sess, nil\n\t}\n\n\tstate, _ := states.Get(name)\n\t// StateError closes the dead session and clears its tools, prompts, and\n\t// resources from the registry.\n\tupdateState(name, StateError, maybeTimeoutErr(pingErr, timeout), nil, state.Counts)\n\n\t// Capture the generation so a reconcile teardown that lands mid-renewal\n\t// invalidates this rebuild instead of letting it clobber the newer one.\n\tgen := currentGen(name)\n\tnewSess, err := newSession(ctx, cfg, name, m, cfg.Resolver(), channelEnabled(cfg.Overrides().EnabledChannels, name))","sourceCodeStart":669,"sourceCodeEnd":705,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/tools/mcp/init.go#L669-L705","documentation":"getOrRenewClient looks up the named MCP session in the sessions registry; if absent it reports the server as unavailable. This check is deliberately done after acquiring the lock because a concurrent renewal transiently removes/errored sessions, and callers arriving mid-renewal must not see a spurious 'not available'.","triggerScenarios":"Calling RunTool, ListResources, ReadResource, or GetPromptMessages for an MCP server name that was never configured, failed init (StateError and removed), or is being rebuilt concurrently and the session is missing.","commonSituations":"Typo in the MCP server name in config or tool invocation; the server failed at startup (see init errors); agent tool called before the client finished initializing; server was removed from config while a session was in flight.","solutions":["Verify the MCP server name matches an entry in your config (mcp builtin / crush.json)","Check startup logs for that server's init failure and fix the underlying cause","Retry after the renewal completes if the call raced a reconnection","List configured MCP servers (crush models / config output) to confirm the name exists"],"exampleFix":"// before: calling with a wrong name\nclient.RunTool(ctx, \"github-mcp\", \"create_issue\", args)\n\n// after: name matches an mcp entry in config\nclient.RunTool(ctx, \"github\", \"create_issue\", args)","handlingStrategy":"validation","validationCode":"func clientReady(sessions *SessionRegistry, name string) error {\n    if _, ok := sessions.Get(name); !ok {\n        return fmt.Errorf(\"mcp '%s' is not configured or failed to start; check your mcp config\", name)\n    }\n    return nil\n}\n// call before RunTool/ListResources/etc.","typeGuard":null,"tryCatchPattern":"if err := client.RunTool(ctx, name, tool, args); err != nil && strings.Contains(err.Error(), \"not available\") {\n    // re-check config / wait for init, then retry once\n}","preventionTips":["Confirm MCP server names in tool calls match config entries exactly","Wait for initialization to complete before issuing tool calls","Check startup logs for that server's init errors","Treat this error as configuration drift, not a transient fault"],"tags":["mcp","session","lookup"],"backgroundTag":"mcp-server-not-found","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}