Change summary
internal/agent/coordinator.go | 6 ++++++
internal/agent/tools/mcp/init.go | 14 ++++++++++++++
2 files changed, 20 insertions(+)
Detailed changes
@@ -20,6 +20,7 @@ import (
"github.com/charmbracelet/crush/internal/agent/hyper"
"github.com/charmbracelet/crush/internal/agent/prompt"
"github.com/charmbracelet/crush/internal/agent/tools"
+ "github.com/charmbracelet/crush/internal/agent/tools/mcp"
"github.com/charmbracelet/crush/internal/config"
"github.com/charmbracelet/crush/internal/csync"
"github.com/charmbracelet/crush/internal/history"
@@ -411,6 +412,11 @@ func (c *coordinator) buildTools(ctx context.Context, agent config.Agent) ([]fan
}
}
+ // Wait for MCP initialization to complete before reading MCP tools.
+ if err := mcp.WaitForInit(ctx); err != nil {
+ return nil, fmt.Errorf("failed to wait for MCP initialization: %w", err)
+ }
+
for _, tool := range tools.GetMCPTools(c.permissions, c.cfg.WorkingDir()) {
if agent.AllowedMCP == nil {
// No MCP restrictions
@@ -29,6 +29,8 @@ var (
sessions = csync.NewMap[string, *mcp.ClientSession]()
states = csync.NewMap[string, ClientInfo]()
broker = pubsub.NewBroker[Event]()
+ initOnce sync.Once
+ initDone = make(chan struct{})
)
// State represents the current state of an MCP client
@@ -197,6 +199,18 @@ func Initialize(ctx context.Context, permissions permission.Service, cfg *config
}(name, m)
}
wg.Wait()
+ initOnce.Do(func() { close(initDone) })
+}
+
+// WaitForInit blocks until MCP initialization is complete.
+// If Initialize was never called, this returns immediately.
+func WaitForInit(ctx context.Context) error {
+ select {
+ case <-initDone:
+ return nil
+ case <-ctx.Done():
+ return ctx.Err()
+ }
}
func getOrRenewClient(ctx context.Context, name string) (*mcp.ClientSession, error) {