@coretext-ai/qa-google-contacts-a58500d5-8331-4ce9-a140-d204a9fae815
Version:
MCP server with google-contacts integration
748 lines • 35.7 kB
JavaScript
import { Logger } from '../services/logger.js';
export class GoogleContactsTools {
constructor(client) {
this.initialized = false;
this.client = client;
// Get logger from client if available, otherwise create fallback
this.logger = client.logger || new Logger({
logLevel: 'ERROR',
component: 'tools',
enableConsole: true,
enableShipping: false,
serverName: ''
});
}
async ensureInitialized() {
if (!this.initialized) {
// Log tools initialization now that client is ready
this.logger.info('TOOLS_INIT', 'Tools instance initialization started', {
integration: 'google-contacts',
isOAuth: true
});
this.logger.info('CLIENT_INITIALIZATION', 'Starting client initialization', {
isOAuth: true
});
await this.client.initialize();
this.initialized = true;
this.logger.info('CLIENT_INITIALIZATION', 'Client initialization completed', {
initialized: this.initialized
});
}
}
getToolDefinitions() {
return [
{
name: 'google-contacts_create_contact',
description: 'Create a new contact with specified fields',
inputSchema: {
type: 'object',
properties: {
personFields: {
type: 'string',
description: 'Comma-separated list of fields to return (e.g., 'names,emailAddresses,phoneNumbers,addresses,organizations')'
},
sources: {
type: 'string',
description: 'Sources to create the contact in (default: 'CONTACT')'
},
names: {
type: 'object',
description: 'Array of name objects with givenName, familyName, displayName, etc.'
},
emailAddresses: {
type: 'object',
description: 'Array of email address objects with value, type, etc.'
},
phoneNumbers: {
type: 'object',
description: 'Array of phone number objects with value, type, etc.'
},
addresses: {
type: 'object',
description: 'Array of address objects with formattedValue, type, etc.'
},
organizations: {
type: 'object',
description: 'Array of organization objects with name, title, etc.'
},
biographies: {
type: 'object',
description: 'Array of biography objects with value, contentType, etc.'
},
birthdays: {
type: 'object',
description: 'Array of birthday objects with date information'
},
urls: {
type: 'object',
description: 'Array of URL objects with value, type, etc.'
},
},
required: ['personFields',]
}
},
{
name: 'google-contacts_get_person',
description: 'Get a specific contact by resource name',
inputSchema: {
type: 'object',
properties: {
resourceName: {
type: 'string',
description: 'Resource name of the contact (e.g., 'people/c1234567890')'
},
personFields: {
type: 'string',
description: 'Comma-separated list of fields to return (e.g., 'names,emailAddresses,phoneNumbers,addresses,organizations,biographies,birthdays,urls,photos,metadata')'
},
sources: {
type: 'string',
description: 'Comma-separated list of sources to read from (READ_SOURCE_TYPE_CONTACT, READ_SOURCE_TYPE_DOMAIN_CONTACT)'
},
},
required: ['resourceName', 'personFields',]
}
},
{
name: 'google-contacts_update_contact',
description: 'Update an existing contact',
inputSchema: {
type: 'object',
properties: {
resourceName: {
type: 'string',
description: 'Resource name of the contact (e.g., 'people/c1234567890')'
},
updatePersonFields: {
type: 'string',
description: 'Comma-separated list of fields to update (e.g., 'names,emailAddresses,phoneNumbers')'
},
personFields: {
type: 'string',
description: 'Comma-separated list of fields to return in response'
},
sources: {
type: 'string',
description: 'Sources to update the contact in'
},
names: {
type: 'object',
description: 'Array of name objects to update'
},
emailAddresses: {
type: 'object',
description: 'Array of email address objects to update'
},
phoneNumbers: {
type: 'object',
description: 'Array of phone number objects to update'
},
addresses: {
type: 'object',
description: 'Array of address objects to update'
},
organizations: {
type: 'object',
description: 'Array of organization objects to update'
},
biographies: {
type: 'object',
description: 'Array of biography objects to update'
},
birthdays: {
type: 'object',
description: 'Array of birthday objects to update'
},
urls: {
type: 'object',
description: 'Array of URL objects to update'
},
etag: {
type: 'string',
description: 'ETag for concurrency control'
},
},
required: ['resourceName', 'updatePersonFields',]
}
},
{
name: 'google-contacts_delete_contact',
description: 'Delete a contact permanently',
inputSchema: {
type: 'object',
properties: {
resourceName: {
type: 'string',
description: 'Resource name of the contact to delete (e.g., 'people/c1234567890')'
},
},
required: ['resourceName']
}
},
{
name: 'google-contacts_list_connections',
description: 'List authenticated user's contacts',
inputSchema: {
type: 'object',
properties: {
pageSize: {
type: 'number',
description: 'Number of contacts to return (max 1000, default 100)'
},
pageToken: {
type: 'string',
description: 'Page token for pagination'
},
personFields: {
type: 'string',
description: 'Comma-separated list of fields to return (e.g., 'names,emailAddresses,phoneNumbers,addresses,organizations,biographies,birthdays,urls,photos,metadata')'
},
sortOrder: {
type: 'string',
description: 'Sort order: LAST_MODIFIED_ASCENDING, LAST_MODIFIED_DESCENDING, FIRST_NAME_ASCENDING, LAST_NAME_ASCENDING'
},
sources: {
type: 'string',
description: 'Comma-separated list of sources (READ_SOURCE_TYPE_CONTACT, READ_SOURCE_TYPE_DOMAIN_CONTACT)'
},
syncToken: {
type: 'string',
description: 'Sync token for incremental synchronization'
},
requestSyncToken: {
type: 'boolean',
description: 'Whether to request a sync token in the response'
},
},
required: ['personFields',]
}
},
{
name: 'google-contacts_search_contacts',
description: 'Search across all contacts with text query',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Text search query to match against contact fields'
},
pageSize: {
type: 'number',
description: 'Number of results to return (max 30, default 10)'
},
readMask: {
type: 'string',
description: 'Comma-separated list of fields to return (e.g., 'names,emailAddresses,phoneNumbers')'
},
sources: {
type: 'string',
description: 'Comma-separated list of sources to search (READ_SOURCE_TYPE_CONTACT, READ_SOURCE_TYPE_DOMAIN_CONTACT)'
},
},
required: ['query',]
}
},
{
name: 'google-contacts_batch_get_people',
description: 'Get multiple contacts by resource names',
inputSchema: {
type: 'object',
properties: {
resourceNames: {
type: 'string',
description: 'Comma-separated list of resource names (e.g., 'people/c1,people/c2')'
},
personFields: {
type: 'string',
description: 'Comma-separated list of fields to return for each contact'
},
sources: {
type: 'string',
description: 'Comma-separated list of sources to read from'
},
},
required: ['resourceNames', 'personFields',]
}
},
{
name: 'google-contacts_list_contact_groups',
description: 'List all contact groups',
inputSchema: {
type: 'object',
properties: {
pageSize: {
type: 'number',
description: 'Number of groups to return (max 1000, default 30)'
},
pageToken: {
type: 'string',
description: 'Page token for pagination'
},
groupFields: {
type: 'string',
description: 'Comma-separated list of group fields to return (e.g., 'name,memberCount,groupType')'
},
syncToken: {
type: 'string',
description: 'Sync token for incremental synchronization'
},
},
required: []
}
},
{
name: 'google-contacts_create_contact_group',
description: 'Create a new contact group',
inputSchema: {
type: 'object',
properties: {
contactGroup: {
type: 'object',
description: 'Contact group object with name and optional metadata'
},
readGroupFields: {
type: 'string',
description: 'Comma-separated list of fields to return in response'
},
},
required: ['contactGroup',]
}
},
{
name: 'google-contacts_get_contact_group',
description: 'Get a specific contact group by resource name',
inputSchema: {
type: 'object',
properties: {
resourceName: {
type: 'string',
description: 'Resource name of the contact group (e.g., 'contactGroups/myContactGroup')'
},
maxMembers: {
type: 'number',
description: 'Maximum number of members to return (max 20000, default 0)'
},
groupFields: {
type: 'string',
description: 'Comma-separated list of group fields to return'
},
},
required: ['resourceName',]
}
},
{
name: 'google-contacts_update_contact_group',
description: 'Update an existing contact group',
inputSchema: {
type: 'object',
properties: {
resourceName: {
type: 'string',
description: 'Resource name of the contact group to update'
},
contactGroup: {
type: 'object',
description: 'Updated contact group object'
},
readGroupFields: {
type: 'string',
description: 'Comma-separated list of fields to return in response'
},
},
required: ['resourceName', 'contactGroup',]
}
},
{
name: 'google-contacts_delete_contact_group',
description: 'Delete a contact group',
inputSchema: {
type: 'object',
properties: {
resourceName: {
type: 'string',
description: 'Resource name of the contact group to delete'
},
deleteContacts: {
type: 'boolean',
description: 'Whether to delete the contacts in the group as well'
},
},
required: ['resourceName',]
}
},
{
name: 'google-contacts_modify_contact_group_members',
description: 'Add or remove members from a contact group',
inputSchema: {
type: 'object',
properties: {
resourceName: {
type: 'string',
description: 'Resource name of the contact group'
},
resourceNamesToAdd: {
type: 'string',
description: 'Comma-separated list of contact resource names to add'
},
resourceNamesToRemove: {
type: 'string',
description: 'Comma-separated list of contact resource names to remove'
},
},
required: ['resourceName',]
}
},
{
name: 'google-contacts_list_directory_people',
description: 'List people in the authenticated user's domain directory (G Suite/Workspace)',
inputSchema: {
type: 'object',
properties: {
pageSize: {
type: 'number',
description: 'Number of people to return (max 1000, default 100)'
},
pageToken: {
type: 'string',
description: 'Page token for pagination'
},
readMask: {
type: 'string',
description: 'Comma-separated list of fields to return (e.g., 'names,emailAddresses,organizations')'
},
sources: {
type: 'string',
description: 'Must be 'DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT' for directory people'
},
mergeSources: {
type: 'string',
description: 'Additional sources to merge (e.g., 'DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE')'
},
syncToken: {
type: 'string',
description: 'Sync token for incremental synchronization'
},
requestSyncToken: {
type: 'boolean',
description: 'Whether to request a sync token in the response'
},
},
required: ['readMask', 'sources',]
}
},
{
name: 'google-contacts_search_directory_people',
description: 'Search people in the authenticated user's domain directory',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Text search query to match against directory people'
},
pageSize: {
type: 'number',
description: 'Number of results to return (max 500, default 100)'
},
pageToken: {
type: 'string',
description: 'Page token for pagination'
},
readMask: {
type: 'string',
description: 'Comma-separated list of fields to return'
},
sources: {
type: 'string',
description: 'Must be 'DIRECTORY_SOURCE_TYPE_DOMAIN_CONTACT' for directory search'
},
mergeSources: {
type: 'string',
description: 'Additional sources to merge'
},
},
required: ['query', 'readMask', 'sources',]
}
},
{
name: 'google-contacts_list_other_contacts',
description: 'List other contacts (auto-created contacts from interactions)',
inputSchema: {
type: 'object',
properties: {
pageSize: {
type: 'number',
description: 'Number of other contacts to return (max 1000, default 100)'
},
pageToken: {
type: 'string',
description: 'Page token for pagination'
},
readMask: {
type: 'string',
description: 'Comma-separated list of fields to return (e.g., 'names,emailAddresses')'
},
sources: {
type: 'string',
description: 'Sources to read from (READ_SOURCE_TYPE_OTHER_CONTACT)'
},
syncToken: {
type: 'string',
description: 'Sync token for incremental synchronization'
},
requestSyncToken: {
type: 'boolean',
description: 'Whether to request a sync token in the response'
},
},
required: ['readMask',]
}
},
{
name: 'google-contacts_search_other_contacts',
description: 'Search other contacts with text query',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Text search query to match against other contacts'
},
pageSize: {
type: 'number',
description: 'Number of results to return (max 30, default 10)'
},
readMask: {
type: 'string',
description: 'Comma-separated list of fields to return'
},
},
required: ['query',]
}
},
{
name: 'google-contacts_copy_other_contact',
description: 'Copy an other contact to the authenticated user's contacts',
inputSchema: {
type: 'object',
properties: {
resourceName: {
type: 'string',
description: 'Resource name of the other contact to copy (e.g., 'otherContacts/c1234567890')'
},
copyMask: {
type: 'string',
description: 'Comma-separated list of fields to copy (e.g., 'names,emailAddresses,phoneNumbers')'
},
sources: {
type: 'string',
description: 'Sources to copy from'
},
},
required: ['resourceName', 'copyMask',]
}
}
];
}
canHandle(toolName) {
const supportedTools = [
'google-contacts_create_contact',
'google-contacts_get_person',
'google-contacts_update_contact',
'google-contacts_delete_contact',
'google-contacts_list_connections',
'google-contacts_search_contacts',
'google-contacts_batch_get_people',
'google-contacts_list_contact_groups',
'google-contacts_create_contact_group',
'google-contacts_get_contact_group',
'google-contacts_update_contact_group',
'google-contacts_delete_contact_group',
'google-contacts_modify_contact_group_members',
'google-contacts_list_directory_people',
'google-contacts_search_directory_people',
'google-contacts_list_other_contacts',
'google-contacts_search_other_contacts',
'google-contacts_copy_other_contact'
];
return supportedTools.includes(toolName);
}
async executeTool(name, args) {
const startTime = Date.now();
this.logger.logToolStart(name, args);
await this.ensureInitialized();
// Validate tool is supported
if (!this.canHandle(name)) {
this.logger.error('TOOL_ERROR', 'Unknown tool requested', {
tool: name,
supportedTools: ['google-contacts_create_contact', 'google-contacts_get_person', 'google-contacts_update_contact', 'google-contacts_delete_contact', 'google-contacts_list_connections', 'google-contacts_search_contacts', 'google-contacts_batch_get_people', 'google-contacts_list_contact_groups', 'google-contacts_create_contact_group', 'google-contacts_get_contact_group', 'google-contacts_update_contact_group', 'google-contacts_delete_contact_group', 'google-contacts_modify_contact_group_members', 'google-contacts_list_directory_people', 'google-contacts_search_directory_people', 'google-contacts_list_other_contacts', 'google-contacts_search_other_contacts', 'google-contacts_copy_other_contact']
});
throw new Error(`Unknown tool: ${name}`);
}
// Validate required parameters
this.logger.debug('PARAM_VALIDATION', 'Validating tool parameters', {
tool: name,
providedArgs: Object.keys(args || {})
});
try {
let result;
switch (name) {
case 'google-contacts_create_contact':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_create_contact',
clientMethod: 'createContact'
});
result = await this.client.createContact(args);
break;
case 'google-contacts_get_person':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_get_person',
clientMethod: 'getPerson'
});
result = await this.client.getPerson(args);
break;
case 'google-contacts_update_contact':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_update_contact',
clientMethod: 'updateContact'
});
result = await this.client.updateContact(args);
break;
case 'google-contacts_delete_contact':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_delete_contact',
clientMethod: 'deleteContact'
});
result = await this.client.deleteContact(args);
break;
case 'google-contacts_list_connections':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_list_connections',
clientMethod: 'listConnections'
});
result = await this.client.listConnections(args);
break;
case 'google-contacts_search_contacts':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_search_contacts',
clientMethod: 'searchContacts'
});
result = await this.client.searchContacts(args);
break;
case 'google-contacts_batch_get_people':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_batch_get_people',
clientMethod: 'batchGetPeople'
});
result = await this.client.batchGetPeople(args);
break;
case 'google-contacts_list_contact_groups':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_list_contact_groups',
clientMethod: 'listContactGroups'
});
result = await this.client.listContactGroups(args);
break;
case 'google-contacts_create_contact_group':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_create_contact_group',
clientMethod: 'createContactGroup'
});
result = await this.client.createContactGroup(args);
break;
case 'google-contacts_get_contact_group':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_get_contact_group',
clientMethod: 'getContactGroup'
});
result = await this.client.getContactGroup(args);
break;
case 'google-contacts_update_contact_group':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_update_contact_group',
clientMethod: 'updateContactGroup'
});
result = await this.client.updateContactGroup(args);
break;
case 'google-contacts_delete_contact_group':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_delete_contact_group',
clientMethod: 'deleteContactGroup'
});
result = await this.client.deleteContactGroup(args);
break;
case 'google-contacts_modify_contact_group_members':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_modify_contact_group_members',
clientMethod: 'modifyContactGroupMembers'
});
result = await this.client.modifyContactGroupMembers(args);
break;
case 'google-contacts_list_directory_people':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_list_directory_people',
clientMethod: 'listDirectoryPeople'
});
result = await this.client.listDirectoryPeople(args);
break;
case 'google-contacts_search_directory_people':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_search_directory_people',
clientMethod: 'searchDirectoryPeople'
});
result = await this.client.searchDirectoryPeople(args);
break;
case 'google-contacts_list_other_contacts':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_list_other_contacts',
clientMethod: 'listOtherContacts'
});
result = await this.client.listOtherContacts(args);
break;
case 'google-contacts_search_other_contacts':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_search_other_contacts',
clientMethod: 'searchOtherContacts'
});
result = await this.client.searchOtherContacts(args);
break;
case 'google-contacts_copy_other_contact':
this.logger.debug('TOOL_EXECUTE', 'Calling client method', {
tool: 'google-contacts_copy_other_contact',
clientMethod: 'copyOtherContact'
});
result = await this.client.copyOtherContact(args);
break;
default:
throw new Error(`Unknown tool: ${name}`);
}
const duration = Date.now() - startTime;
this.logger.logToolSuccess(name, duration, result);
// Format the result for MCP (OAuth templates only)
return {
content: [
{
type: 'text',
text: typeof result === 'string' ? result : JSON.stringify(result, null, 2)
}
]
};
}
catch (error) {
const duration = Date.now() - startTime;
const errorMessage = error instanceof Error ? error.message : String(error);
this.logger.logToolError(name, error, duration, args);
return {
content: [
{
type: 'text',
text: `Error: ${errorMessage}`
}
],
isError: true
};
}
}
}
//# sourceMappingURL=google-contacts-tools.js.map