diff --git a/frontend/src/app/(dashboard)/agents/_components/create-agent-dialog.tsx b/frontend/src/app/(dashboard)/agents/_components/create-agent-dialog.tsx index 0376db8d..b45ac483 100644 --- a/frontend/src/app/(dashboard)/agents/_components/create-agent-dialog.tsx +++ b/frontend/src/app/(dashboard)/agents/_components/create-agent-dialog.tsx @@ -289,7 +289,10 @@ export const CreateAgentDialog = ({ isOpen, onOpenChange, onAgentCreated }: Crea ({ + ...mcp, + enabledTools: mcp.enabledTools || [] + }))} onConfigurationChange={handleMCPConfigurationChange} /> diff --git a/frontend/src/app/(dashboard)/agents/_components/update-agent-dialog.tsx b/frontend/src/app/(dashboard)/agents/_components/update-agent-dialog.tsx index 21903132..12ef258a 100644 --- a/frontend/src/app/(dashboard)/agents/_components/update-agent-dialog.tsx +++ b/frontend/src/app/(dashboard)/agents/_components/update-agent-dialog.tsx @@ -393,14 +393,20 @@ export const UpdateAgentDialog = ({ agentId, isOpen, onOpenChange, onAgentUpdate ({ - name: customMcp.name, - qualifiedName: `custom_${customMcp.type}_${customMcp.name.replace(' ', '_').toLowerCase()}`, - config: customMcp.config, - enabledTools: customMcp.enabledTools, - isCustom: true, - customType: customMcp.type - }))]} + configuredMCPs={[ + ...(formData.configured_mcps || []).map(mcp => ({ + ...mcp, + enabledTools: mcp.enabledTools || [] + })), + ...(formData.custom_mcps || []).map(customMcp => ({ + name: customMcp.name, + qualifiedName: `custom_${customMcp.type}_${customMcp.name.replace(' ', '_').toLowerCase()}`, + config: customMcp.config, + enabledTools: customMcp.enabledTools, + isCustom: true, + customType: customMcp.type + })) + ]} onConfigurationChange={handleMCPConfigurationChange} /> diff --git a/frontend/src/components/agents/pipedream/pipedream-app-browser.tsx b/frontend/src/components/agents/pipedream/pipedream-app-browser.tsx index b683aaf6..7aacf62f 100644 --- a/frontend/src/components/agents/pipedream/pipedream-app-browser.tsx +++ b/frontend/src/components/agents/pipedream/pipedream-app-browser.tsx @@ -86,15 +86,25 @@ export const PipedreamAppBrowser: React.FC = ({
-
+
{e.preventDefault()}} className="relative flex-1"> setSearchQuery(e.target.value)} className="pl-10" /> -
+ {searchQuery && ( + + )} + {selectedCategory && (
-
+
handleSearch(e.target.value)} className="pl-8 h-8 text-sm" /> -
+ {search && ( + + )} +
{isLoading && ( diff --git a/frontend/src/hooks/react-query/pipedream/keys.ts b/frontend/src/hooks/react-query/pipedream/keys.ts index 0db7334e..e958b29e 100644 --- a/frontend/src/hooks/react-query/pipedream/keys.ts +++ b/frontend/src/hooks/react-query/pipedream/keys.ts @@ -7,6 +7,7 @@ export const pipedreamKeys = { workflows: () => [...pipedreamKeys.all, 'workflows'] as const, workflowRuns: (workflowId: string) => [...pipedreamKeys.all, 'workflow-runs', workflowId] as const, apps: (page: number, search?: string, category?: string) => [...pipedreamKeys.all, 'apps', page, search || '', category || ''] as const, + appsSearch: (query: string, page: number, category?: string) => [...pipedreamKeys.all, 'apps', 'search', query, page, category || ''] as const, availableTools: () => [...pipedreamKeys.all, 'available-tools'] as const, mcpDiscovery: (options?: { app_slug?: string; oauth_app_id?: string; custom?: boolean }) => [...pipedreamKeys.all, 'mcp-discovery', options?.app_slug, options?.oauth_app_id, options?.custom] as const, diff --git a/frontend/src/hooks/react-query/pipedream/use-pipedream.ts b/frontend/src/hooks/react-query/pipedream/use-pipedream.ts index 22b1cf0f..8b22ec8a 100644 --- a/frontend/src/hooks/react-query/pipedream/use-pipedream.ts +++ b/frontend/src/hooks/react-query/pipedream/use-pipedream.ts @@ -62,6 +62,18 @@ export const usePipedreamApps = (page: number = 1, search?: string, category?: s }); }; +export const usePipedreamAppsSearch = (query: string, page: number = 1, category?: string) => { + return useQuery({ + queryKey: ['pipedream', 'apps', 'search', query, page, category], + queryFn: async (): Promise => { + return await pipedreamApi.searchApps(query, page, category); + }, + staleTime: 5 * 60 * 1000, + retry: 2, + enabled: !!query && query.length > 0, + }); +}; + export const usePipedreamAvailableTools = createQueryHook( pipedreamKeys.availableTools(), async (forceRefresh: boolean = false): Promise => { diff --git a/frontend/src/hooks/react-query/pipedream/utils.ts b/frontend/src/hooks/react-query/pipedream/utils.ts index a1ce2c19..8dd08cf7 100644 --- a/frontend/src/hooks/react-query/pipedream/utils.ts +++ b/frontend/src/hooks/react-query/pipedream/utils.ts @@ -100,8 +100,13 @@ export interface PipedreamAppResponse { current_page: number; page_size: number; has_more: boolean; + count?: number; + start_cursor?: string; + end_cursor?: string; }; total_count: number; + error?: string; + search_query?: string; } export interface PipedreamTool { @@ -174,13 +179,14 @@ export const pipedreamApi = { }, async getApps(page: number = 1, search?: string, category?: string): Promise { + if (search) { + return await this.searchApps(search, page, category); + } + const params = new URLSearchParams({ page: page.toString(), }); - if (search) { - params.append('search', search); - } if (category) { params.append('category', category); } @@ -195,8 +201,41 @@ export const pipedreamApi = { if (!result.success) { throw new Error(result.error?.message || 'Failed to get apps'); } + const data = result.data!; + if (!data.success && data.error) { + throw new Error(data.error); + } + return data; + }, - return result.data!; + async searchApps(query: string, page: number = 1, category?: string): Promise { + const params = new URLSearchParams({ + q: query, + page: page.toString(), + }); + + if (category) { + params.append('category', category); + } + + const result = await backendApi.get( + `/pipedream/apps/search?${params.toString()}`, + { + errorContext: { operation: 'search apps', resource: 'Pipedream apps' }, + } + ); + + if (!result.success) { + throw new Error(result.error?.message || 'Failed to search apps'); + } + + // Handle both success response and potential error in the data + const data = result.data!; + if (!data.success && data.error) { + throw new Error(data.error); + } + + return data; }, async getAvailableTools(): Promise {