Skip to main content
Glama

salesforce_search_all

Search across multiple Salesforce objects using SOSL queries with customizable filters, field selection, and access controls to find relevant records.

Instructions

Search across multiple Salesforce objects using SOSL (Salesforce Object Search Language).

Examples:

  1. Basic search across all objects: { "searchTerm": "John", "objects": [ { "name": "Account", "fields": ["Name"], "limit": 10 }, { "name": "Contact", "fields": ["FirstName", "LastName", "Email"] } ] }

  2. Advanced search with filters: { "searchTerm": "Cloud*", "searchIn": "NAME FIELDS", "objects": [ { "name": "Account", "fields": ["Name", "Industry"], "orderBy": "Name DESC", "where": "Industry = 'Technology'" } ], "withClauses": [ { "type": "NETWORK", "value": "ALL NETWORKS" }, { "type": "SNIPPET", "fields": ["Description"] } ] }

Notes:

  • Use * and ? for wildcards in search terms

  • Each object can have its own WHERE, ORDER BY, and LIMIT clauses

  • Support for WITH clauses: DATA CATEGORY, DIVISION, METADATA, NETWORK, PRICEBOOKID, SNIPPET, SECURITY_ENFORCED

  • "updateable" and "viewable" options control record access filtering

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
searchTermYesText to search for (supports wildcards * and ?)
searchInNoWhich fields to search in
objectsYesList of objects to search and their return fields
withClausesNoAdditional WITH clauses for the search
updateableNoReturn only updateable records
viewableNoReturn only viewable records

