fix agent builder tool calls

This commit is contained in:
Saumya 2025-07-26 11:04:07 +05:30
parent 8f2ea13fa2
commit 845c17d968
10 changed files with 194 additions and 101 deletions

View File

@ -2243,11 +2243,17 @@ async def get_pipedream_tools_for_agent(
available_tools = server.available_tools
formatted_tools = []
def tools_match(api_tool_name, stored_tool_name):
api_normalized = api_tool_name.lower().replace('-', '_')
stored_normalized = stored_tool_name.lower().replace('-', '_')
return api_normalized == stored_normalized
for tool in available_tools:
is_enabled = any(tools_match(tool.name, stored_tool) for stored_tool in enabled_tools)
formatted_tools.append({
'name': tool.name,
'description': tool.description or f"Tool from {profile.app_name}",
'enabled': tool.name in enabled_tools
'enabled': is_enabled
})
return {
@ -2285,9 +2291,8 @@ async def update_pipedream_tools_for_agent(
):
try:
client = await db.client
agent_row = await client.table('agents')\
.select('custom_mcps')\
.select('config')\
.eq('agent_id', agent_id)\
.eq('account_id', user_id)\
.maybe_single()\
@ -2295,7 +2300,9 @@ async def update_pipedream_tools_for_agent(
if not agent_row.data:
raise HTTPException(status_code=404, detail="Agent not found")
custom_mcps = agent_row.data.get('custom_mcps', []) or []
agent_config = agent_row.data.get('config', {})
tools = agent_config.get('tools', {})
custom_mcps = tools.get('custom_mcp', []) or []
if any(mcp.get('config', {}).get('profile_id') == profile_id for mcp in custom_mcps):
raise HTTPException(status_code=400, detail="This profile is already added to this agent")
@ -2336,7 +2343,9 @@ async def get_custom_mcp_tools_for_agent(
raise HTTPException(status_code=404, detail="Agent not found")
agent = agent_result.data[0]
custom_mcps = agent.get('custom_mcps', [])
agent_config = agent.get('config', {})
tools = agent_config.get('tools', {})
custom_mcps = tools.get('custom_mcp', [])
mcp_url = request.headers.get('X-MCP-URL')
mcp_type = request.headers.get('X-MCP-Type', 'sse')
@ -2409,7 +2418,9 @@ async def update_custom_mcp_tools_for_agent(
raise HTTPException(status_code=404, detail="Agent not found")
agent = agent_result.data[0]
custom_mcps = agent.get('custom_mcps', [])
agent_config = agent.get('config', {})
tools = agent_config.get('tools', {})
custom_mcps = tools.get('custom_mcp', [])
mcp_url = request.get('url')
mcp_type = request.get('type', 'sse')
@ -2438,8 +2449,12 @@ async def update_custom_mcp_tools_for_agent(
}
custom_mcps.append(new_mcp_config)
# Update the config structure with the modified custom MCPs
tools['custom_mcp'] = custom_mcps
agent_config['tools'] = tools
update_result = await client.table('agents').update({
'custom_mcps': custom_mcps
'config': agent_config
}).eq('agent_id', agent_id).execute()
if not update_result.data:

View File

@ -659,6 +659,58 @@ For casual conversation and social interactions:
* The system will continue running in a loop if completion is not signaled
* Additional commands after completion are considered errors
* Redundant verifications after completion are prohibited
# 🔧 SELF-CONFIGURATION CAPABILITIES
You have the ability to configure and enhance yourself! When users ask you to modify your capabilities, add integrations, create workflows, or set up automation, you can use these advanced tools:
## 🛠️ Available Self-Configuration Tools
### Agent Configuration (`update_agent`)
- Add MCP integrations to connect with external services
- Create and manage workflows for structured processes
- Set up triggers for scheduled automation
- Configure credential profiles for secure service connections
### MCP Integration Tools
- `search_mcp_servers`: Find integrations for specific services (Gmail, Slack, GitHub, etc.)
- `get_popular_mcp_servers`: Browse trending integrations
- `get_mcp_server_tools`: Explore integration capabilities
- `configure_mcp_server`: Set up external service connections
### Credential Management
- `create_credential_profile`: Set up secure connections to external services
- `connect_credential_profile`: Generate connection links for users
- `check_profile_connection`: Verify service connections
- `configure_profile_for_agent`: Add connected services to your configuration
### Workflow & Automation
- `create_workflow`: Design multi-step automated processes
- `create_scheduled_trigger`: Set up time-based automation
- `get_workflows` / `get_scheduled_triggers`: Review existing automation
## 🎯 When Users Request Configuration Changes
**If a user asks you to:**
- "Add Gmail integration" Search for Gmail MCP, create credential profile, guide connection
- "Set up daily reports" Create workflow + scheduled trigger
- "Connect to Slack" Find Slack integration, set up credential profile
- "Automate [task]" Design appropriate workflow/trigger combination
- "Add [service] capabilities" Search for relevant MCP servers
**Important Guidelines:**
- Always search for integrations before creating credential profiles
- Guide users through connection processes step-by-step
- Explain what each integration enables and why it's useful
- Test connections before finalizing configurations
- Only enable tools that users specifically request or need
- **ALWAYS ADD THE NECESSARY TOOLS** - If you don't have capabilities for something, automatically search for and add the required MCP integrations
## 🌟 Self-Configuration Philosophy
You are Suna, and you can now evolve and adapt based on user needs. When someone asks you to gain new capabilities or connect to services, proactively use these tools to enhance yourself. You're not just following instructions - you're actively improving your own abilities to better serve your users.
Remember: You maintain all your core Suna capabilities while gaining the power to extend and customize yourself. This makes you more helpful and adaptable to each user's specific needs.
"""
@ -669,4 +721,6 @@ def get_system_prompt():
return SYSTEM_PROMPT.format(
current_date=datetime.datetime.now(datetime.timezone.utc).strftime('%Y-%m-%d'),
current_time=datetime.datetime.now(datetime.timezone.utc).strftime('%H:%M:%S')
)
)

View File

@ -35,64 +35,6 @@ from agentpress.tool import SchemaType
load_dotenv()
def _get_suna_builder_enhancement():
"""
Returns additional system prompt content for Suna when self-configuration is enabled.
This enhances Suna's default capabilities with agent builder functionality.
"""
return """
# 🔧 SELF-CONFIGURATION CAPABILITIES
You now have the ability to configure and enhance yourself! When users ask you to modify your capabilities, add integrations, create workflows, or set up automation, you can use these advanced tools:
## 🛠️ Available Self-Configuration Tools
### Agent Configuration (`update_agent`)
- Add MCP integrations to connect with external services
- Create and manage workflows for structured processes
- Set up triggers for scheduled automation
- Configure credential profiles for secure service connections
### MCP Integration Tools
- `search_mcp_servers`: Find integrations for specific services (Gmail, Slack, GitHub, etc.)
- `get_popular_mcp_servers`: Browse trending integrations
- `get_mcp_server_tools`: Explore integration capabilities
- `configure_mcp_server`: Set up external service connections
### Credential Management
- `create_credential_profile`: Set up secure connections to external services
- `connect_credential_profile`: Generate connection links for users
- `check_profile_connection`: Verify service connections
- `configure_profile_for_agent`: Add connected services to your configuration
### Workflow & Automation
- `create_workflow`: Design multi-step automated processes
- `create_scheduled_trigger`: Set up time-based automation
- `get_workflows` / `get_scheduled_triggers`: Review existing automation
## 🎯 When Users Request Configuration Changes
**If a user asks you to:**
- "Add Gmail integration" Search for Gmail MCP, create credential profile, guide connection
- "Set up daily reports" Create workflow + scheduled trigger
- "Connect to Slack" Find Slack integration, set up credential profile
- "Automate [task]" Design appropriate workflow/trigger combination
- "Add [service] capabilities" Search for relevant MCP servers
**Important Guidelines:**
- Always search for integrations before creating credential profiles
- Guide users through connection processes step-by-step
- Explain what each integration enables and why it's useful
- Test connections before finalizing configurations
- Only enable tools that users specifically request or need
## 🌟 Self-Configuration Philosophy
You are Suna, and you can now evolve and adapt based on user needs. When someone asks you to gain new capabilities or connect to services, proactively use these tools to enhance yourself. You're not just following instructions - you're actively improving your own abilities to better serve your users.
Remember: You maintain all your core Suna capabilities while gaining the power to extend and customize yourself. This makes you more helpful and adaptable to each user's specific needs.
"""
async def run_agent(
thread_id: str,
@ -147,10 +89,8 @@ async def run_agent(
# Check if this is Suna (default agent) and enable builder capabilities for self-configuration
suna_builder_enabled = False
if agent_config and agent_config.get('is_suna_default', False):
logger.info("Detected Suna default agent - enabling self-configuration capabilities")
suna_builder_enabled = True
from agent.tools.agent_builder_tools.agent_config_tool import AgentConfigTool
from agent.tools.agent_builder_tools.mcp_search_tool import MCPSearchTool
@ -332,16 +272,10 @@ async def run_agent(
# Handle custom agent system prompt
if agent_config and agent_config.get('system_prompt'):
custom_system_prompt = agent_config['system_prompt'].strip()
# Special case: If this is Suna with builder capabilities enabled, enhance the default prompt
if suna_builder_enabled:
system_content = custom_system_prompt + "\n\n" + _get_suna_builder_enhancement()
logger.info(f"Using Suna default system prompt with self-configuration capabilities")
else:
# Completely replace the default system prompt with the custom one
# This prevents confusion and tool hallucination
system_content = custom_system_prompt
logger.info(f"Using ONLY custom agent system prompt for: {agent_config.get('name', 'Unknown')}")
# Completely replace the default system prompt with the custom one
# This prevents confusion and tool hallucination
system_content = custom_system_prompt
logger.info(f"Using ONLY custom agent system prompt for: {agent_config.get('name', 'Unknown')}")
elif is_agent_builder:
system_content = get_agent_builder_prompt()
logger.info("Using agent builder system prompt")

View File

@ -422,14 +422,20 @@ class CredentialProfileTool(AgentBuilderBaseTool):
if not profile:
return self.fail_response("Credential profile not found")
agent_result = await client.table('agents').select('custom_mcps').eq('agent_id', self.agent_id).execute()
agent_result = await client.table('agents').select('config').eq('agent_id', self.agent_id).execute()
if agent_result.data:
current_custom_mcps = agent_result.data[0].get('custom_mcps', [])
current_config = agent_result.data[0].get('config', {})
current_tools = current_config.get('tools', {})
current_custom_mcps = current_tools.get('custom_mcp', [])
updated_mcps = [mcp for mcp in current_custom_mcps if mcp.get('config', {}).get('profile_id') != str(profile.profile_id)]
if len(updated_mcps) != len(current_custom_mcps):
current_tools['custom_mcp'] = updated_mcps
current_config['tools'] = current_tools
await client.table('agents').update({
'custom_mcps': updated_mcps
'config': current_config
}).eq('agent_id', self.agent_id).execute()
await self.pipedream_manager.delete_profile(account_id, profile_id)

View File

@ -240,7 +240,9 @@ class PipedreamManager:
agent = agent_result.data[0]
agent_custom_mcps = agent.get('custom_mcps', [])
agent_config = agent.get('config', {})
tools = agent_config.get('tools', {})
agent_custom_mcps = tools.get('custom_mcp', [])
version_custom_mcps = []
if agent.get('current_version_id'):
@ -254,36 +256,44 @@ class PipedreamManager:
except Exception as e:
pass
# Prioritize version MCPs, but ensure we get the right data
pipedream_mcp = None
# First, look in version custom MCPs (most current)
print(f"[PROFILE {profile_id}] Searching for pipedream MCP. Version MCPs: {len(version_custom_mcps)}, Agent MCPs: {len(agent_custom_mcps)}")
print(f"[PROFILE {profile_id}] Version custom MCPs: {version_custom_mcps}")
print(f"[PROFILE {profile_id}] Agent custom MCPs: {agent_custom_mcps}")
for mcp in version_custom_mcps:
mcp_type = mcp.get('type')
mcp_config = mcp.get('config', {})
mcp_profile_id = mcp_config.get('profile_id')
print(f"[PROFILE {profile_id}] Version MCP: type={mcp_type}, profile_id={mcp_profile_id}, target_profile_id={profile_id}")
if mcp_type == 'pipedream' and mcp_profile_id == profile_id:
pipedream_mcp = mcp
print(f"[PROFILE {profile_id}] Found matching MCP in version data: {mcp}")
break
# Fallback to agent custom MCPs only if not found in version
if not pipedream_mcp:
print(f"[PROFILE {profile_id}] No matching MCP found in version data, checking agent data")
for mcp in agent_custom_mcps:
mcp_type = mcp.get('type')
mcp_config = mcp.get('config', {})
mcp_profile_id = mcp_config.get('profile_id')
print(f"[PROFILE {profile_id}] Agent MCP: type={mcp_type}, profile_id={mcp_profile_id}, target_profile_id={profile_id}")
if mcp_type == 'pipedream' and mcp_profile_id == profile_id:
pipedream_mcp = mcp
print(f"[PROFILE {profile_id}] Found matching MCP in agent data: {mcp}")
break
if not pipedream_mcp:
print(f"[PROFILE {profile_id}] No matching pipedream MCP found!")
return []
# Handle both naming conventions for enabled tools
enabled_tools = pipedream_mcp.get('enabledTools', pipedream_mcp.get('enabled_tools', []))
logger.info(f"[PROFILE {profile_id}] Found MCP in {'version' if pipedream_mcp in version_custom_mcps else 'agent'} data with {len(enabled_tools)} enabled tools: {enabled_tools}")
print(f"[PROFILE {profile_id}] Found MCP in {'version' if pipedream_mcp in version_custom_mcps else 'agent'} data with {len(enabled_tools)} enabled tools: {enabled_tools}")
return enabled_tools
async def get_enabled_tools_for_agent_profile_version(
@ -315,21 +325,28 @@ class PipedreamManager:
return []
pipedream_mcp = None
print(f"[VERSION {version_id}] [PROFILE {profile_id}] Searching for pipedream MCP. Version MCPs: {len(version_custom_mcps)}")
print(f"[VERSION {version_id}] [PROFILE {profile_id}] Version custom MCPs: {version_custom_mcps}")
for mcp in version_custom_mcps:
mcp_type = mcp.get('type')
mcp_config = mcp.get('config', {})
mcp_profile_id = mcp_config.get('profile_id')
print(f"[VERSION {version_id}] [PROFILE {profile_id}] Version MCP: type={mcp_type}, profile_id={mcp_profile_id}, target_profile_id={profile_id}")
if mcp_type == 'pipedream' and mcp_profile_id == profile_id:
pipedream_mcp = mcp
print(f"[VERSION {version_id}] [PROFILE {profile_id}] Found matching MCP in version data: {mcp}")
break
if not pipedream_mcp:
print(f"[VERSION {version_id}] [PROFILE {profile_id}] No matching pipedream MCP found!")
return []
# Handle both naming conventions for enabled tools
enabled_tools = pipedream_mcp.get('enabledTools', pipedream_mcp.get('enabled_tools', []))
logger.info(f"[VERSION {version_id}] [PROFILE {profile_id}] Found MCP with {len(enabled_tools)} enabled tools: {enabled_tools}")
print(f"[VERSION {version_id}] [PROFILE {profile_id}] Found MCP with {len(enabled_tools)} enabled tools: {enabled_tools}")
return enabled_tools
async def update_agent_profile_tools(
@ -377,10 +394,12 @@ class PipedreamManager:
agentpress_tools = current_version_data.get('agentpress_tools', {})
current_custom_mcps = current_version_data.get('custom_mcps', [])
else:
system_prompt = agent.get('system_prompt', '')
configured_mcps = agent.get('configured_mcps', [])
agentpress_tools = agent.get('agentpress_tools', {})
current_custom_mcps = agent.get('custom_mcps', [])
agent_config = agent.get('config', {})
system_prompt = agent_config.get('system_prompt', '')
tools = agent_config.get('tools', {})
configured_mcps = tools.get('mcp', [])
agentpress_tools = tools.get('agentpress', {})
current_custom_mcps = tools.get('custom_mcp', [])
updated_custom_mcps = copy.deepcopy(current_custom_mcps)
@ -415,8 +434,7 @@ class PipedreamManager:
}
updated_custom_mcps.append(new_mcp_config)
# Create new version with updated configuration
new_version = await version_manager.create_version(
agent_id=agent_id,
user_id=user_id,
@ -426,8 +444,15 @@ class PipedreamManager:
agentpress_tools=agentpress_tools,
change_description=f"Updated {profile.app_name} tools"
)
# Update the agent's config to reflect the changes in the unified config structure
current_config = agent.get('config', {})
current_tools = current_config.get('tools', {})
current_tools['custom_mcp'] = updated_custom_mcps
current_config['tools'] = current_tools
update_result = await client.table('agents').update({
'custom_mcps': updated_custom_mcps,
'config': current_config,
'current_version_id': new_version['version_id']
}).eq('agent_id', agent_id).execute()

View File

@ -48,6 +48,12 @@ export const MCPConfigurationNew: React.FC<MCPConfigurationProps> = ({
const handleConfigureTools = (index: number) => {
const mcp = configuredMCPs[index];
console.log('[MCPConfiguration] Configure tools clicked for MCP:', {
index,
mcp,
enabledTools: mcp.enabledTools,
customType: mcp.customType
});
setSelectedMCPForTools(mcp);
if (mcp.customType === 'pipedream') {
const profileId = mcp.selectedProfileId || mcp.config?.profile_id;
@ -240,6 +246,14 @@ export const MCPConfigurationNew: React.FC<MCPConfigurationProps> = ({
versionData={versionData}
saveMode={saveMode}
versionId={versionId}
initialEnabledTools={(() => {
console.log('[MCPConfiguration] Rendering Pipedream ToolsManager with:', {
selectedMCPForTools,
enabledTools: selectedMCPForTools.enabledTools,
profileId: selectedMCPForTools.selectedProfileId || selectedMCPForTools.config?.profile_id
});
return selectedMCPForTools.enabledTools;
})()}
/>
)}
{selectedMCPForTools && selectedMCPForTools.customType !== 'pipedream' && (
@ -257,6 +271,14 @@ export const MCPConfigurationNew: React.FC<MCPConfigurationProps> = ({
versionData={versionData}
saveMode={saveMode}
versionId={versionId}
initialEnabledTools={(() => {
console.log('[MCPConfiguration] Rendering Custom ToolsManager with:', {
selectedMCPForTools,
enabledTools: selectedMCPForTools.enabledTools,
customType: selectedMCPForTools.customType
});
return selectedMCPForTools.enabledTools;
})()}
/>
)}
</div>

View File

@ -40,6 +40,7 @@ interface BaseToolsManagerProps {
};
saveMode?: 'direct' | 'callback';
versionId?: string;
initialEnabledTools?: string[];
}
interface PipedreamToolsManagerProps extends BaseToolsManagerProps {
@ -58,7 +59,7 @@ interface CustomToolsManagerProps extends BaseToolsManagerProps {
type ToolsManagerProps = PipedreamToolsManagerProps | CustomToolsManagerProps;
export const ToolsManager: React.FC<ToolsManagerProps> = (props) => {
const { agentId, open, onOpenChange, onToolsUpdate, mode, versionData, saveMode = 'direct', versionId } = props;
const { agentId, open, onOpenChange, onToolsUpdate, mode, versionData, saveMode = 'direct', versionId, initialEnabledTools } = props;
const updatePipedreamTools = useUpdatePipedreamToolsForAgent();
const pipedreamResult = usePipedreamToolsData(
@ -91,14 +92,25 @@ export const ToolsManager: React.FC<ToolsManagerProps> = (props) => {
React.useEffect(() => {
if (data?.tools) {
console.log('[ToolsManager] API data received:', {
tools: data.tools,
initialEnabledTools,
mode,
data
});
const toolsMap: Record<string, boolean> = {};
data.tools.forEach((tool: { name: string; enabled: boolean }) => {
toolsMap[tool.name] = tool.enabled;
console.log(`[ToolsManager] Tool ${tool.name}: using API enabled=${tool.enabled}`);
});
console.log('[ToolsManager] Final toolsMap:', toolsMap);
console.log('[ToolsManager] Setting localTools to:', toolsMap);
setLocalTools(toolsMap);
setHasChanges(false);
}
}, [data]);
}, [data, initialEnabledTools]);
const enabledCount = useMemo(() => {
return Object.values(localTools).filter(Boolean).length;
@ -115,7 +127,11 @@ export const ToolsManager: React.FC<ToolsManagerProps> = (props) => {
const updated = { ...prev, [toolName]: newValue };
const comparisonState: Record<string, boolean> = {};
data?.tools?.forEach((tool: any) => {
comparisonState[tool.name] = tool.enabled;
if (initialEnabledTools && initialEnabledTools.length > 0) {
comparisonState[tool.name] = initialEnabledTools.includes(tool.name);
} else {
comparisonState[tool.name] = tool.enabled;
}
});
const hasChanges = Object.keys(updated).some(key => updated[key] !== comparisonState[key]);
setHasChanges(hasChanges);
@ -160,12 +176,15 @@ export const ToolsManager: React.FC<ToolsManagerProps> = (props) => {
const handleCancel = () => {
if (data?.tools) {
const serverState: Record<string, boolean> = {};
const resetState: Record<string, boolean> = {};
data.tools.forEach((tool: any) => {
serverState[tool.name] = tool.enabled;
if (initialEnabledTools && initialEnabledTools.length > 0) {
resetState[tool.name] = initialEnabledTools.includes(tool.name);
} else {
resetState[tool.name] = tool.enabled;
}
});
setLocalTools(serverState);
setLocalTools(resetState);
setHasChanges(false);
}
};

View File

@ -414,7 +414,8 @@ export const PipedreamRegistry: React.FC<PipedreamRegistryProps> = ({
queryClient.invalidateQueries({ queryKey: ['agent', currentAgentId] });
}}
versionData={effectiveVersionData}
versionId={versionId}
// Don't pass versionId for existing integrations - we want current configuration
versionId={undefined}
/>
)}
</div>

View File

@ -19,6 +19,13 @@ export const useCustomMCPToolsData = (agentId: string, mcpConfig: any) => {
const { data, isLoading, error, refetch } = useQuery<CustomMCPToolsResponse>({
queryKey: ['custom-mcp-tools', agentId, mcpConfig?.url],
queryFn: async () => {
console.log('[useCustomMCPToolsData] Making API request with config:', {
agentId,
mcpConfig,
url: mcpConfig?.url,
type: mcpConfig?.type,
headers: mcpConfig?.headers
});
const response = await backendApi.get(`/agents/${agentId}/custom-mcp-tools`, {
headers: {
'X-MCP-URL': mcpConfig.url,
@ -26,6 +33,10 @@ export const useCustomMCPToolsData = (agentId: string, mcpConfig: any) => {
...(mcpConfig.headers ? { 'X-MCP-Headers': JSON.stringify(mcpConfig.headers) } : {})
}
});
console.log('[useCustomMCPToolsData] API response received:', {
response: response.data,
tools: response.data?.tools
});
if (!response.success) {
throw new Error(response.error?.message || 'Failed to fetch custom MCP tools');
}

View File

@ -28,7 +28,13 @@ export const usePipedreamToolsForAgent = (agentId: string, profileId: string, ve
const url = versionId
? `/agents/${agentId}/pipedream-tools/${profileId}?version=${versionId}`
: `/agents/${agentId}/pipedream-tools/${profileId}`;
console.log('[usePipedreamToolsForAgent] Making API request to:', url);
const response = await backendApi.get(url);
console.log('[usePipedreamToolsForAgent] API response received:', {
url,
response: response.data,
tools: JSON.stringify(response.data?.tools)
});
return response.data;
},
enabled: !!agentId && !!profileId,