twenty-mcp-server
Version:
Easy-to-install Model Context Protocol server for Twenty CRM. Try instantly with 'npx twenty-mcp-server setup' or install globally for permanent use.
320 lines • 13.8 kB
JavaScript
export function createGetCompanyContactsTool(client) {
return {
name: 'get_company_contacts',
description: 'Get all contacts (people) associated with a specific company',
inputSchema: {
type: 'object',
properties: {
companyId: {
type: 'string',
description: 'The ID of the company to get contacts for'
}
},
required: ['companyId']
},
handler: async (args) => {
try {
const result = await client.getCompanyContacts(args.companyId);
const contactsList = result.contacts.map(contact => `• ${contact.name.firstName} ${contact.name.lastName}` +
(contact.jobTitle ? ` - ${contact.jobTitle}` : '') +
(contact.email ? ` (${contact.email})` : '') +
(contact.phone ? ` | Phone: ${contact.phone}` : '') +
`\n ID: ${contact.id}`).join('\n');
return {
content: [{
type: 'text',
text: `Company Contacts for "${result.companyName}"\n` +
`Company ID: ${result.companyId}\n` +
`Total Contacts: ${result.totalContacts}\n\n` +
(result.totalContacts > 0 ? `Contacts:\n${contactsList}` : 'No contacts found for this company.')
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error retrieving company contacts: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
};
}
export function createGetPersonOpportunitiesTool(client) {
return {
name: 'get_person_opportunities',
description: 'Get all opportunities where a specific person is the point of contact',
inputSchema: {
type: 'object',
properties: {
personId: {
type: 'string',
description: 'The ID of the person to get opportunities for'
}
},
required: ['personId']
},
handler: async (args) => {
try {
const result = await client.getPersonOpportunities(args.personId);
const opportunitiesList = result.opportunities.map(opp => {
let oppText = `• ${opp.name}`;
if (opp.stage)
oppText += ` (${opp.stage})`;
if (opp.amount) {
const amount = opp.amount.amountMicros / 1000000;
oppText += ` - ${opp.amount.currencyCode} ${amount.toLocaleString()}`;
}
if (opp.company)
oppText += ` | Company: ${opp.company.name}`;
if (opp.closeDate)
oppText += ` | Close: ${opp.closeDate}`;
oppText += `\n ID: ${opp.id}`;
return oppText;
}).join('\n');
return {
content: [{
type: 'text',
text: `Opportunities for "${result.personName}"\n` +
`Person ID: ${result.personId}\n` +
`Total Opportunities: ${result.totalOpportunities}\n\n` +
(result.totalOpportunities > 0 ? `Opportunities:\n${opportunitiesList}` : 'No opportunities found for this person.')
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error retrieving person opportunities: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
};
}
export function createLinkOpportunityTool(client) {
return {
name: 'link_opportunity_to_company',
description: 'Link an opportunity to a company and/or point of contact',
inputSchema: {
type: 'object',
properties: {
opportunityId: {
type: 'string',
description: 'The ID of the opportunity to update'
},
companyId: {
type: 'string',
description: 'The ID of the company to link to (optional)'
},
pointOfContactId: {
type: 'string',
description: 'The ID of the person to set as point of contact (optional)'
}
},
required: ['opportunityId']
},
handler: async (args) => {
try {
if (!args.companyId && !args.pointOfContactId) {
return {
content: [{
type: 'text',
text: 'Error: At least one of companyId or pointOfContactId must be provided'
}],
isError: true
};
}
const result = await client.linkOpportunityToCompany({
opportunityId: args.opportunityId,
companyId: args.companyId,
pointOfContactId: args.pointOfContactId
});
let relationshipInfo = '';
if (result.company) {
relationshipInfo += `Company: ${result.company.name} (${result.company.id})\n`;
}
if (result.pointOfContact) {
relationshipInfo += `Point of Contact: ${result.pointOfContact.name.firstName} ${result.pointOfContact.name.lastName} (${result.pointOfContact.id})\n`;
}
return {
content: [{
type: 'text',
text: `Successfully linked opportunity "${result.name}"\n` +
`Opportunity ID: ${result.id}\n\n` +
`Updated Relationships:\n${relationshipInfo}`
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error linking opportunity: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
};
}
export function createTransferContactTool(client) {
return {
name: 'transfer_contact_to_company',
description: 'Transfer a contact (person) from one company to another',
inputSchema: {
type: 'object',
properties: {
contactId: {
type: 'string',
description: 'The ID of the contact to transfer'
},
toCompanyId: {
type: 'string',
description: 'The ID of the company to transfer the contact to'
},
fromCompanyId: {
type: 'string',
description: 'The ID of the current company (optional, for validation)'
}
},
required: ['contactId', 'toCompanyId']
},
handler: async (args) => {
try {
const result = await client.transferContactToCompany({
contactId: args.contactId,
fromCompanyId: args.fromCompanyId,
toCompanyId: args.toCompanyId
});
return {
content: [{
type: 'text',
text: `Successfully transferred contact "${result.name.firstName} ${result.name.lastName}"\n` +
`Contact ID: ${result.id}\n` +
`New Company: ${result.company?.name || 'Unknown'} (${result.companyId})`
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error transferring contact: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
};
}
export function createGetRelationshipSummaryTool(client) {
return {
name: 'get_relationship_summary',
description: 'Get a summary of all relationships for a specific entity (company or person)',
inputSchema: {
type: 'object',
properties: {
entityId: {
type: 'string',
description: 'The ID of the entity to get relationship summary for'
},
entityType: {
type: 'string',
enum: ['company', 'person'],
description: 'The type of entity (company or person)'
}
},
required: ['entityId', 'entityType']
},
handler: async (args) => {
try {
const result = await client.getRelationshipSummary(args.entityId, args.entityType);
return {
content: [{
type: 'text',
text: `Relationship Summary for ${args.entityType}: ${args.entityId}\n\n` +
`Connected Relationships:\n` +
`• Companies: ${result.relationships.companies}\n` +
`• Contacts: ${result.relationships.contacts}\n` +
`• Opportunities: ${result.relationships.opportunities}\n` +
`• Tasks: ${result.relationships.tasks}\n` +
`• Activities: ${result.relationships.activities}`
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error getting relationship summary: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
};
}
export function createFindOrphanedRecordsTool(client) {
return {
name: 'find_orphaned_records',
description: 'Find records that are missing important relationships (companies without contacts, contacts without companies, etc.)',
inputSchema: {
type: 'object',
properties: {},
required: []
},
handler: async (args) => {
try {
const result = await client.findOrphanedRecords();
let report = 'Orphaned Records Report\n====================\n\n';
// Companies without contacts
if (result.companies.length > 0) {
report += `Companies without contacts (${result.companies.length}):\n`;
result.companies.forEach(company => {
report += `• ${company.name} (${company.opportunityCount} opportunities)\n ID: ${company.id}\n`;
});
report += '\n';
}
// Contacts without companies
if (result.contacts.length > 0) {
report += `Contacts without companies (${result.contacts.length}):\n`;
result.contacts.forEach(contact => {
report += `• ${contact.name} (${contact.opportunityCount} opportunities)\n ID: ${contact.id}\n`;
});
report += '\n';
}
// Summary
report += `Summary:\n`;
report += `• ${result.companies.length} companies without contacts\n`;
report += `• ${result.contacts.length} contacts without companies\n`;
report += `• ${result.opportunities.length} opportunities with missing relationships\n`;
report += `• ${result.tasks.length} tasks without assignees`;
if (result.companies.length === 0 && result.contacts.length === 0 &&
result.opportunities.length === 0 && result.tasks.length === 0) {
report += '\n\n✅ No orphaned records found! All records have proper relationships.';
}
return {
content: [{
type: 'text',
text: report
}]
};
}
catch (error) {
return {
content: [{
type: 'text',
text: `Error finding orphaned records: ${error instanceof Error ? error.message : 'Unknown error'}`
}],
isError: true
};
}
}
};
}
//# sourceMappingURL=relationships.js.map