Skip to main content
Glama
waldzellai

Exa Websets MCP Server

by waldzellai

websets_manager

Manage content websets: create, search, enhance with AI, and set notifications for web content collections using Exa's platform.

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
NameRequiredDescriptionDefault
operationYesWhat you want to do
resourceIdNoID of the webset, search, or enhancement to work with
websetNo
searchNo
enhancementNo
notificationNo
updateNo
queryNo

Implementation Reference

  • The main tool registration object for 'websets_manager'. Contains the handler function that routes to sub-operations (create_webset, list_websets, search_webset, enhance_content, etc.) based on the 'operation' field.
    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
    };
  • Input validation schemas for the tool. Includes BaseOperationSchema (defining all 19 operations), WebsetParamsSchema, SearchParamsSchema, EnhancementParamsSchema, NotificationParamsSchema, UpdateParamsSchema, QueryParamsSchema, and the combined WebsetsManagerSchema.
    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/index.ts:124-143 (registration)
    Tools are registered with the MCP server in registerTools(). The websets_manager tool is pulled from toolRegistry and registered via this.server.tool(name, description, schema, handler).
    private registerTools(): void {
      // Create our simplified tool registry with three tools
        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/index.ts:18-19 (registration)
    Importing './tools/websetsManager.js' at module level triggers the tool's self-registration into toolRegistry via the side-effect import pattern.
    import "./tools/websetsManager.js";
    import "./tools/knowledgeGraph.js";
  • Helper function getOperationHelp() that provides user-friendly guidance for operations when errors occur.
    function getOperationHelp(operation: string): string[] {
      const helpMap: Record<string, string[]> = {
        "create_webset": [
          "Provide a searchQuery describing what content you want to collect",
          "Optionally specify resultCount in advanced settings",
          "Webset creation takes 10-15 minutes to complete"
        ],
        "search_webset": [
          "Provide resourceId of the webset to search within",
          "Provide query describing what to find in the webset"
        ],
        "enhance_content": [
          "Provide resourceId of the webset to enhance",
          "Provide task describing what additional data you want to extract"
        ]
      };
      
      return helpMap[operation] || [
        "Check the operation name and required parameters",
        "Ensure resourceId is provided when working with existing resources"
      ];
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, and the description does not disclose behavioral traits such as sync/async nature, permissions, rate limits, or side effects. Minimal disclosure.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short (3 sentences) and front-loads the purpose. No unnecessary words, but could benefit from structured formatting like bullet points.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness1/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool is complex with many operations and nested parameters, but the description provides no details on how to use specific parameters or operations. Extremely incomplete for the tool's complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description does not explain any parameters; it only offers a high-level overview. With schema description coverage at 25%, the description fails to add meaning beyond the limited schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool manages websets, searches, enhancements, and notifications via Exa. It lists multiple operations, making the purpose clear, though it does not explicitly differentiate from siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description mentions it's simpler than using separate tools, but provides no explicit when-to-use or when-not-to-use guidance, nor comparison to sibling tools like knowledge_graph or web_search_exa.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/waldzellai/exa-mcp-server-websets'

If you have feedback or need assistance with the MCP directory API, please join our Discord server