import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { z } from "zod"; import { sfQuery } from "../salesforce-client.js"; export function registerContactTools(server: McpServer): void { server.tool( "search_contacts", "Search for contacts in Salesforce by name, email, or company", { query: z.string().describe("Search query (name, email, or company)"), limit: z.number().default(10).describe("Maximum number of results"), }, async ({ query, limit }) => { const escapedQuery = query.replace(/'/g, "\\'"); const soql = ` SELECT Id, Name, Email, Phone, Title, Account.Name, Account.Strategic_Priority__c, LastActivityDate FROM Contact WHERE Name LIKE '%${escapedQuery}%' OR Email LIKE '%${escapedQuery}%' OR Account.Name LIKE '%${escapedQuery}%' ORDER BY LastActivityDate DESC NULLS LAST LIMIT ${limit} `; const records = await sfQuery(soql); return { content: [{ type: "text", text: JSON.stringify(records, null, 2) }], }; } ); server.tool( "get_strategic_contacts", "Get contacts from strategic priority accounts that need follow-up", { priority: z .enum(["A", "B", "C", "D"]) .default("D") .describe("Strategic priority filter"), daysInactive: z.number().default(90).describe("Days since last activity"), limit: z.number().default(20).describe("Maximum number of results"), }, async ({ priority, daysInactive, limit }) => { const cutoffDate = new Date( Date.now() - daysInactive * 24 * 60 * 60 * 1000 ) .toISOString() .split("T")[0]; const soql = ` SELECT Id, Name, Title, Email, Account.Name, Account.Strategic_Priority__c, Account.Technology__c, Account.Description, Account.LastActivityDate FROM Contact WHERE Account.Type = 'Prospect' AND Account.Strategic_Priority__c = '${priority}' AND Email != null AND (Account.LastActivityDate < ${cutoffDate} OR Account.LastActivityDate = null) LIMIT ${limit} `; const records = await sfQuery(soql); return { content: [{ type: "text", text: JSON.stringify(records, null, 2) }], }; } ); server.tool( "get_contact_activities", "Get recent activities (events and tasks) for a specific contact", { contactId: z.string().describe("Salesforce Contact ID"), daysBack: z.number().default(90).describe("Number of days to look back"), }, async ({ contactId, daysBack }) => { const cutoffDate = new Date( Date.now() - daysBack * 24 * 60 * 60 * 1000 ).toISOString(); const eventsSoql = ` SELECT Id, Subject, Description, ActivityDateTime FROM Event WHERE WhoId = '${contactId}' AND ActivityDateTime > ${cutoffDate} ORDER BY ActivityDateTime DESC `; const tasksSoql = ` SELECT Id, Subject, Description, ActivityDate, Status FROM Task WHERE WhoId = '${contactId}' AND ActivityDate > ${cutoffDate.split("T")[0]} ORDER BY ActivityDate DESC `; const [events, tasks] = await Promise.all([ sfQuery(eventsSoql), sfQuery(tasksSoql), ]); return { content: [ { type: "text", text: JSON.stringify({ events, tasks }, null, 2), }, ], }; } ); server.tool( "get_comprehensive_contact", "Get comprehensive contact information including account details and recent activities", { contactId: z.string().describe("Salesforce Contact ID"), daysBack: z .number() .default(90) .describe("Number of days to look back for activities"), }, async ({ contactId, daysBack }) => { const contactSoql = ` SELECT Id, Name, Title, Email, Phone, Description, Account.Name, Account.Strategic_Priority__c, Account.Technology__c, Account.Description, Account.Industry, Account.Website FROM Contact WHERE Id = '${contactId}' `; const contacts = await sfQuery(contactSoql); const contact = contacts[0]; if (!contact) { return { content: [ { type: "text", text: `No contact found with ID: ${contactId}` }, ], isError: true, }; } const cutoffDate = new Date( Date.now() - daysBack * 24 * 60 * 60 * 1000 ).toISOString(); const eventsSoql = ` SELECT Id, Subject, Description, ActivityDateTime FROM Event WHERE WhoId = '${contactId}' AND ActivityDateTime > ${cutoffDate} ORDER BY ActivityDateTime DESC LIMIT 20 `; const tasksSoql = ` SELECT Id, Subject, Description, ActivityDate, Status FROM Task WHERE WhoId = '${contactId}' AND ActivityDate > ${cutoffDate.split("T")[0]} ORDER BY ActivityDate DESC LIMIT 20 `; const [events, tasks] = await Promise.all([ sfQuery(eventsSoql), sfQuery(tasksSoql), ]); return { content: [ { type: "text", text: JSON.stringify({ contact, events, tasks }, null, 2), }, ], }; } ); }