Skip to main content
Glama
tsmztech

Salesforce MCP Server

salesforce_search_all

Search across multiple Salesforce objects using SOSL queries to find records with specific criteria, returning selected fields with optional filters and clauses.

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

  • The primary handler function that builds and executes a complex SOSL query 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 schema definition including name, description, and detailed inputSchema for validating parameters like searchTerm, objects array, withClauses, etc.
    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:196-225 (registration)
    Tool registration in the main switch statement: validates arguments, converts types, and calls the handleSearchAll handler.
    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);
    }
  • src/index.ts:45-63 (registration)
    Registration of the tool in the list of available tools returned by ListToolsRequest.
    server.setRequestHandler(ListToolsRequestSchema, async () => ({
      tools: [
        SEARCH_OBJECTS, 
        DESCRIBE_OBJECT, 
        QUERY_RECORDS, 
        AGGREGATE_QUERY,
        DML_RECORDS,
        MANAGE_OBJECT,
        MANAGE_FIELD,
        MANAGE_FIELD_PERMISSIONS,
        SEARCH_ALL,
        READ_APEX,
        WRITE_APEX,
        READ_APEX_TRIGGER,
        WRITE_APEX_TRIGGER,
        EXECUTE_ANONYMOUS,
        MANAGE_DEBUG_LOGS
      ],
    }));
  • Helper function to construct SOSL WITH clauses from the 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 about wildcard support, access filtering via 'updateable' and 'viewable' options, and WITH clause types, but does not cover critical aspects like rate limits, authentication requirements, error handling, or response format. The description is informative but incomplete 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. It is appropriately sized for a complex tool, but could be more front-loaded by moving key usage notes earlier. Some sentences in the notes section are slightly redundant with schema descriptions.

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 basic usage and parameters but lacks details on behavioral traits (e.g., performance, limits), error handling, and output format. The examples help, but more context is needed for a robust understanding.

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 through examples that illustrate parameter usage (e.g., object configurations, WITH clauses) and notes on wildcards and access filtering, enhancing understanding beyond the schema. However, it does not fully explain all parameter interactions or edge cases.

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'), and distinguishes it from sibling tools like 'salesforce_query_records' by emphasizing multi-object search capability.

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, showing when to use this tool for multi-object searches with SOSL. However, it lacks explicit guidance on when to choose this tool over alternatives like 'salesforce_search_objects' or 'salesforce_query_records', and does not mention prerequisites or exclusions.

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

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