Skip to main content
Glama
usama-dtc

Salesforce MCP Server

by usama-dtc

salesforce_search_all

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

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

  • Main execution logic for salesforce_search_all tool. Constructs and executes 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 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:37-47 (registration)
    Registers the salesforce_search_all tool (as SEARCH_ALL) in the list of available tools returned by ListToolsRequest.
      tools: [
        SEARCH_OBJECTS, 
        DESCRIBE_OBJECT, 
        QUERY_RECORDS, 
        DML_RECORDS,
        MANAGE_OBJECT,
        MANAGE_FIELD,
        SEARCH_ALL,
        UPLOAD_REPORT_XML  // Add new tool to the list
      ],
    }));
  • src/index.ts:145-174 (registration)
    Switch case in main tool dispatcher that validates arguments and calls 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 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 full burden for behavioral disclosure. It mentions access control options ('updateable' and 'viewable' options control record access filtering') and search capabilities, but doesn't cover important behavioral aspects like rate limits, authentication requirements, error handling, or what the output looks like. It provides some operational context but misses key behavioral traits.

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

Conciseness3/5

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

The description is front-loaded with the core purpose, but includes extensive examples and notes that could be streamlined. While informative, some details (like the second advanced example) might be excessive. The structure is logical but could be more concise while maintaining clarity.

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?

For a complex search tool with 6 parameters, no annotations, and no output schema, the description provides good operational guidance but lacks critical context. It doesn't describe the return format, error conditions, performance characteristics, or how results are structured across multiple objects. The examples help but don't fully compensate for missing behavioral and output information.

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?

With 100% schema description coverage, the baseline is 3. The description adds significant value through detailed examples showing how parameters work together, explanations of wildcard support, WITH clause types, and object-specific clauses. It provides practical context beyond the schema's technical definitions, though it doesn't fully explain all parameter interactions.

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 specific action ('Search across multiple Salesforce objects') and technology used ('using SOSL'), distinguishing it from sibling tools like salesforce_query_records (which likely uses SOQL) and salesforce_search_objects (which might be more limited). It provides a verb+resource+method combination that is precise and differentiated.

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

Usage Guidelines4/5

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

The description implies usage context through examples and notes (e.g., 'Search across multiple Salesforce objects'), but doesn't explicitly state when to use this tool versus alternatives like salesforce_query_records or salesforce_search_objects. It provides operational guidance but lacks comparative context with sibling tools.

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/usama-dtc/salesforce_mcp'

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