Successfully Connected!
Your {app.name} integration is ready.
diff --git a/frontend/src/components/agents/composio/composio-registry.tsx b/frontend/src/components/agents/composio/composio-registry.tsx
index a5c07134..0184eab1 100644
--- a/frontend/src/components/agents/composio/composio-registry.tsx
+++ b/frontend/src/components/agents/composio/composio-registry.tsx
@@ -4,40 +4,41 @@ import { Badge } from '@/components/ui/badge';
import { Skeleton } from '@/components/ui/skeleton';
import { Button } from '@/components/ui/button';
import { ScrollArea } from '@/components/ui/scroll-area';
-import { Search, Zap, X } from 'lucide-react';
+import { Search, Zap, X, Settings, ChevronDown, ChevronUp } from 'lucide-react';
import { useComposioToolkits, useComposioCategories } from '@/hooks/react-query/composio/use-composio';
import { useComposioProfiles } from '@/hooks/react-query/composio/use-composio-profiles';
-import { useAgent } from '@/hooks/react-query/agents/use-agents';
+import { useAgent, useUpdateAgent } from '@/hooks/react-query/agents/use-agents';
import { ComposioConnector } from './composio-connector';
+import { ComposioToolsManager } from './composio-tools-manager';
import type { ComposioToolkit, ComposioProfile } from '@/hooks/react-query/composio/utils';
import { toast } from 'sonner';
import { cn } from '@/lib/utils';
import { useQueryClient } from '@tanstack/react-query';
import { AgentSelector } from '../../thread/chat-input/agent-selector';
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
+import { Switch } from '@/components/ui/switch';
const CATEGORY_EMOJIS: Record = {
'popular': '🔥',
'productivity': '📊',
- 'ai': '🤖',
'crm': '👥',
'marketing': '📢',
- 'email': '📧',
'analytics': '📈',
- 'automation': '⚡',
'communication': '💬',
'project-management': '📋',
- 'e-commerce': '🛒',
- 'social-media': '📱',
- 'payments': '💳',
- 'finance': '💰',
- 'developer-tools': '🛠️',
- 'api': '🔌',
- 'notifications': '🔔',
'scheduling': '📅',
- 'data-analytics': '📊',
- 'customer-support': '🎧'
};
+interface ConnectedApp {
+ toolkit: ComposioToolkit;
+ profile: ComposioProfile;
+ mcpConfig: {
+ name: string;
+ type: string;
+ config: Record;
+ enabledTools: string[];
+ };
+}
interface ComposioRegistryProps {
onToolsSelected?: (profileId: string, selectedTools: string[], appName: string, appSlug: string) => void;
@@ -49,6 +50,52 @@ interface ComposioRegistryProps {
onAgentChange?: (agentId: string | undefined) => void;
}
+// Helper function to get agent-specific connected apps
+const getAgentConnectedApps = (
+ agent: any,
+ profiles: ComposioProfile[],
+ toolkits: ComposioToolkit[]
+): ConnectedApp[] => {
+ if (!agent?.custom_mcps || !profiles?.length || !toolkits?.length) return [];
+
+ const connectedApps: ConnectedApp[] = [];
+
+ agent.custom_mcps.forEach((mcpConfig: any) => {
+ // Check if this is a Composio MCP by looking for profile_id in config
+ if (mcpConfig.config?.profile_id) {
+ const profile = profiles.find(p => p.profile_id === mcpConfig.config.profile_id);
+ const toolkit = toolkits.find(t => t.slug === profile?.toolkit_slug);
+
+ if (profile && toolkit) {
+ connectedApps.push({
+ toolkit,
+ profile,
+ mcpConfig
+ });
+ }
+ }
+ });
+
+ return connectedApps;
+};
+
+// Helper function to check if an app is connected to the agent
+const isAppConnectedToAgent = (
+ agent: any,
+ appSlug: string,
+ profiles: ComposioProfile[]
+): boolean => {
+ if (!agent?.custom_mcps) return false;
+
+ return agent.custom_mcps.some((mcpConfig: any) => {
+ if (mcpConfig.config?.profile_id) {
+ const profile = profiles.find(p => p.profile_id === mcpConfig.config.profile_id);
+ return profile?.toolkit_slug === appSlug;
+ }
+ return false;
+ });
+};
+
const AppCardSkeleton = () => (
@@ -69,16 +116,100 @@ const AppCardSkeleton = () => (
);
-const AppCard = ({ app, profiles, onConnect, onConfigure }: {
+const ConnectedAppSkeleton = () => (
+
+);
+
+const ConnectedAppCard = ({
+ connectedApp,
+ onToggleTools,
+ onConfigure,
+ onManageTools,
+ isUpdating
+}: {
+ connectedApp: ConnectedApp;
+ onToggleTools: (profileId: string, enabled: boolean) => void;
+ onConfigure: (app: ComposioToolkit, profile: ComposioProfile) => void;
+ onManageTools: (connectedApp: ConnectedApp) => void;
+ isUpdating: boolean;
+}) => {
+ const { toolkit, profile, mcpConfig } = connectedApp;
+ const hasEnabledTools = mcpConfig.enabledTools && mcpConfig.enabledTools.length > 0;
+
+ return (
+
+
+ {toolkit.logo ? (
+

+ ) : (
+
+ {toolkit.name.charAt(0)}
+
+ )}
+
+
{toolkit.name}
+
+ Connected as "{profile.profile_name}"
+
+
+
+
+
+
+
+
+
+
+
+ {hasEnabledTools ? `${mcpConfig.enabledTools.length} tools enabled` : 'Connected (no tools)'}
+
+
+
+
+ );
+};
+
+const AppCard = ({ app, profiles, onConnect, onConfigure, isConnectedToAgent, currentAgentId, mode }: {
app: ComposioToolkit;
profiles: ComposioProfile[];
onConnect: () => void;
onConfigure: (profile: ComposioProfile) => void;
+ isConnectedToAgent: boolean;
+ currentAgentId?: string;
+ mode?: 'full' | 'profile-only';
}) => {
const connectedProfiles = profiles.filter(p => p.is_connected);
+ const canConnect = mode === 'profile-only' ? true : (!isConnectedToAgent && currentAgentId);
return (
-
0 ? () => onConfigure(connectedProfiles[0]) : onConnect} className="group border bg-card hover:bg-muted rounded-2xl p-4 transition-all duration-200">
+
0 ? () => onConfigure(connectedProfiles[0]) : onConnect) : undefined}
+ className={cn(
+ "group border bg-card rounded-2xl p-4 transition-all duration-200",
+ canConnect ? "hover:bg-muted cursor-pointer" : "opacity-60 cursor-not-allowed"
+ )}
+ >
{app.logo ? (

@@ -111,11 +242,32 @@ const AppCard = ({ app, profiles, onConnect, onConfigure }: {
)}
- {connectedProfiles.length > 0 && (
+ {mode === 'profile-only' ? (
+
+
+
+ {connectedProfiles.length > 0 ? `${connectedProfiles.length} existing profile${connectedProfiles.length !== 1 ? 's' : ''}` : 'Click to connect'}
+
+
+ ) : isConnectedToAgent ? (
+
+
+
+ Connected to this agent
+
+
+ ) : connectedProfiles.length > 0 ? (
- Connected ({connectedProfiles.length})
+ Profile available ({connectedProfiles.length})
+
+
+ ) : (
+
)}
@@ -137,16 +289,20 @@ export const ComposioRegistry: React.FC
= ({
const [selectedCategory, setSelectedCategory] = useState('');
const [selectedApp, setSelectedApp] = useState(null);
const [showConnector, setShowConnector] = useState(false);
+ const [showConnectedApps, setShowConnectedApps] = useState(true);
+ const [showToolsManager, setShowToolsManager] = useState(false);
+ const [selectedConnectedApp, setSelectedConnectedApp] = useState(null);
const [internalSelectedAgentId, setInternalSelectedAgentId] = useState(selectedAgentId);
const queryClient = useQueryClient();
const { data: categoriesData, isLoading: isLoadingCategories } = useComposioCategories();
const { data: toolkits, isLoading } = useComposioToolkits(search, selectedCategory);
- const { data: profiles } = useComposioProfiles();
+ const { data: profiles, isLoading: isLoadingProfiles } = useComposioProfiles();
const currentAgentId = selectedAgentId ?? internalSelectedAgentId;
- const { data: agent } = useAgent(currentAgentId || '');
+ const { data: agent, isLoading: isLoadingAgent } = useAgent(currentAgentId || '');
+ const { mutate: updateAgent, isPending: isUpdatingAgent } = useUpdateAgent();
const handleAgentSelect = (agentId: string | undefined) => {
if (onAgentChange) {
@@ -169,13 +325,20 @@ export const ComposioRegistry: React.FC = ({
return grouped;
}, [profiles]);
+ const connectedApps = useMemo(() => {
+ if (!currentAgentId || !agent) return [];
+ return getAgentConnectedApps(agent, profiles || [], toolkits?.toolkits || []);
+ }, [agent, profiles, toolkits, currentAgentId]);
+
+ const isLoadingConnectedApps = currentAgentId && (isLoadingAgent || isLoadingProfiles || isLoading);
+
const filteredToolkits = useMemo(() => {
if (!toolkits?.toolkits) return [];
return toolkits.toolkits;
}, [toolkits]);
const handleConnect = (app: ComposioToolkit) => {
- if (!currentAgentId && showAgentSelector) {
+ if (mode !== 'profile-only' && !currentAgentId && showAgentSelector) {
toast.error('Please select an agent first');
return;
}
@@ -184,7 +347,7 @@ export const ComposioRegistry: React.FC = ({
};
const handleConfigure = (app: ComposioToolkit, profile: ComposioProfile) => {
- if (!currentAgentId) {
+ if (mode !== 'profile-only' && !currentAgentId) {
toast.error('Please select an agent first');
return;
}
@@ -192,6 +355,37 @@ export const ComposioRegistry: React.FC = ({
setShowConnector(true);
};
+ const handleToggleTools = (profileId: string, enabled: boolean) => {
+ if (!currentAgentId || !agent) return;
+
+ const updatedCustomMcps = agent.custom_mcps?.map((mcpConfig: any) => {
+ if (mcpConfig.config?.profile_id === profileId) {
+ return {
+ ...mcpConfig,
+ enabledTools: enabled ? mcpConfig.enabledTools || [] : []
+ };
+ }
+ return mcpConfig;
+ }) || [];
+
+ updateAgent({
+ agentId: currentAgentId,
+ custom_mcps: updatedCustomMcps
+ }, {
+ onSuccess: () => {
+ toast.success(enabled ? 'Tools enabled' : 'Tools disabled');
+ },
+ onError: (error: any) => {
+ toast.error(error.message || 'Failed to update tools');
+ }
+ });
+ };
+
+ const handleManageTools = (connectedApp: ConnectedApp) => {
+ setSelectedConnectedApp(connectedApp);
+ setShowToolsManager(true);
+ };
+
const handleConnectionComplete = (profileId: string, appName: string, appSlug: string) => {
setShowConnector(false);
queryClient.invalidateQueries({ queryKey: ['composio', 'profiles'] });
@@ -263,9 +457,14 @@ export const ComposioRegistry: React.FC = ({
-
App Integrations
+
+ {mode === 'profile-only' ? 'Connect New App' : 'App Integrations'}
+
- Connect your favorite apps powered by Composio
+ {mode === 'profile-only'
+ ? 'Create a connection profile for your favorite apps'
+ : `Connect your favorite apps with ${currentAgentId ? 'this agent' : 'your agent'}`
+ }
@@ -276,11 +475,6 @@ export const ComposioRegistry: React.FC = ({
isSunaAgent={agent?.metadata?.is_suna_default}
/>
)}
- {onClose && (
-
- )}
@@ -313,42 +507,103 @@ export const ComposioRegistry: React.FC
= ({
-
- {isLoading ? (
-
- {Array.from({ length: 12 }).map((_, i) => (
-
- ))}
-
- ) : filteredToolkits.length === 0 ? (
-
-
-
-
-
No apps found
-
- {search ? `No apps match "${search}"` : 'No apps available in this category'}
-
-
- ) : (
-
- {filteredToolkits.map((app) => (
-
handleConnect(app)}
- onConfigure={(profile) => handleConfigure(app, profile)}
- />
- ))}
-
+
+ {currentAgentId && (
+
+
+
+
+
Connected to this agent
+ {isLoadingConnectedApps ? (
+
+ ) : connectedApps.length > 0 && (
+
+ {connectedApps.length}
+
+ )}
+
+ {showConnectedApps ? (
+
+ ) : (
+
+ )}
+
+
+
+ {isLoadingConnectedApps ? (
+
+ {Array.from({ length: 3 }).map((_, i) => (
+
+ ))}
+
+ ) : connectedApps.length === 0 ? (
+
+
+
+
+
No connected apps
+
Connect apps below to manage tools for this agent.
+
+ ) : (
+
+ {connectedApps.map((connectedApp) => (
+
+ ))}
+
+ )}
+
+
)}
+
+
+ {currentAgentId ? 'Available Apps' : 'Browse Apps'}
+
+
+ {isLoading ? (
+
+ {Array.from({ length: 12 }).map((_, i) => (
+
+ ))}
+
+ ) : filteredToolkits.length === 0 ? (
+
+
+
+
+
No apps found
+
+ {search ? `No apps match "${search}"` : 'No apps available in this category'}
+
+
+ ) : (
+
+ {filteredToolkits.map((app) => (
+
handleConnect(app)}
+ onConfigure={(profile) => handleConfigure(app, profile)}
+ isConnectedToAgent={isAppConnectedToAgent(agent, app.slug, profiles || [])}
+ currentAgentId={currentAgentId}
+ mode={mode}
+ />
+ ))}
+
+ )}
+
-
{selectedApp && (
= ({
open={showConnector}
onOpenChange={setShowConnector}
onComplete={handleConnectionComplete}
+ mode={mode}
+ />
+ )}
+
+ {selectedConnectedApp && currentAgentId && (
+ {
+ queryClient.invalidateQueries({ queryKey: ['agents', currentAgentId] });
+ }}
/>
)}
diff --git a/frontend/src/components/agents/composio/composio-tools-manager.tsx b/frontend/src/components/agents/composio/composio-tools-manager.tsx
index 34370c8e..14150db6 100644
--- a/frontend/src/components/agents/composio/composio-tools-manager.tsx
+++ b/frontend/src/components/agents/composio/composio-tools-manager.tsx
@@ -11,6 +11,7 @@ import { Separator } from '@/components/ui/separator';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Search, Save, AlertCircle, Zap, CheckCircle2, Settings2, Loader2 } from 'lucide-react';
import { useComposioProfiles } from '@/hooks/react-query/composio/use-composio-profiles';
+import { useComposioToolkitIcon } from '@/hooks/react-query/composio/use-composio';
import { backendApi } from '@/lib/api-client';
import { toast } from 'sonner';
import { useQueryClient } from '@tanstack/react-query';
@@ -129,6 +130,9 @@ export const ComposioToolsManager: React.FC
= ({
const { data: profiles } = useComposioProfiles();
const currentProfile = profileInfo || profiles?.find(p => p.profile_id === profileId);
+ const { data: iconData } = useComposioToolkitIcon(currentProfile?.toolkit_slug || '', {
+ enabled: !!currentProfile?.toolkit_slug
+ });
const filteredTools = useMemo(() => {
if (!searchTerm) return availableTools;
@@ -241,11 +245,11 @@ export const ComposioToolsManager: React.FC = ({
- {appLogo ? (
+ {iconData?.icon_url || appLogo ? (

) : (
diff --git a/frontend/src/components/agents/custom-agents-page/marketplace-tab.tsx b/frontend/src/components/agents/custom-agents-page/marketplace-tab.tsx
index 1231604a..fc14e74f 100644
--- a/frontend/src/components/agents/custom-agents-page/marketplace-tab.tsx
+++ b/frontend/src/components/agents/custom-agents-page/marketplace-tab.tsx
@@ -1,11 +1,12 @@
'use client';
-import React from 'react';
+import React, { useState } from 'react';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Skeleton } from '@/components/ui/skeleton';
import { SearchBar } from './search-bar';
import { MarketplaceSectionHeader } from './marketplace-section-header';
import { AgentCard } from './agent-card';
+import { MarketplaceAgentPreviewDialog } from '@/components/agents/marketplace-agent-preview-dialog';
import type { MarketplaceTemplate } from '@/components/agents/installation/types';
interface MarketplaceTabProps {
@@ -41,6 +42,25 @@ export const MarketplaceTab = ({
getItemStyling,
currentUserId
}: MarketplaceTabProps) => {
+ const [previewAgent, setPreviewAgent] = useState
(null);
+ const [previewDialogOpen, setPreviewDialogOpen] = useState(false);
+
+ const handleAgentClick = (item: MarketplaceTemplate) => {
+ setPreviewAgent(item);
+ setPreviewDialogOpen(true);
+ };
+
+ const handlePreviewClose = () => {
+ setPreviewDialogOpen(false);
+ setPreviewAgent(null);
+ };
+
+ const handleInstallFromPreview = (agent: MarketplaceTemplate) => {
+ onInstallClick(agent);
+ setPreviewDialogOpen(false);
+ setPreviewAgent(null);
+ };
+
return (
@@ -107,7 +127,7 @@ export const MarketplaceTab = ({
isActioning={installingItemId === item.id}
onPrimaryAction={onInstallClick}
onDeleteAction={onDeleteTemplate}
- onClick={() => onInstallClick(item)}
+ onClick={() => handleAgentClick(item)}
currentUserId={currentUserId}
/>
))}
@@ -126,7 +146,7 @@ export const MarketplaceTab = ({
isActioning={installingItemId === item.id}
onPrimaryAction={onInstallClick}
onDeleteAction={onDeleteTemplate}
- onClick={() => onInstallClick(item)}
+ onClick={() => handleAgentClick(item)}
currentUserId={currentUserId}
/>
))}
@@ -145,7 +165,7 @@ export const MarketplaceTab = ({
isActioning={installingItemId === item.id}
onPrimaryAction={onInstallClick}
onDeleteAction={onDeleteTemplate}
- onClick={() => onInstallClick(item)}
+ onClick={() => handleAgentClick(item)}
currentUserId={currentUserId}
/>
))}
@@ -154,6 +174,14 @@ export const MarketplaceTab = ({
)}
+
+
);
};
\ No newline at end of file
diff --git a/frontend/src/components/agents/installation/types.ts b/frontend/src/components/agents/installation/types.ts
index 43e75fc9..f67b4655 100644
--- a/frontend/src/components/agents/installation/types.ts
+++ b/frontend/src/components/agents/installation/types.ts
@@ -12,6 +12,7 @@ export interface MarketplaceTemplate {
avatar_color?: string;
template_id: string;
is_kortix_team?: boolean;
+ agentpress_tools?: Record
;
mcp_requirements?: Array<{
qualified_name: string;
display_name: string;
diff --git a/frontend/src/components/agents/marketplace-agent-preview-dialog.tsx b/frontend/src/components/agents/marketplace-agent-preview-dialog.tsx
new file mode 100644
index 00000000..4aac6e75
--- /dev/null
+++ b/frontend/src/components/agents/marketplace-agent-preview-dialog.tsx
@@ -0,0 +1,282 @@
+'use client';
+
+import React from 'react';
+import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
+import { Badge } from '@/components/ui/badge';
+import { Button } from '@/components/ui/button';
+import { Card, CardContent } from '@/components/ui/card';
+import { Separator } from '@/components/ui/separator';
+import { Bot, Download, Wrench, Plug, Tag, User, Calendar, Loader2 } from 'lucide-react';
+import type { MarketplaceTemplate } from '@/components/agents/installation/types';
+import { AGENTPRESS_TOOL_DEFINITIONS } from '@/components/agents/tools';
+import { useComposioToolkitIcon } from '@/hooks/react-query/composio/use-composio';
+import { usePipedreamAppIcon } from '@/hooks/react-query/pipedream/use-pipedream';
+
+interface MarketplaceAgentPreviewDialogProps {
+ agent: MarketplaceTemplate | null;
+ isOpen: boolean;
+ onClose: () => void;
+ onInstall: (agent: MarketplaceTemplate) => void;
+ isInstalling?: boolean;
+}
+
+const extractAppInfo = (qualifiedName: string, customType?: string) => {
+ if (customType === 'pipedream') {
+ const qualifiedMatch = qualifiedName.match(/^pipedream_([^_]+)_/);
+ if (qualifiedMatch) {
+ return { type: 'pipedream', slug: qualifiedMatch[1] };
+ }
+ }
+
+ if (customType === 'composio') {
+ if (qualifiedName.startsWith('composio.')) {
+ const extractedSlug = qualifiedName.substring(9);
+ if (extractedSlug) {
+ return { type: 'composio', slug: extractedSlug };
+ }
+ }
+ }
+
+ return null;
+};
+
+const IntegrationLogo: React.FC<{
+ qualifiedName: string;
+ displayName: string;
+ customType?: string;
+}> = ({ qualifiedName, displayName, customType }) => {
+ const appInfo = extractAppInfo(qualifiedName, customType);
+
+ const { data: pipedreamIconData } = usePipedreamAppIcon(
+ appInfo?.type === 'pipedream' ? appInfo.slug : '',
+ { enabled: appInfo?.type === 'pipedream' }
+ );
+
+ const { data: composioIconData } = useComposioToolkitIcon(
+ appInfo?.type === 'composio' ? appInfo.slug : '',
+ { enabled: appInfo?.type === 'composio' }
+ );
+
+ let logoUrl: string | undefined;
+ if (appInfo?.type === 'pipedream') {
+ logoUrl = pipedreamIconData?.icon_url;
+ } else if (appInfo?.type === 'composio') {
+ logoUrl = composioIconData?.icon_url;
+ }
+
+ const firstLetter = displayName.charAt(0).toUpperCase();
+
+ return (
+
+ {logoUrl ? (
+

{
+ const target = e.target as HTMLImageElement;
+ target.style.display = 'none';
+ target.nextElementSibling?.classList.remove('hidden');
+ }}
+ />
+ ) : null}
+
+ {firstLetter}
+
+
+ );
+};
+
+export const MarketplaceAgentPreviewDialog: React.FC = ({
+ agent,
+ isOpen,
+ onClose,
+ onInstall,
+ isInstalling = false
+}) => {
+ if (!agent) return null;
+
+ const { avatar, avatar_color } = agent;
+ const isSunaAgent = agent.is_kortix_team || false;
+
+ const tools = agent.mcp_requirements || [];
+ const integrations = tools.filter(tool => !tool.custom_type || tool.custom_type !== 'sse');
+ const customTools = tools.filter(tool => tool.custom_type === 'sse');
+
+ const agentpressTools = Object.entries(agent.agentpress_tools || {})
+ .filter(([_, enabled]) => enabled)
+ .map(([toolName]) => toolName);
+
+ const handleInstall = () => {
+ onInstall(agent);
+ };
+
+ const formatDate = (dateString: string) => {
+ return new Date(dateString).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric'
+ });
+ };
+
+ const getAppDisplayName = (qualifiedName: string) => {
+ if (qualifiedName.includes('_')) {
+ const parts = qualifiedName.split('_');
+ return parts[parts.length - 1].replace(/\b\w/g, l => l.toUpperCase());
+ }
+ return qualifiedName.replace(/\b\w/g, l => l.toUpperCase());
+ };
+
+ return (
+
+ );
+};
\ No newline at end of file
diff --git a/frontend/src/components/agents/mcp/mcp-configuration-new.tsx b/frontend/src/components/agents/mcp/mcp-configuration-new.tsx
index ddde3c19..829a93e6 100644
--- a/frontend/src/components/agents/mcp/mcp-configuration-new.tsx
+++ b/frontend/src/components/agents/mcp/mcp-configuration-new.tsx
@@ -90,8 +90,6 @@ export const MCPConfigurationNew: React.FC = ({
const handleToolsSelected = (profileId: string, selectedTools: string[], appName: string, appSlug: string) => {
console.log('Tools selected:', { profileId, selectedTools, appName, appSlug });
setShowRegistryDialog(false);
- // ComposioRegistry handles all the actual configuration internally
- // We need to refresh the agent data to show updated configuration
queryClient.invalidateQueries({ queryKey: ['agents'] });
queryClient.invalidateQueries({ queryKey: ['agent', selectedAgentId] });
queryClient.invalidateQueries({ queryKey: ['composio', 'profiles'] });
diff --git a/frontend/src/components/workflows/workflow-builder.tsx b/frontend/src/components/workflows/workflow-builder.tsx
index b779c659..3cdc5a6c 100644
--- a/frontend/src/components/workflows/workflow-builder.tsx
+++ b/frontend/src/components/workflows/workflow-builder.tsx
@@ -152,10 +152,9 @@ export function WorkflowBuilder({
{editableSteps.length === 0 ? (
- // Empty state
-
+
@@ -165,13 +164,12 @@ export function WorkflowBuilder({
Add steps to create a workflow that guides your agent through tasks.
) : (
- // Steps list - uses the children array from root node
{
if (stepType.id === 'credentials_profile') {
- setShowPipedreamRegistry(true);
+ setShowComposioRegistry(true);
} else if (stepType.id === 'mcp_configuration') {
setShowCustomMCPDialog(true);
} else {
@@ -113,88 +117,25 @@ export function WorkflowSidePanel({
}
};
- const handlePipedreamToolsSelected = async (profileId: string, selectedTools: string[], appName: string, appSlug: string) => {
- try {
- const pipedreamMCP = {
- name: appName,
- qualifiedName: `pipedream_${appSlug}_${profileId}`,
- config: {
- url: 'https://remote.mcp.pipedream.net',
- headers: {
- 'x-pd-app-slug': appSlug,
- },
- profile_id: profileId
- },
- enabledTools: selectedTools,
- selectedProfileId: profileId
- };
-
- // Update agent with new MCP
- const existingCustomMCPs = agent?.custom_mcps || [];
- const nonPipedreamMCPs = existingCustomMCPs.filter((mcp: any) =>
- mcp.type !== 'pipedream' || mcp.config?.profile_id !== profileId
- );
-
- await updateAgentMutation.mutateAsync({
- agentId: agentId!,
- custom_mcps: [
- ...nonPipedreamMCPs,
- {
- name: appName,
- type: 'pipedream',
- config: pipedreamMCP.config,
- enabledTools: selectedTools
- } as any
- ]
- });
-
- // Create a step for the credentials profile
- const credentialsStep: ConditionalStep = {
- id: `credentials-${Date.now()}`,
- name: `${appName} Credentials`,
- description: `Credentials profile for ${appName} integration`,
- type: 'instruction',
- config: {
- step_type: 'credentials_profile',
- tool_name: selectedTools[0] || `${appName} Profile`,
- profile_id: profileId,
- app_name: appName,
- app_slug: appSlug
- },
- order: 0,
- enabled: true,
- children: []
- };
-
- onCreateStep({
- id: 'credentials_profile',
- name: credentialsStep.name,
- description: credentialsStep.description,
- icon: 'Key',
- category: 'configuration',
- config: credentialsStep.config
- });
-
- // Invalidate queries to refresh tools
- queryClient.invalidateQueries({ queryKey: ['agent', agentId] });
- queryClient.invalidateQueries({ queryKey: ['agent-tools', agentId] });
-
- // Trigger parent tools update
- if (onToolsUpdate) {
- onToolsUpdate();
- }
-
- setShowPipedreamRegistry(false);
- toast.success(`Added ${appName} credentials profile!`);
- } catch (error) {
- toast.error('Failed to add integration');
+ const handleComposioToolsSelected = (profileId: string, selectedTools: string[], appName: string, appSlug: string) => {
+ console.log('Tools selected:', { profileId, selectedTools, appName, appSlug });
+ setShowComposioRegistry(false);
+ // ComposioRegistry handles all the actual configuration internally
+ // We need to refresh the agent data to show updated configuration
+ queryClient.invalidateQueries({ queryKey: ['agents'] });
+ queryClient.invalidateQueries({ queryKey: ['agent', agentId] });
+ queryClient.invalidateQueries({ queryKey: ['composio', 'profiles'] });
+
+ if (onToolsUpdate) {
+ onToolsUpdate();
}
+
+ toast.success(`Connected ${appName} integration!`);
};
const handleCustomMCPSave = async (customConfig: any) => {
try {
const existingCustomMCPs = agent?.custom_mcps || [];
-
await updateAgentMutation.mutateAsync({
agentId: agentId!,
custom_mcps: [
@@ -208,7 +149,6 @@ export function WorkflowSidePanel({
]
});
- // Create a step for the MCP configuration
const mcpStep: ConditionalStep = {
id: `mcp-${Date.now()}`,
name: `${customConfig.name} MCP`,
@@ -302,7 +242,7 @@ export function WorkflowSidePanel({
onClick={() => handleStepTypeClick(stepType)}
className="w-full p-3 text-left border border-border rounded-2xl hover:bg-muted/50 transition-colors"
>
-
+
@@ -472,30 +412,18 @@ export function WorkflowSidePanel({
{renderContent()}
-
- {/* Pipedream Registry Dialog */}
-