Implementation Reference

  • Core handler function that constructs and executes SOSL search queries across multiple Salesforce objects, formats results, and handles errors.
    export async function handleSearchAll(conn: any, args: SearchAllArgs) {
      const { searchTerm, searchIn = "ALL FIELDS", objects, withClauses, updateable, viewable } = args;
    
      try {
        // Validate the search term
        if (!searchTerm.trim()) {
          throw new Error('Search term cannot be empty');
        }
    
        // Construct the RETURNING clause with object-specific clauses
        const returningClause = objects
          .map(obj => {
            let clause = `${obj.name}(${obj.fields.join(',')}`
            
            // Add object-specific clauses if present
            if (obj.where) clause += ` WHERE ${obj.where}`;
            if (obj.orderBy) clause += ` ORDER BY ${obj.orderBy}`;
            if (obj.limit) clause += ` LIMIT ${obj.limit}`;
            
            return clause + ')';
          })
          .join(', ');
    
        // Build WITH clauses if present
        const withClausesStr = withClauses
          ? withClauses.map(buildWithClause).join(' ')
          : '';
    
        // Add updateable/viewable flags if specified
        const accessFlags = [];
        if (updateable) accessFlags.push('UPDATEABLE');
        if (viewable) accessFlags.push('VIEWABLE');
        const accessClause = accessFlags.length > 0 ? 
          ` RETURNING ${accessFlags.join(',')}` : '';
    
        // Construct complete SOSL query
        const soslQuery = `FIND {${searchTerm}} IN ${searchIn} 
          ${withClausesStr}
          RETURNING ${returningClause}
          ${accessClause}`.trim();
    
        // Execute search
        const result = await conn.search(soslQuery);
    
        // Format results by object
        let formattedResults = '';
        objects.forEach((obj, index) => {
          const objectResults = result.searchRecords.filter((record: any) => 
            record.attributes.type === obj.name
          );
    
          formattedResults += `\n${obj.name} (${objectResults.length} records found):\n`;
          
          if (objectResults.length > 0) {
            objectResults.forEach((record: any, recordIndex: number) => {
              formattedResults += `  Record ${recordIndex + 1}:\n`;
              obj.fields.forEach(field => {
                const value = record[field];
                formattedResults += `    ${field}: ${value !== null && value !== undefined ? value : 'null'}\n`;
              });
              // Add metadata or snippet info if requested
              if (withClauses?.some(w => w.type === "METADATA")) {
                formattedResults += `    Metadata:\n      Last Modified: ${record.attributes.lastModifiedDate}\n`;
              }
              if (withClauses?.some(w => w.type === "SNIPPET")) {
                formattedResults += `    Snippets:\n${record.snippets?.map((s: any) => 
                  `      ${s.field}: ${s.snippet}`).join('\n') || '      None'}\n`;
              }
            });
          }
    
          if (index < objects.length - 1) {
            formattedResults += '\n';
          }
        });
    
        return {
          content: [{
            type: "text",
            text: `Search Results:${formattedResults}`
          }],
          isError: false,
        };
      } catch (error) {
        // Enhanced error handling for SOSL queries
        const errorMessage = error instanceof Error ? error.message : String(error);
        let enhancedError = errorMessage;
    
        if (errorMessage.includes('MALFORMED_SEARCH')) {
          enhancedError = `Invalid search query format. Common issues:\n` +
            `1. Search term contains invalid characters\n` +
            `2. Object or field names are incorrect\n` +
            `3. Missing required SOSL syntax elements\n` +
            `4. Invalid WITH clause combination\n\n` +
            `Original error: ${errorMessage}`;
        } else if (errorMessage.includes('INVALID_FIELD')) {
          enhancedError = `Invalid field specified in RETURNING clause. Please check:\n` +
            `1. Field names are correct\n` +
            `2. Fields exist on the specified objects\n` +
            `3. You have access to all specified fields\n` +
            `4. WITH SNIPPET fields are valid\n\n` +
            `Original error: ${errorMessage}`;
        } else if (errorMessage.includes('WITH_CLAUSE')) {
          enhancedError = `Error in WITH clause. Please check:\n` +
            `1. WITH clause type is supported\n` +
            `2. WITH clause value is valid\n` +
            `3. You have permission to use the specified WITH clause\n\n` +
            `Original error: ${errorMessage}`;
        }
    
        return {
          content: [{
            type: "text",
            text: `Error executing search: ${enhancedError}`
          }],
          isError: true,
        };
      }
    }
  • Tool definition including name, description, and detailed inputSchema for validating salesforce_search_all parameters.
    export const SEARCH_ALL: Tool = {
      name: "salesforce_search_all",
      description: `Search across multiple Salesforce objects using SOSL (Salesforce Object Search Language).
      
    Examples:
    1. Basic search across all objects:
       {
         "searchTerm": "John",
         "objects": [
           { "name": "Account", "fields": ["Name"], "limit": 10 },
           { "name": "Contact", "fields": ["FirstName", "LastName", "Email"] }
         ]
       }
    
    2. Advanced search with filters:
       {
         "searchTerm": "Cloud*",
         "searchIn": "NAME FIELDS",
         "objects": [
           { 
             "name": "Account", 
             "fields": ["Name", "Industry"], 
             "orderBy": "Name DESC",
             "where": "Industry = 'Technology'"
           }
         ],
         "withClauses": [
           { "type": "NETWORK", "value": "ALL NETWORKS" },
           { "type": "SNIPPET", "fields": ["Description"] }
         ]
       }
    
    Notes:
    - Use * and ? for wildcards in search terms
    - Each object can have its own WHERE, ORDER BY, and LIMIT clauses
    - Support for WITH clauses: DATA CATEGORY, DIVISION, METADATA, NETWORK, PRICEBOOKID, SNIPPET, SECURITY_ENFORCED
    - "updateable" and "viewable" options control record access filtering`,
      inputSchema: {
        type: "object",
        properties: {
          searchTerm: {
            type: "string",
            description: "Text to search for (supports wildcards * and ?)"
          },
          searchIn: {
            type: "string",
            enum: ["ALL FIELDS", "NAME FIELDS", "EMAIL FIELDS", "PHONE FIELDS", "SIDEBAR FIELDS"],
            description: "Which fields to search in",
            optional: true
          },
          objects: {
            type: "array",
            items: {
              type: "object",
              properties: {
                name: { 
                  type: "string",
                  description: "API name of the object"
                },
                fields: {
                  type: "array",
                  items: { type: "string" },
                  description: "Fields to return for this object"
                },
                where: {
                  type: "string",
                  description: "WHERE clause for this object",
                  optional: true
                },
                orderBy: {
                  type: "string",
                  description: "ORDER BY clause for this object",
                  optional: true
                },
                limit: {
                  type: "number",
                  description: "Maximum number of records to return for this object",
                  optional: true
                }
              },
              required: ["name", "fields"]
            },
            description: "List of objects to search and their return fields"
          },
          withClauses: {
            type: "array",
            items: {
              type: "object",
              properties: {
                type: {
                  type: "string",
                  enum: ["DATA CATEGORY", "DIVISION", "METADATA", "NETWORK", 
                        "PRICEBOOKID", "SNIPPET", "SECURITY_ENFORCED"]
                },
                value: {
                  type: "string",
                  description: "Value for the WITH clause",
                  optional: true
                },
                fields: {
                  type: "array",
                  items: { type: "string" },
                  description: "Fields for SNIPPET clause",
                  optional: true
                }
              },
              required: ["type"]
            },
            description: "Additional WITH clauses for the search",
            optional: true
          },
          updateable: {
            type: "boolean",
            description: "Return only updateable records",
            optional: true
          },
          viewable: {
            type: "boolean",
            description: "Return only viewable records",
            optional: true
          }
        },
        required: ["searchTerm", "objects"]
      }
    };
  • src/index.ts:35-45 (registration)
    Registers the salesforce_search_all tool in the MCP server's list of available tools.
    server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        SEARCH_OBJECTS, 
        DESCRIBE_OBJECT, 
        QUERY_RECORDS, 
        DML_RECORDS,
        MANAGE_OBJECT,
        MANAGE_FIELD,
        SEARCH_ALL
      ],
    }));
  • src/index.ts:143-172 (registration)
    Switch case in tool dispatcher that validates arguments and invokes the handleSearchAll handler for salesforce_search_all.
    case "salesforce_search_all": {
      const searchArgs = args as Record<string, unknown>;
      if (!searchArgs.searchTerm || !Array.isArray(searchArgs.objects)) {
        throw new Error('searchTerm and objects array are required for search');
      }
    
      // Validate objects array
      const objects = searchArgs.objects as Array<Record<string, unknown>>;
      if (!objects.every(obj => obj.name && Array.isArray(obj.fields))) {
        throw new Error('Each object must specify name and fields array');
      }
    
      // Type check and conversion
      const validatedArgs: SearchAllArgs = {
        searchTerm: searchArgs.searchTerm as string,
        searchIn: searchArgs.searchIn as "ALL FIELDS" | "NAME FIELDS" | "EMAIL FIELDS" | "PHONE FIELDS" | "SIDEBAR FIELDS" | undefined,
        objects: objects.map(obj => ({
          name: obj.name as string,
          fields: obj.fields as string[],
          where: obj.where as string | undefined,
          orderBy: obj.orderBy as string | undefined,
          limit: obj.limit as number | undefined
        })),
        withClauses: searchArgs.withClauses as WithClause[] | undefined,
        updateable: searchArgs.updateable as boolean | undefined,
        viewable: searchArgs.viewable as boolean | undefined
      };
    
      return await handleSearchAll(conn, validatedArgs);
    }
  • Helper function to construct SOSL WITH clauses from input parameters.
    function buildWithClause(withClause: WithClause): string {
      switch (withClause.type) {
        case "SNIPPET":
          return `WITH SNIPPET (${withClause.fields?.join(', ')})`;
        case "DATA CATEGORY":
        case "DIVISION":
        case "NETWORK":
        case "PRICEBOOKID":
          return `WITH ${withClause.type} = ${withClause.value}`;
        case "METADATA":
        case "SECURITY_ENFORCED":
          return `WITH ${withClause.type}`;
        default:
          return '';
      }
    }
