salesforce_read_apex
Read Apex classes from Salesforce by name or pattern, retrieve code bodies, list classes, and access metadata like API version and modification dates.
Instructions
Read Apex classes from Salesforce.
Examples:
Read a specific Apex class by name: { "className": "AccountController" }
List all Apex classes with an optional name pattern: { "namePattern": "Controller" }
Get metadata about Apex classes: { "includeMetadata": true, "namePattern": "Trigger" }
Use wildcards in name patterns: { "namePattern": "AccountCont" }
Notes:
When className is provided, the full body of that specific class is returned
When namePattern is provided, all matching class names are returned (without body)
Use includeMetadata to get additional information like API version, length, and last modified date
If neither className nor namePattern is provided, all Apex class names will be listed
Wildcards are supported in namePattern: * (matches any characters) and ? (matches a single character)
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| className | No | Name of a specific Apex class to read | |
| namePattern | No | Pattern to match Apex class names (supports wildcards * and ?) | |
| includeMetadata | No | Whether to include metadata about the Apex classes |
Implementation Reference
- src/tools/readApex.ts:83-188 (handler)Main handler function that implements the core logic for reading Apex classes or listing them from Salesforce using SOQL queries on ApexClass object.export async function handleReadApex(conn: any, args: ReadApexArgs) { try { // If a specific class name is provided, get the full class body if (args.className) { console.error(`Reading Apex class: ${args.className}`); // Query the ApexClass object to get the class body const result = await conn.query(` SELECT Id, Name, Body, ApiVersion, LengthWithoutComments, Status, IsValid, LastModifiedDate, LastModifiedById FROM ApexClass WHERE Name = '${args.className}' `); if (result.records.length === 0) { return { content: [{ type: "text", text: `No Apex class found with name: ${args.className}` }], isError: true, }; } const apexClass = result.records[0]; // Format the response with the class body and metadata return { content: [ { type: "text", text: `# Apex Class: ${apexClass.Name}\n\n` + (args.includeMetadata ? `**API Version:** ${apexClass.ApiVersion}\n` + `**Length:** ${apexClass.LengthWithoutComments} characters\n` + `**Status:** ${apexClass.Status}\n` + `**Valid:** ${apexClass.IsValid ? 'Yes' : 'No'}\n` + `**Last Modified:** ${new Date(apexClass.LastModifiedDate).toLocaleString()}\n\n` : '') + "```apex\n" + apexClass.Body + "\n```" } ] }; } // Otherwise, list classes matching the pattern else { console.error(`Listing Apex classes${args.namePattern ? ` matching: ${args.namePattern}` : ''}`); // Build the query let query = ` SELECT Id, Name${args.includeMetadata ? ', ApiVersion, LengthWithoutComments, Status, IsValid, LastModifiedDate' : ''} FROM ApexClass `; // Add name pattern filter if provided if (args.namePattern) { const likePattern = wildcardToLikePattern(args.namePattern); query += ` WHERE Name LIKE '${likePattern}'`; } // Order by name query += ` ORDER BY Name`; const result = await conn.query(query); if (result.records.length === 0) { return { content: [{ type: "text", text: `No Apex classes found${args.namePattern ? ` matching: ${args.namePattern}` : ''}` }] }; } // Format the response as a list of classes let responseText = `# Found ${result.records.length} Apex Classes\n\n`; if (args.includeMetadata) { // Table format with metadata responseText += "| Name | API Version | Length | Status | Valid | Last Modified |\n"; responseText += "|------|------------|--------|--------|-------|---------------|\n"; for (const cls of result.records) { responseText += `| ${cls.Name} | ${cls.ApiVersion} | ${cls.LengthWithoutComments} | ${cls.Status} | ${cls.IsValid ? 'Yes' : 'No'} | ${new Date(cls.LastModifiedDate).toLocaleString()} |\n`; } } else { // Simple list format for (const cls of result.records) { responseText += `- ${cls.Name}\n`; } } return { content: [{ type: "text", text: responseText }] }; } } catch (error) { console.error('Error reading Apex classes:', error); return { content: [{ type: "text", text: `Error reading Apex classes: ${error instanceof Error ? error.message : String(error)}` }], isError: true, }; } }
- src/tools/readApex.ts:3-52 (schema)Tool schema definition including name, description, and input schema for validating arguments.export const READ_APEX: Tool = { name: "salesforce_read_apex", description: `Read Apex classes from Salesforce. Examples: 1. Read a specific Apex class by name: { "className": "AccountController" } 2. List all Apex classes with an optional name pattern: { "namePattern": "Controller" } 3. Get metadata about Apex classes: { "includeMetadata": true, "namePattern": "Trigger" } 4. Use wildcards in name patterns: { "namePattern": "Account*Cont*" } Notes: - When className is provided, the full body of that specific class is returned - When namePattern is provided, all matching class names are returned (without body) - Use includeMetadata to get additional information like API version, length, and last modified date - If neither className nor namePattern is provided, all Apex class names will be listed - Wildcards are supported in namePattern: * (matches any characters) and ? (matches a single character)`, inputSchema: { type: "object", properties: { className: { type: "string", description: "Name of a specific Apex class to read" }, namePattern: { type: "string", description: "Pattern to match Apex class names (supports wildcards * and ?)" }, includeMetadata: { type: "boolean", description: "Whether to include metadata about the Apex classes" } } } };
- src/index.ts:227-238 (registration)Registration in the main switch dispatcher that maps tool calls to the handleReadApex function.case "salesforce_read_apex": { const apexArgs = args as Record<string, unknown>; // Type check and conversion const validatedArgs: ReadApexArgs = { className: apexArgs.className as string | undefined, namePattern: apexArgs.namePattern as string | undefined, includeMetadata: apexArgs.includeMetadata as boolean | undefined }; return await handleReadApex(conn, validatedArgs); }
- src/tools/readApex.ts:65-75 (helper)Helper function to convert wildcard patterns (*, ?) to SQL LIKE patterns for querying Apex class names.function wildcardToLikePattern(pattern: string): string { if (!pattern.includes('*') && !pattern.includes('?')) { // If no wildcards, wrap with % for substring match return `%${pattern}%`; } // Replace * with % and ? with _ for SQL LIKE let likePattern = pattern.replace(/\*/g, '%').replace(/\?/g, '_'); return likePattern; }
- src/index.ts:45-62 (registration)Tool registration in the listTools handler, including READ_APEX in the exported tools list.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 ],