websets_manager
Create and manage content collections, search within websets, enhance data with AI, and set up notifications using Exa's platform for research and analysis workflows.
Instructions
Manage content websets, searches, and data enhancements using Exa's platform. This single tool handles creating websets of web content, searching within them, enhancing data with AI, and setting up notifications. Much simpler than using separate tools for each operation.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| operation | Yes | What you want to do | |
| resourceId | No | ID of the webset, search, or enhancement to work with | |
| webset | No | ||
| search | No | ||
| enhancement | No | ||
| notification | No | ||
| update | No | ||
| query | No |
Implementation Reference
- src/tools/websetsManager.ts:148-269 (registration)Primary registration of the websets_manager tool in toolRegistry, including name, description, schema reference, category, and the main handler function that routes operations to specific handlers.toolRegistry["websets_manager"] = { name: "websets_manager", description: "Manage content websets, searches, and data enhancements using Exa's platform. This single tool handles creating websets of web content, searching within them, enhancing data with AI, and setting up notifications. Much simpler than using separate tools for each operation.", schema: WebsetsManagerSchema.shape, category: ToolCategory.WEBSETS, service: ServiceType.WEBSETS, handler: async (args) => { const { operation, resourceId, webset, search, enhancement, notification, update, query: params } = args; const requestId = `websets_manager-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`; const logger = createRequestLogger(requestId, 'websets_manager'); logger.start(`${operation} operation`); try { // Get API key from environment (no more repetition in parameters!) const apiKey = process.env.EXA_API_KEY; if (!apiKey) { throw new Error("EXA_API_KEY environment variable is required"); } const services = createServices(apiKey); // Route to appropriate operation handler switch (operation) { case "create_webset": return await handleCreateWebset(services, webset, logger); case "list_websets": return await handleListWebsets(services, params, logger); case "get_webset_status": return await handleGetWebsetStatus(services, resourceId, logger); case "update_webset": return await handleUpdateWebset(services, resourceId, update, logger); case "delete_webset": return await handleDeleteWebset(services, resourceId, logger); case "cancel_webset": return await handleCancelWebset(services, resourceId, logger); case "search_webset": return await handleSearchWebset(services, resourceId, search, logger); case "get_search_results": return await handleGetSearchResults(services, resourceId, logger); case "cancel_search": return await handleCancelSearch(services, resourceId, logger); case "enhance_content": return await handleEnhanceContent(services, resourceId, enhancement, logger); case "get_enhancement_results": return await handleGetEnhancementResults(services, resourceId, logger); case "delete_enhancement": return await handleDeleteEnhancement(services, resourceId, logger); case "cancel_enhancement": return await handleCancelEnhancement(services, resourceId, logger); case "setup_notifications": return await handleSetupNotifications(services, notification, logger); case "list_notifications": return await handleListNotifications(services, params, logger); case "get_notification_details": return await handleGetNotificationDetails(services, resourceId, logger); case "remove_notifications": return await handleRemoveNotifications(services, resourceId, logger); case "list_activities": return await handleListActivities(services, params, logger); case "get_activity_details": return await handleGetActivityDetails(services, resourceId, logger); case "list_content_items": return await handleListContentItems(services, resourceId, params, logger); default: throw new Error(`Unknown operation: ${operation}`); } } catch (error) { logger.error(error); let errorMessage: string; if (error instanceof Error) { errorMessage = error.message; } else if (typeof error === 'object' && error !== null) { // Try to extract meaningful information from the error object errorMessage = JSON.stringify(error, null, 2); } else if (error === undefined) { errorMessage = 'An unknown error occurred'; } else { errorMessage = String(error); } logger.log(`Operation failed: ${errorMessage}`); return { content: [{ type: "text" as const, text: JSON.stringify({ success: false, operation, error: errorMessage, help: getOperationHelp(operation) }, null, 2) }], isError: true }; } }, enabled: true };
- src/tools/websetsManager.ts:22-145 (schema)Comprehensive Zod schema definition for the websets_manager tool, including BaseOperationSchema with all supported operations and parameter objects for each operation type.const BaseOperationSchema = z.object({ operation: z.enum([ // Content Webset Operations "create_webset", "list_websets", "get_webset_status", "update_webset", "delete_webset", "cancel_webset", // Content Search Operations "search_webset", "get_search_results", "cancel_search", // Data Enhancement Operations "enhance_content", "get_enhancement_results", "delete_enhancement", "cancel_enhancement", // Notification Operations "setup_notifications", "list_notifications", "get_notification_details", "remove_notifications", // Activity Monitoring "list_activities", "get_activity_details", // Content Management "list_content_items" ]).describe("What you want to do"), // Target resource ID (when working with existing resources) resourceId: z.string().optional().describe("ID of the webset, search, or enhancement to work with") }); // Content Webset Parameters const WebsetParamsSchema = z.object({ searchQuery: z.string().describe("What you want to find (required for new websets)"), description: z.string().optional().describe("Human-readable description of this webset"), advanced: z.object({ resultCount: z.number().min(1).max(1000).default(10).describe("How many items to find"), focusArea: z.enum(["company"]).optional().describe("What type of entities to focus on"), criteria: z.array(z.object({ description: z.string().describe("Specific requirement or filter") })).optional().describe("Additional requirements for filtering results"), externalReference: z.string().optional().describe("Your own reference ID for tracking"), tags: z.record(z.string().max(1000)).optional().describe("Custom labels for organization") }).optional().describe("Advanced webset settings") }).optional(); // Search Parameters const SearchParamsSchema = z.object({ query: z.string().describe("What to search for within the webset"), maxResults: z.number().min(1).max(100).default(10).describe("Maximum number of results to return"), advanced: z.object({ focusArea: z.object({ type: z.literal("company").describe("Currently supports companies only") }).optional().describe("What type of entities to focus search on"), requirements: z.array(z.object({ description: z.string().describe("Specific requirement for search results") })).optional().describe("Additional search requirements"), tags: z.record(z.string().max(1000)).optional().describe("Custom labels for this search"), waitForResults: z.boolean().optional().describe("Automatically poll until search completes (max 1 minute)") }).optional().describe("Advanced search settings") }).optional(); // Enhancement Parameters const EnhancementParamsSchema = z.object({ task: z.string().describe("What kind of additional data you want to extract or analyze"), advanced: z.object({ outputFormat: z.enum(["text", "date", "number", "options", "email", "phone"]).default("text").describe("Expected format of the results"), waitForResults: z.boolean().optional().describe("Automatically poll until enhancement completes (max 2 minutes)"), choices: z.array(z.object({ label: z.string().describe("Possible answer option") })).optional().describe("Predefined answer choices (only for 'options' format)"), tags: z.record(z.string().max(1000)).optional().describe("Custom labels for this enhancement") }).optional().describe("Advanced enhancement settings") }).optional(); // Notification Parameters const NotificationParamsSchema = z.object({ webhookUrl: z.string().url().describe("URL where notifications should be sent"), events: z.array(z.enum([ "webset.created", "webset.deleted", "webset.paused", "webset.idle", "webset.search.created", "webset.search.completed", "webset.search.updated", "webset.search.canceled", "webset.export.created", "webset.export.completed", "webset.item.created", "webset.item.enriched" ])).describe("Which events you want to be notified about"), advanced: z.object({ tags: z.record(z.string().max(1000)).optional().describe("Custom labels for this notification setup") }).optional().describe("Advanced notification settings") }).optional(); // Update Parameters const UpdateParamsSchema = z.object({ description: z.string().optional().describe("New description for the webset"), tags: z.record(z.string().max(1000)).optional().describe("Updated custom labels") }).optional(); // Query Parameters (for listing operations) const QueryParamsSchema = z.object({ limit: z.number().min(1).max(100).default(25).describe("Maximum number of items to return"), offset: z.number().min(0).default(0).describe("Number of items to skip"), status: z.enum(["pending", "processing", "completed", "failed", "cancelled"]).optional().describe("Filter by status") }).optional(); // Combined schema const WebsetsManagerSchema = BaseOperationSchema.extend({ // Operation-specific parameters webset: WebsetParamsSchema, search: SearchParamsSchema, enhancement: EnhancementParamsSchema, notification: NotificationParamsSchema, update: UpdateParamsSchema, query: QueryParamsSchema });
- src/tools/websetsManager.ts:272-322 (handler)Example helper handler for 'create_webset' operation: validates params, calls websetService.createWebset, handles keep-alive progress, returns formatted success response.async function handleCreateWebset(services: any, params: any, logger: any) { if (!params?.searchQuery) { throw new Error("searchQuery is required to create a webset"); } const request = { search: { query: params.searchQuery, count: params.advanced?.resultCount || 10, ...(params.advanced?.focusArea && { entity: { type: params.advanced.focusArea } }), ...(params.advanced?.criteria && { criteria: params.advanced.criteria }) }, ...(params.advanced?.externalReference && { externalId: params.advanced.externalReference }), ...(params.advanced?.tags && { metadata: params.advanced.tags }) }; logger.log(`Creating webset for: "${params.searchQuery}"`); // Use keep-alive for long-running operation const result = await withKeepAlive( 'Creating webset', async (keepAlive) => { keepAlive.sendProgress('Initializing webset creation', 10); const webset = await services.websetService.createWebset(request); keepAlive.sendProgress('Webset created, processing will continue in background', 100); return webset; }, { interval: 5000, enableLogging: true } ); return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, message: "Content webset created successfully! This will take 10-15 minutes to process.", websetId: result.id, status: result.status, searchQuery: params.searchQuery, expectedResults: params.advanced?.resultCount || 10, nextSteps: [ `Check progress: use operation "get_webset_status" with resourceId "${result.id}"`, `When complete: use operation "list_content_items" with resourceId "${result.id}" to see results` ] }, null, 2) }] }; }
- src/index.ts:126-143 (registration)Secondary registration: Imports websetsManager and registers websets_manager tool with the MCP server via server.tool() calls.const simplifiedRegistry = { web_search_exa: toolRegistry["web_search_exa"], websets_manager: toolRegistry["websets_manager"], websets_guide: websetsGuideTool, knowledge_graph: toolRegistry["knowledge_graph"], }; // Register our tools Object.values(simplifiedRegistry).forEach(tool => { if (tool) { this.server.tool( tool.name, tool.description, tool.schema, tool.handler ); } });
- src/tools/websetsManager.ts:355-396 (helper)Helper handler for 'get_webset_status' operation: fetches status from service, provides user-friendly messages and next steps.async function handleGetWebsetStatus(services: any, resourceId: string | undefined, logger: any) { if (!resourceId) { throw new Error("resourceId is required to check webset status"); } logger.log(`Getting status for webset: ${resourceId}`); const result = await services.websetService.getWebsetStatus(resourceId); const statusMessages = { pending: "Webset is queued for processing", processing: "Webset is being built (this takes 10-15 minutes)", completed: "Webset is ready! You can now search and enhance the content.", failed: "Webset creation failed. Please try again or contact support.", cancelled: "Webset creation was cancelled" }; return { content: [{ type: "text" as const, text: JSON.stringify({ success: true, websetId: resourceId, status: result.status, message: statusMessages[result.status as keyof typeof statusMessages] || `Status: ${result.status}`, details: { createdAt: result.createdAt, updatedAt: result.updatedAt, itemCount: result.searches?.[0]?.progress?.found || 0, searchQuery: result.searches?.[0]?.query, ...(result.error && { error: result.error }) }, ...(result.status === "completed" && { nextSteps: [ `Search within webset: use operation "search_webset" with resourceId "${resourceId}"`, `View content: use operation "list_content_items" with resourceId "${resourceId}"`, `Enhance data: use operation "enhance_content" with resourceId "${resourceId}"` ] }) }, null, 2) }] }; }