Behavior3/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It adds some context, such as support for wildcards, object-specific clauses, and WITH clauses, but does not cover critical aspects like rate limits, authentication needs, error handling, or what the output looks like (since no output schema exists). It partially compensates but leaves gaps for a complex search tool.

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 well-structured with a clear purpose statement, examples, and notes, but it is lengthy due to detailed examples. Every sentence earns its place by clarifying usage, though it could be more front-loaded; the purpose is clear, but the bulk is in examples, which are helpful but add verbosity.

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

Completeness3/5

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

Given the tool's complexity (6 parameters, no annotations, no output schema), the description is moderately complete. It covers input semantics and usage examples but lacks output details, error handling, and behavioral constraints like performance or limits. It provides enough for basic use but falls short for full agent guidance in a production environment.

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

Parameters4/5

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

Schema description coverage is 100%, so the baseline is 3. The description adds value by providing examples that illustrate parameter usage (e.g., 'objects' with nested properties, 'withClauses' types) and notes on wildcards and access filtering ('updateable' and 'viewable'), enhancing understanding beyond the schema's technical definitions. This elevates the score above baseline.

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

Purpose5/5

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

The description clearly states the tool's purpose: 'Search across multiple Salesforce objects using SOSL (Salesforce Object Search Language).' It specifies the verb ('search'), resource ('multiple Salesforce objects'), and technology ('SOSL'), distinguishing it from sibling tools like 'salesforce_query_records' (likely SOQL) and 'salesforce_search_objects' (possibly simpler). This is specific and avoids tautology.

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

Usage Guidelines3/5

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

The description implies usage through examples and notes, such as wildcard support and object-specific clauses, but does not explicitly state when to use this tool versus alternatives like 'salesforce_query_records' or 'salesforce_search_objects'. It provides context for advanced searches but lacks clear guidance on tool selection among siblings.

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/SurajAdsul/mcp-server-salesforce'

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