mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-17 22:14:14 +00:00
fix(mcp): wire per-user MCP tool discovery into agent pipeline
MCP servers with require_user_credentials (e.g. Notion) were defined
but never loaded into the agent's tool registry. Three gaps:
1. getUserMCPTools was defined but never called — add call in
makeBuildFilteredTools before FilterTools runs each iteration.
2. hasMCPTools stayed false when only user-credential servers existed,
so agentToolPolicyWithMCP never injected "group:mcp" into alsoAllow.
Now set true when mcpUserCredSrvs is non-empty.
3. Per-user BridgeTools were registered in the registry but never added
to the "mcp" tool group, so expandSpec("group:mcp") returned empty.
Add MergeToolGroup helper for additive group updates.
Also add debug log when getUserMCPTools skips due to empty userID.
This commit is contained in:
1 parent
7d6fcc3b7e
commit
e79a8bbd39
4 files changed
+40
No files matched your search
@@ -15,6 +15,9 @@ import (
|
||||
// connections are established via pool.AcquireUser() and BridgeTools created.
|
||||
func (l *Loop) getUserMCPTools(ctx context.Context, userID string) []tools.Tool {
|
||||
if len(l.mcpUserCredSrvs) == 0 || l.mcpPool == nil || l.mcpStore == nil || userID == "" {
|
||||
if userID == "" && len(l.mcpUserCredSrvs) > 0 {
|
||||
slog.Debug("mcp.user_tools_skipped", "reason", "empty_user_id", "servers", len(l.mcpUserCredSrvs))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -100,6 +103,13 @@ func (l *Loop) getUserMCPTools(ctx context.Context, userID string) []tools.Tool
|
||||
|
||||
if len(userTools) > 0 {
|
||||
l.mcpUserTools.Store(userID, userTools)
|
||||
// Update "mcp" tool group so policy expansion via alsoAllow includes
|
||||
// per-user tools. MergeToolGroup is additive — safe for concurrent users.
|
||||
var names []string
|
||||
for _, t := range userTools {
|
||||
names = append(names, t.Name())
|
||||
}
|
||||
tools.MergeToolGroup("mcp", names)
|
||||
slog.Info("mcp.user_tools_loaded", "user", userID, "tools", len(userTools))
|
||||
}
|
||||
return userTools
|
||||
|
||||
@@ -191,6 +191,11 @@ func (l *Loop) makeInjectReminders(req *RunRequest) func(ctx context.Context, in
|
||||
|
||||
func (l *Loop) makeBuildFilteredTools(req *RunRequest) func(state *pipeline.RunState) ([]providers.ToolDefinition, error) {
|
||||
return func(state *pipeline.RunState) ([]providers.ToolDefinition, error) {
|
||||
// Load per-user MCP tools (Notion, etc.) into registry before filtering.
|
||||
// Servers with require_user_credentials are deferred at startup and
|
||||
// connected per-request here with the actual user's credentials.
|
||||
l.getUserMCPTools(state.Ctx, state.Input.UserID)
|
||||
|
||||
maxIter := l.maxIterations
|
||||
if req.MaxIterations > 0 && req.MaxIterations < maxIter {
|
||||
maxIter = req.MaxIterations
|
||||
|
||||
@@ -305,6 +305,12 @@ func NewManagedResolver(deps ResolverDeps) ResolverFunc {
|
||||
slog.Warn("failed to load MCP servers for agent", "agent", agentKey, "error", err)
|
||||
} else {
|
||||
mcpUserCredSrvs = mcpMgr.UserCredServers()
|
||||
// User-credential servers (Notion, etc.) are deferred at startup
|
||||
// but will produce tools per-request via getUserMCPTools.
|
||||
// Set flag so agentToolPolicyWithMCP injects "group:mcp" into alsoAllow.
|
||||
if len(mcpUserCredSrvs) > 0 {
|
||||
hasMCPTools = true
|
||||
}
|
||||
if mcpMgr.IsSearchMode() {
|
||||
// Search mode: too many tools — register mcp_tool_search meta-tool.
|
||||
// Also wire lazy activator so deferred tools can be called by name directly.
|
||||
|
||||
@@ -50,6 +50,25 @@ func RegisterToolGroup(name string, members []string) {
|
||||
toolGroupsMu.Unlock()
|
||||
}
|
||||
|
||||
// MergeToolGroup adds members to an existing tool group without removing existing entries.
|
||||
// Used by per-user MCP tool loading to extend the "mcp" group incrementally.
|
||||
func MergeToolGroup(name string, members []string) {
|
||||
toolGroupsMu.Lock()
|
||||
defer toolGroupsMu.Unlock()
|
||||
existing := toolGroups[name]
|
||||
seen := make(map[string]bool, len(existing))
|
||||
for _, m := range existing {
|
||||
seen[m] = true
|
||||
}
|
||||
for _, m := range members {
|
||||
if !seen[m] {
|
||||
existing = append(existing, m)
|
||||
seen[m] = true
|
||||
}
|
||||
}
|
||||
toolGroups[name] = existing
|
||||
}
|
||||
|
||||
// UnregisterToolGroup removes a dynamic tool group.
|
||||
func UnregisterToolGroup(name string) {
|
||||
toolGroupsMu.Lock()
|
||||
|
||||
Reference in new issue
Block a user