mirror of https://github.com/kortix-ai/suna.git
cleanup agent builder tools
This commit is contained in:
parent
8e1cce5cbd
commit
26fd18b6b8
|
@ -45,7 +45,6 @@ Connect your agent to the world:
|
|||
- **`search_mcp_servers`**: Find integrations by keyword (Gmail, Slack, databases, etc.)
|
||||
- **`get_popular_mcp_servers`**: Browse trending, well-tested integrations
|
||||
- **`get_mcp_server_tools`**: Explore what each integration can do
|
||||
- **`configure_mcp_server`**: Set up and connect external services
|
||||
- **`test_mcp_server_connection`**: Verify everything works perfectly
|
||||
|
||||
### 🔐 Credential Profile Management
|
||||
|
|
|
@ -672,7 +672,6 @@ You have the ability to configure and enhance yourself! When users ask you to mo
|
|||
- `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
|
||||
|
|
|
@ -397,14 +397,11 @@ class PipedreamManager:
|
|||
print(f"[DEBUG] After deepcopy - updated_custom_mcps count: {len(updated_custom_mcps)}")
|
||||
print(f"[DEBUG] After deepcopy - updated_custom_mcps: {updated_custom_mcps}")
|
||||
|
||||
# Normalize enabledTools vs enabled_tools
|
||||
for mcp in updated_custom_mcps:
|
||||
if 'enabled_tools' in mcp and 'enabledTools' not in mcp:
|
||||
mcp['enabledTools'] = mcp['enabled_tools']
|
||||
elif 'enabledTools' not in mcp and 'enabled_tools' not in mcp:
|
||||
mcp['enabledTools'] = []
|
||||
if 'enabledTools' not in mcp:
|
||||
mcp['enabledTools'] = mcp.get('enabled_tools', [])
|
||||
|
||||
|
||||
# Look for existing MCP with same profile_id
|
||||
found_match = False
|
||||
for i, mcp in enumerate(updated_custom_mcps):
|
||||
print(f"[DEBUG] Checking MCP {i}: type={mcp.get('type')}, profile_id={mcp.get('config', {}).get('profile_id')}")
|
||||
|
@ -412,7 +409,6 @@ class PipedreamManager:
|
|||
mcp.get('config', {}).get('profile_id') == profile_id):
|
||||
print(f"[DEBUG] Found existing MCP at index {i}, updating tools from {mcp.get('enabledTools', [])} to {enabled_tools}")
|
||||
mcp['enabledTools'] = enabled_tools
|
||||
mcp['enabled_tools'] = enabled_tools
|
||||
found_match = True
|
||||
break
|
||||
|
||||
|
@ -428,8 +424,7 @@ class PipedreamManager:
|
|||
},
|
||||
"profile_id": profile_id
|
||||
},
|
||||
"enabledTools": enabled_tools,
|
||||
"enabled_tools": enabled_tools
|
||||
"enabledTools": enabled_tools
|
||||
}
|
||||
print(f"[DEBUG] New MCP config: {new_mcp_config}")
|
||||
updated_custom_mcps.append(new_mcp_config)
|
||||
|
|
|
@ -113,12 +113,7 @@ export default function AgentConfigurationPage() {
|
|||
});
|
||||
return;
|
||||
}
|
||||
if (restrictions.system_prompt_editable === false && formData.system_prompt !== originalData.system_prompt) {
|
||||
toast.error("Cannot save changes", {
|
||||
description: "Suna's system prompt cannot be modified.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (restrictions.tools_editable === false && JSON.stringify(formData.agentpress_tools) !== JSON.stringify(originalData.agentpress_tools)) {
|
||||
toast.error("Cannot save changes", {
|
||||
description: "Suna's default tools cannot be modified.",
|
||||
|
@ -138,7 +133,7 @@ export default function AgentConfigurationPage() {
|
|||
const newVersion = await createVersionMutation.mutateAsync({
|
||||
agentId,
|
||||
data: {
|
||||
system_prompt: formData.system_prompt,
|
||||
system_prompt: isSunaAgent ? '' : formData.system_prompt,
|
||||
configured_mcps: formData.configured_mcps,
|
||||
custom_mcps: normalizedCustomMcps,
|
||||
agentpress_tools: formData.agentpress_tools,
|
||||
|
|
|
@ -60,7 +60,7 @@ export function ConfigurationTab({
|
|||
const isSunaAgent = agentMetadata?.is_suna_default || false;
|
||||
|
||||
const mapAccordion = (val?: string) => {
|
||||
if (val === 'instructions') return 'system';
|
||||
if (val === 'instructions') return isSunaAgent ? 'integrations' : 'system';
|
||||
if (isSunaAgent && (val === 'system' || val === 'tools')) {
|
||||
return 'integrations';
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import React, { useRef, useState, useCallback } from 'react';
|
||||
import { ArrowDown, CircleDashed, CheckCircle, AlertTriangle } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Markdown } from '@/components/ui/markdown';
|
||||
import { UnifiedMessage, ParsedContent, ParsedMetadata } from '@/components/thread/types';
|
||||
import { FileAttachmentGrid } from '@/components/thread/file-attachment';
|
||||
import { useFilePreloader, FileCache } from '@/hooks/react-query/files';
|
||||
import { useFilePreloader } from '@/hooks/react-query/files';
|
||||
import { useAuth } from '@/components/AuthProvider';
|
||||
import { Project } from '@/lib/api';
|
||||
import {
|
||||
|
@ -13,13 +12,10 @@ import {
|
|||
getUserFriendlyToolName,
|
||||
safeJsonParse,
|
||||
} from '@/components/thread/utils';
|
||||
import { formatMCPToolDisplayName } from '@/components/thread/tool-views/mcp-tool/_utils';
|
||||
import { KortixLogo } from '@/components/sidebar/kortix-logo';
|
||||
import { AgentLoader } from './loader';
|
||||
import { parseXmlToolCalls, isNewXmlFormat, extractToolNameFromStream } from '@/components/thread/tool-views/xml-parser';
|
||||
import { parseToolResult } from '@/components/thread/tool-views/tool-result-parser';
|
||||
import { parseXmlToolCalls, isNewXmlFormat } from '@/components/thread/tool-views/xml-parser';
|
||||
import { ShowToolStream } from './ShowToolStream';
|
||||
import { PipedreamConnectButton } from './pipedream-connect-button';
|
||||
import { PipedreamUrlDetector } from './pipedream-url-detector';
|
||||
|
||||
const HIDE_STREAMING_XML_TAGS = new Set([
|
||||
|
@ -56,22 +52,6 @@ const HIDE_STREAMING_XML_TAGS = new Set([
|
|||
'execute-data-provider-endpoint',
|
||||
]);
|
||||
|
||||
function getEnhancedToolDisplayName(toolName: string, rawXml?: string): string {
|
||||
if (toolName === 'call-mcp-tool' && rawXml) {
|
||||
const toolNameMatch = rawXml.match(/tool_name="([^"]+)"/);
|
||||
if (toolNameMatch) {
|
||||
const fullToolName = toolNameMatch[1];
|
||||
const parts = fullToolName.split('_');
|
||||
if (parts.length >= 3 && fullToolName.startsWith('mcp_')) {
|
||||
const serverName = parts[1];
|
||||
const toolNamePart = parts.slice(2).join('_');
|
||||
return formatMCPToolDisplayName(serverName, toolNamePart);
|
||||
}
|
||||
}
|
||||
}
|
||||
return getUserFriendlyToolName(toolName);
|
||||
}
|
||||
|
||||
// Helper function to render attachments (keeping original implementation for now)
|
||||
export function renderAttachments(attachments: string[], fileViewerHandler?: (filePath?: string, filePathList?: string[]) => void, sandboxId?: string, project?: Project) {
|
||||
if (!attachments || attachments.length === 0) return null;
|
||||
|
|
|
@ -157,25 +157,6 @@ export function ToolCallSidePanel({
|
|||
const currentToolCall = currentSnapshot?.toolCall;
|
||||
const totalCalls = toolCallSnapshots.length;
|
||||
|
||||
const extractToolName = (toolCall: any) => {
|
||||
const rawName = toolCall?.assistantCall?.name || 'Tool Call';
|
||||
if (rawName === 'call-mcp-tool') {
|
||||
const assistantContent = toolCall?.assistantCall?.content;
|
||||
if (assistantContent) {
|
||||
try {
|
||||
const toolNameMatch = assistantContent.match(/tool_name="([^"]+)"/);
|
||||
if (toolNameMatch && toolNameMatch[1]) {
|
||||
const mcpToolName = toolNameMatch[1];
|
||||
return getUserFriendlyToolName(mcpToolName);
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
return 'External Tool';
|
||||
}
|
||||
return getUserFriendlyToolName(rawName);
|
||||
};
|
||||
|
||||
const completedToolCalls = toolCallSnapshots.filter(snapshot =>
|
||||
snapshot.toolCall.toolResult?.content &&
|
||||
snapshot.toolCall.toolResult.content !== 'STREAMING'
|
||||
|
|
|
@ -1,288 +0,0 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import {
|
||||
PlugIcon,
|
||||
CheckCircle,
|
||||
AlertTriangle,
|
||||
Loader2,
|
||||
ChevronDown,
|
||||
ChevronUp,
|
||||
Zap,
|
||||
Clock,
|
||||
Settings,
|
||||
} from 'lucide-react';
|
||||
import { ToolViewProps } from '../types';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Progress } from '@/components/ui/progress';
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { formatTimestamp } from '../utils';
|
||||
import { detectMCPFormat } from '../mcp-format-detector';
|
||||
import { MCPContentRenderer } from '../mcp-content-renderer';
|
||||
import {
|
||||
parseMCPResult,
|
||||
parseMCPToolCall,
|
||||
getMCPServerIcon,
|
||||
getMCPServerColor,
|
||||
formatMCPToolDisplayName,
|
||||
MCPResult,
|
||||
ParsedMCPTool
|
||||
} from './_utils';
|
||||
|
||||
export function McpToolView({
|
||||
name = 'call-mcp-tool',
|
||||
assistantContent,
|
||||
toolContent,
|
||||
assistantTimestamp,
|
||||
toolTimestamp,
|
||||
isSuccess = true,
|
||||
isStreaming = false,
|
||||
}: ToolViewProps) {
|
||||
const { resolvedTheme } = useTheme();
|
||||
const [progress, setProgress] = useState(0);
|
||||
const [expandedArgs, setExpandedArgs] = useState(false);
|
||||
const [expandedResult, setExpandedResult] = useState(false);
|
||||
|
||||
const parsedTool = parseMCPToolCall(assistantContent || '');
|
||||
const result = toolContent ? parseMCPResult(toolContent) : null;
|
||||
|
||||
const serverName = result?.mcp_metadata?.server_name || parsedTool.serverName;
|
||||
const toolName = result?.mcp_metadata?.tool_name || parsedTool.toolName;
|
||||
const fullToolName = result?.mcp_metadata?.full_tool_name || parsedTool.fullToolName;
|
||||
const argumentsCount = result?.mcp_metadata?.arguments_count ?? Object.keys(parsedTool.arguments).length;
|
||||
|
||||
const displayName = result?.mcp_metadata ?
|
||||
formatMCPToolDisplayName(serverName, toolName) :
|
||||
parsedTool.displayName;
|
||||
|
||||
const ServerIcon = getMCPServerIcon(serverName);
|
||||
const serverColor = getMCPServerColor(serverName);
|
||||
|
||||
useEffect(() => {
|
||||
if (isStreaming) {
|
||||
const timer = setInterval(() => {
|
||||
setProgress((prevProgress) => {
|
||||
if (prevProgress >= 95) {
|
||||
clearInterval(timer);
|
||||
return prevProgress;
|
||||
}
|
||||
return prevProgress + 8;
|
||||
});
|
||||
}, 400);
|
||||
return () => clearInterval(timer);
|
||||
} else {
|
||||
setProgress(100);
|
||||
}
|
||||
}, [isStreaming]);
|
||||
|
||||
const hasArguments = Object.keys(parsedTool.arguments).length > 0;
|
||||
|
||||
return (
|
||||
<Card className="gap-0 flex border shadow-none border-t border-b-0 border-x-0 p-0 rounded-none flex-col h-full overflow-hidden bg-card">
|
||||
<CardHeader className="h-14 bg-zinc-50/80 dark:bg-zinc-900/80 backdrop-blur-sm border-b p-2 px-4 space-y-2">
|
||||
<div className="flex flex-row items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={cn(
|
||||
"relative p-2 rounded-lg bg-gradient-to-br border",
|
||||
serverColor
|
||||
)}>
|
||||
<ServerIcon className="w-5 h-5 text-current" />
|
||||
<div className="absolute -bottom-1 -right-1 w-4 h-4 bg-white dark:bg-zinc-950 rounded-full flex items-center justify-center border border-zinc-200 dark:border-zinc-800">
|
||||
<PlugIcon className="w-2.5 h-2.5 text-zinc-600 dark:text-zinc-400" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-base font-medium text-zinc-900 dark:text-zinc-100">
|
||||
{displayName}
|
||||
</CardTitle>
|
||||
<p className="text-xs text-zinc-500 dark:text-zinc-400 mt-0.5">
|
||||
MCP Server • {serverName}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!isStreaming && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className={
|
||||
isSuccess && result && !result.isError
|
||||
? "bg-gradient-to-b from-emerald-200 to-emerald-100 text-emerald-700 dark:from-emerald-800/50 dark:to-emerald-900/60 dark:text-emerald-300"
|
||||
: "bg-gradient-to-b from-rose-200 to-rose-100 text-rose-700 dark:from-rose-800/50 dark:to-rose-900/60 dark:text-rose-300"
|
||||
}
|
||||
>
|
||||
{isSuccess && result && !result.isError ? (
|
||||
<CheckCircle className="h-3.5 w-3.5" />
|
||||
) : (
|
||||
<AlertTriangle className="h-3.5 w-3.5" />
|
||||
)}
|
||||
{isSuccess && result && !result.isError ? 'Completed successfully' : 'Execution failed'}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="p-0 h-full flex-1 overflow-hidden relative">
|
||||
{isStreaming ? (
|
||||
<div className="flex flex-col items-center justify-center h-full py-12 px-6 bg-gradient-to-b from-white to-zinc-50 dark:from-zinc-950 dark:to-zinc-900">
|
||||
<div className="text-center w-full max-w-xs">
|
||||
<div className="w-16 h-16 rounded-full mx-auto mb-6 flex items-center justify-center bg-gradient-to-b from-indigo-100 to-indigo-50 shadow-inner dark:from-indigo-800/40 dark:to-indigo-900/60 dark:shadow-indigo-950/20 relative">
|
||||
<div className="absolute inset-0 rounded-full border-2 border-indigo-200 dark:border-indigo-700 animate-pulse" />
|
||||
<ServerIcon className="h-6 w-6 text-indigo-600 dark:text-indigo-400 relative z-10" />
|
||||
<div className="absolute -bottom-1 -right-1">
|
||||
<Loader2 className="h-5 w-5 animate-spin text-indigo-500 dark:text-indigo-400" />
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="text-lg font-medium text-zinc-900 dark:text-zinc-100 mb-2">
|
||||
Executing MCP Tool
|
||||
</h3>
|
||||
<p className="text-sm text-zinc-500 dark:text-zinc-400 mb-1">
|
||||
<span className="font-medium">{displayName}</span>
|
||||
</p>
|
||||
<p className="text-xs text-zinc-400 dark:text-zinc-500 mb-6">
|
||||
via {serverName} server
|
||||
</p>
|
||||
<Progress value={progress} className="w-full h-2" />
|
||||
<p className="text-xs text-zinc-400 dark:text-zinc-500 mt-2">{progress}%</p>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<ScrollArea className="h-full w-full">
|
||||
<div className="p-4 space-y-4">
|
||||
|
||||
{/* Tool Information */}
|
||||
<div className="bg-zinc-50/70 dark:bg-zinc-900/30 p-4 rounded-lg border border-zinc-100 dark:border-zinc-800">
|
||||
<div className="flex items-center gap-2 mb-3">
|
||||
<Settings className="h-4 w-4 text-zinc-600 dark:text-zinc-400" />
|
||||
<h3 className="text-sm font-medium text-zinc-700 dark:text-zinc-300">Tool Details</h3>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3 text-sm">
|
||||
<div>
|
||||
<span className="text-zinc-500 dark:text-zinc-400">Server:</span>
|
||||
<span className="ml-2 font-medium text-zinc-700 dark:text-zinc-300">
|
||||
{serverName}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-zinc-500 dark:text-zinc-400">Tool:</span>
|
||||
<span className="ml-2 font-medium text-zinc-700 dark:text-zinc-300">
|
||||
{toolName}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-zinc-500 dark:text-zinc-400">Arguments:</span>
|
||||
<span className="ml-2 font-medium text-zinc-700 dark:text-zinc-300">
|
||||
{argumentsCount} parameter{argumentsCount !== 1 ? 's' : ''}
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span className="text-zinc-500 dark:text-zinc-400">Status:</span>
|
||||
<span className={cn(
|
||||
"ml-2 font-medium",
|
||||
isSuccess && result && !result.isError
|
||||
? "text-emerald-600 dark:text-emerald-400"
|
||||
: "text-red-600 dark:text-red-400"
|
||||
)}>
|
||||
{isSuccess && result && !result.isError ? 'Success' : 'Failed'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Show error type if available */}
|
||||
{result?.error_type && (
|
||||
<div className="mt-3 pt-3 border-t border-zinc-200 dark:border-zinc-700">
|
||||
<div className="flex items-center gap-2">
|
||||
<AlertTriangle className="h-4 w-4 text-red-500" />
|
||||
<span className="text-sm text-red-600 dark:text-red-400">
|
||||
Error Type: {result.error_type}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Result Section */}
|
||||
{result && (
|
||||
<Card className={cn(
|
||||
"border",
|
||||
result.success && !result.isError
|
||||
? "border-emerald-200 dark:border-emerald-800 bg-emerald-50/30 dark:bg-emerald-900/10"
|
||||
: "border-red-200 dark:border-red-800 bg-red-50/30 dark:bg-red-900/10"
|
||||
)}>
|
||||
<div
|
||||
className="p-3 cursor-pointer hover:bg-black/5 dark:hover:bg-white/5 transition-colors"
|
||||
onClick={() => setExpandedResult(!expandedResult)}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{result.success && !result.isError ? (
|
||||
<>
|
||||
<CheckCircle className="h-4 w-4 text-emerald-600 dark:text-emerald-400" />
|
||||
<h3 className="text-sm font-medium text-emerald-700 dark:text-emerald-300">
|
||||
Execution Result
|
||||
</h3>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<AlertTriangle className="h-4 w-4 text-red-600 dark:text-red-400" />
|
||||
<h3 className="text-sm font-medium text-red-700 dark:text-red-300">
|
||||
Error Result
|
||||
</h3>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{expandedResult ? (
|
||||
<ChevronUp className="h-4 w-4 text-zinc-500" />
|
||||
) : (
|
||||
<ChevronDown className="h-4 w-4 text-zinc-500" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{expandedResult && (
|
||||
<div className="border-t border-zinc-200 dark:border-zinc-800">
|
||||
<MCPContentRenderer
|
||||
detectionResult={detectMCPFormat(result.data || '')}
|
||||
rawContent={result.data || ''}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Timestamps */}
|
||||
{(assistantTimestamp || toolTimestamp) && (
|
||||
<div className="flex items-center gap-4 text-xs text-zinc-500 dark:text-zinc-400 pt-2 border-t border-zinc-100 dark:border-zinc-800">
|
||||
<div className="flex items-center gap-1">
|
||||
<Clock className="h-3 w-3" />
|
||||
<span>
|
||||
{toolTimestamp ? (
|
||||
`Completed ${formatTimestamp(toolTimestamp)}`
|
||||
) : assistantTimestamp ? (
|
||||
`Started ${formatTimestamp(assistantTimestamp)}`
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* MCP Info Footer */}
|
||||
<div className="flex items-start gap-2 p-3 rounded-md bg-indigo-50/70 dark:bg-indigo-900/20 border border-indigo-100 dark:border-indigo-800">
|
||||
<Zap className="h-4 w-4 text-indigo-600 dark:text-indigo-400 mt-0.5 flex-shrink-0" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-indigo-700 dark:text-indigo-300 mb-1">
|
||||
Model Context Protocol (MCP)
|
||||
</p>
|
||||
<p className="text-xs text-indigo-600 dark:text-indigo-400">
|
||||
This tool is provided by an external MCP server, enabling dynamic integration with specialized services and data sources.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ScrollArea>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
|
@ -1,354 +0,0 @@
|
|||
import { Search, Code, FileText, Network, Database, Server } from 'lucide-react';
|
||||
|
||||
export interface MCPResult {
|
||||
success: boolean;
|
||||
data: any;
|
||||
isError?: boolean;
|
||||
content?: string;
|
||||
mcp_metadata?: {
|
||||
server_name: string;
|
||||
tool_name: string;
|
||||
full_tool_name: string;
|
||||
arguments_count: number;
|
||||
is_mcp_tool: boolean;
|
||||
};
|
||||
error_type?: string;
|
||||
raw_result?: any;
|
||||
}
|
||||
|
||||
export interface ParsedMCPTool {
|
||||
serverName: string;
|
||||
toolName: string;
|
||||
fullToolName: string;
|
||||
displayName: string;
|
||||
arguments: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced MCP tool name formatter that handles various naming conventions
|
||||
*/
|
||||
export function formatMCPToolDisplayName(serverName: string, toolName: string): string {
|
||||
// Handle server name formatting
|
||||
const formattedServerName = formatServerName(serverName);
|
||||
|
||||
// Handle tool name formatting
|
||||
const formattedToolName = formatToolName(toolName);
|
||||
|
||||
return `${formattedServerName}: ${formattedToolName}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format server names with special handling for known services
|
||||
*/
|
||||
function formatServerName(serverName: string): string {
|
||||
const serverMappings: Record<string, string> = {
|
||||
'exa': 'Exa Search',
|
||||
'github': 'GitHub',
|
||||
'notion': 'Notion',
|
||||
'slack': 'Slack',
|
||||
'filesystem': 'File System',
|
||||
'memory': 'Memory',
|
||||
'anthropic': 'Anthropic',
|
||||
'openai': 'OpenAI',
|
||||
'composio': 'Composio',
|
||||
'langchain': 'LangChain',
|
||||
'llamaindex': 'LlamaIndex'
|
||||
};
|
||||
|
||||
const lowerName = serverName.toLowerCase();
|
||||
return serverMappings[lowerName] || capitalizeWords(serverName);
|
||||
}
|
||||
|
||||
function formatToolName(toolName: string): string {
|
||||
if (toolName.includes('-')) {
|
||||
return toolName
|
||||
.split('-')
|
||||
.map(word => capitalizeWord(word))
|
||||
.join(' ');
|
||||
}
|
||||
if (toolName.includes('_')) {
|
||||
return toolName
|
||||
.split('_')
|
||||
.map(word => capitalizeWord(word))
|
||||
.join(' ');
|
||||
}
|
||||
if (/[a-z][A-Z]/.test(toolName)) {
|
||||
return toolName
|
||||
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
||||
.split(' ')
|
||||
.map(word => capitalizeWord(word))
|
||||
.join(' ');
|
||||
}
|
||||
return capitalizeWord(toolName);
|
||||
}
|
||||
|
||||
function capitalizeWord(word: string): string {
|
||||
if (!word) return '';
|
||||
const upperCaseWords = new Set([
|
||||
'api', 'url', 'http', 'https', 'json', 'xml', 'csv', 'pdf', 'id', 'uuid',
|
||||
'oauth', 'jwt', 'sql', 'html', 'css', 'js', 'ts', 'ai', 'ml', 'ui', 'ux'
|
||||
]);
|
||||
const lowerWord = word.toLowerCase();
|
||||
if (upperCaseWords.has(lowerWord)) {
|
||||
return word.toUpperCase();
|
||||
}
|
||||
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
||||
}
|
||||
|
||||
function capitalizeWords(text: string): string {
|
||||
return text
|
||||
.split(/[\s_-]+/)
|
||||
.map(word => capitalizeWord(word))
|
||||
.join(' ');
|
||||
}
|
||||
|
||||
function extractFromNewFormat(toolContent: string | object): MCPResult | null {
|
||||
try {
|
||||
let parsed: any;
|
||||
|
||||
if (typeof toolContent === 'string') {
|
||||
parsed = JSON.parse(toolContent);
|
||||
} else {
|
||||
parsed = toolContent;
|
||||
}
|
||||
|
||||
if (parsed && typeof parsed === 'object' && 'tool_execution' in parsed) {
|
||||
const toolExecution = parsed.tool_execution;
|
||||
|
||||
if (toolExecution && typeof toolExecution === 'object' && 'result' in toolExecution) {
|
||||
const result = toolExecution.result;
|
||||
const success = result.success === true;
|
||||
let output = result.output;
|
||||
|
||||
if (typeof output === 'string') {
|
||||
try {
|
||||
output = JSON.parse(output);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
const args = toolExecution.arguments || {};
|
||||
const toolName = args.tool_name || 'unknown_mcp_tool';
|
||||
const toolArgs = args.arguments || {};
|
||||
|
||||
const parts = toolName.split('_');
|
||||
const serverName = parts.length > 1 ? parts[1] : 'unknown';
|
||||
const actualToolName = parts.length > 2 ? parts.slice(2).join('_') : toolName;
|
||||
|
||||
return {
|
||||
success,
|
||||
data: output,
|
||||
isError: !success,
|
||||
content: output,
|
||||
mcp_metadata: {
|
||||
server_name: serverName,
|
||||
tool_name: actualToolName,
|
||||
full_tool_name: toolName,
|
||||
arguments_count: Object.keys(toolArgs).length,
|
||||
is_mcp_tool: true
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function extractFromLegacyFormat(toolContent: string): MCPResult | null {
|
||||
try {
|
||||
const toolResultMatch = toolContent.match(/ToolResult\(success=(\w+),\s*output='([\s\S]*)'\)/);
|
||||
if (toolResultMatch) {
|
||||
const isSuccess = toolResultMatch[1] === 'True';
|
||||
let output = toolResultMatch[2];
|
||||
|
||||
output = output.replace(/\\n/g, '\n').replace(/\\'/g, "'").replace(/\\"/g, '"');
|
||||
|
||||
return {
|
||||
success: isSuccess,
|
||||
data: output,
|
||||
isError: !isSuccess,
|
||||
content: output
|
||||
};
|
||||
}
|
||||
|
||||
const xmlMatch = toolContent.match(/<tool_result>\s*<call-mcp-tool>\s*([\s\S]*?)\s*<\/call-mcp-tool>\s*<\/tool_result>/);
|
||||
if (xmlMatch && xmlMatch[1]) {
|
||||
const innerContent = xmlMatch[1].trim();
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(innerContent);
|
||||
|
||||
if (parsed.mcp_metadata) {
|
||||
let actualContent = parsed.content;
|
||||
if (typeof actualContent === 'string') {
|
||||
try {
|
||||
actualContent = JSON.parse(actualContent);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: !parsed.isError,
|
||||
data: actualContent,
|
||||
isError: parsed.isError,
|
||||
content: actualContent,
|
||||
mcp_metadata: parsed.mcp_metadata,
|
||||
error_type: parsed.error_type,
|
||||
raw_result: parsed.raw_result
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: !parsed.isError,
|
||||
data: parsed.content || parsed,
|
||||
isError: parsed.isError,
|
||||
content: parsed.content
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
success: true,
|
||||
data: innerContent,
|
||||
content: innerContent
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const parsed = JSON.parse(toolContent);
|
||||
|
||||
if (parsed.mcp_metadata) {
|
||||
let actualContent = parsed.content;
|
||||
if (typeof actualContent === 'string') {
|
||||
try {
|
||||
actualContent = JSON.parse(actualContent);
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: !parsed.isError,
|
||||
data: actualContent,
|
||||
isError: parsed.isError,
|
||||
content: actualContent,
|
||||
mcp_metadata: parsed.mcp_metadata,
|
||||
error_type: parsed.error_type,
|
||||
raw_result: parsed.raw_result
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
success: !parsed.isError,
|
||||
data: parsed.content || parsed,
|
||||
isError: parsed.isError,
|
||||
content: parsed.content
|
||||
};
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function parseMCPResult(toolContent: string | object | undefined): MCPResult {
|
||||
if (!toolContent) {
|
||||
return {
|
||||
success: true,
|
||||
data: '',
|
||||
content: ''
|
||||
};
|
||||
}
|
||||
|
||||
const newFormatResult = extractFromNewFormat(toolContent);
|
||||
if (newFormatResult) {
|
||||
return newFormatResult;
|
||||
}
|
||||
|
||||
if (typeof toolContent === 'string') {
|
||||
const legacyResult = extractFromLegacyFormat(toolContent);
|
||||
if (legacyResult) {
|
||||
return legacyResult;
|
||||
}
|
||||
}
|
||||
|
||||
const content = typeof toolContent === 'string' ? toolContent : JSON.stringify(toolContent);
|
||||
return {
|
||||
success: true,
|
||||
data: content,
|
||||
content: content
|
||||
};
|
||||
}
|
||||
|
||||
export function parseMCPToolCall(assistantContent: string): ParsedMCPTool {
|
||||
try {
|
||||
const toolNameMatch = assistantContent.match(/tool_name="([^"]+)"/);
|
||||
const fullToolName = toolNameMatch ? toolNameMatch[1] : 'unknown_mcp_tool';
|
||||
|
||||
const contentMatch = assistantContent.match(/<call-mcp-tool[^>]*>([\s\S]*?)<\/call-mcp-tool>/);
|
||||
let args = {};
|
||||
|
||||
if (contentMatch && contentMatch[1]) {
|
||||
try {
|
||||
args = JSON.parse(contentMatch[1].trim());
|
||||
} catch (e) {
|
||||
args = { raw: contentMatch[1].trim() };
|
||||
}
|
||||
}
|
||||
|
||||
const parts = fullToolName.split('_');
|
||||
const serverName = parts.length > 1 ? parts[1] : 'unknown';
|
||||
const toolName = parts.length > 2 ? parts.slice(2).join('_') : fullToolName;
|
||||
|
||||
// Use the enhanced formatting function
|
||||
const displayName = formatMCPToolDisplayName(serverName, toolName);
|
||||
|
||||
return {
|
||||
serverName,
|
||||
toolName,
|
||||
fullToolName,
|
||||
displayName,
|
||||
arguments: args
|
||||
};
|
||||
} catch (e) {
|
||||
return {
|
||||
serverName: 'unknown',
|
||||
toolName: 'unknown',
|
||||
fullToolName: 'unknown_mcp_tool',
|
||||
displayName: 'MCP Tool',
|
||||
arguments: {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getMCPServerIcon(serverName: string) {
|
||||
switch (serverName.toLowerCase()) {
|
||||
case 'exa':
|
||||
return Search;
|
||||
case 'github':
|
||||
return Code;
|
||||
case 'notion':
|
||||
return FileText;
|
||||
case 'slack':
|
||||
return Network;
|
||||
case 'filesystem':
|
||||
return Database;
|
||||
default:
|
||||
return Server;
|
||||
}
|
||||
}
|
||||
|
||||
export function getMCPServerColor(serverName: string) {
|
||||
switch (serverName.toLowerCase()) {
|
||||
case 'exa':
|
||||
return 'from-blue-500/20 to-blue-600/10 border-blue-500/20';
|
||||
case 'github':
|
||||
return 'from-purple-500/20 to-purple-600/10 border-purple-500/20';
|
||||
case 'notion':
|
||||
return 'from-gray-500/20 to-gray-600/10 border-gray-500/20';
|
||||
case 'slack':
|
||||
return 'from-green-500/20 to-green-600/10 border-green-500/20';
|
||||
case 'filesystem':
|
||||
return 'from-orange-500/20 to-orange-600/10 border-orange-500/20';
|
||||
default:
|
||||
return 'from-indigo-500/20 to-indigo-600/10 border-indigo-500/20';
|
||||
}
|
||||
}
|
|
@ -81,8 +81,6 @@ const defaultRegistry: ToolViewRegistryType = {
|
|||
|
||||
'see-image': SeeImageToolView,
|
||||
|
||||
'call-mcp-tool': GenericToolView,
|
||||
|
||||
'ask': AskToolView,
|
||||
'complete': CompleteToolView,
|
||||
|
||||
|
|
|
@ -153,11 +153,6 @@ export const getToolIcon = (toolName: string): ElementType => {
|
|||
case 'complete':
|
||||
return CheckCircle2;
|
||||
|
||||
// MCP tools
|
||||
case 'call-mcp-tool':
|
||||
return PlugIcon;
|
||||
|
||||
// Default case
|
||||
default:
|
||||
if (toolName?.startsWith('mcp_')) {
|
||||
const parts = toolName.split('_');
|
||||
|
@ -335,7 +330,6 @@ const TOOL_DISPLAY_NAMES = new Map([
|
|||
['web-search', 'Searching Web'],
|
||||
['see-image', 'Viewing Image'],
|
||||
|
||||
['call-mcp-tool', 'External Tool'],
|
||||
|
||||
['update-agent', 'Updating Agent'],
|
||||
['get-current-agent-config', 'Getting Agent Config'],
|
||||
|
|
|
@ -33,10 +33,6 @@ export class VersionService implements IVersionService {
|
|||
agentId: string,
|
||||
request: CreateVersionRequest
|
||||
): Promise<AgentVersion> {
|
||||
if (!request.system_prompt?.trim()) {
|
||||
throw new Error('System prompt cannot be empty');
|
||||
}
|
||||
|
||||
const newVersion = await this.repository.createVersion(agentId, request);
|
||||
console.log(`Created version ${newVersion.versionName} for agent ${agentId}`);
|
||||
return newVersion;
|
||||
|
|
Loading…
Reference in New Issue