UNPKG

@coretext-ai/qa-gsuite-f47ac10b-58cc-4372-a567-0e02b2c3d479

Version:

MCP server with GSuite (Contacts, Drive, Gmail, Calendar) integration

515 lines 19.4 kB
import { GoogleOAuthClient } from '../oauth/google-oauth-client.js'; export class GoogleCalendarClient { constructor() { this.baseUrl = 'https://www.googleapis.com/calendar/v3'; // Generate unique session ID for this client instance this.sessionId = `google-calendar-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`; this.logDebug('INIT', 'OAuth client instance created', { baseUrl: this.baseUrl, isOAuth: true }); this.oauthClient = new GoogleOAuthClient(); } /** * Get the session ID for this client instance */ getSessionId() { return this.sessionId; } /** * Enhanced debug logging with session context */ logDebug(action, message, metadata) { const timestamp = new Date().toISOString(); const user = process.env.CORETEXT_USER || 'unknown'; const logEntry = { timestamp, sessionId: this.sessionId, user, integration: 'google-calendar', component: 'oauth-client', action, message, ...(metadata && { metadata }) }; // Use stderr to avoid MCP protocol interference console.error(`[GOOGLE_CALENDAR-OAUTH-CLIENT] ${JSON.stringify(logEntry)}`); } /** * Initialize the API client */ async initialize() { this.logDebug('INITIALIZE', 'Starting OAuth client initialization', { hasOAuthClient: true }); await this.oauthClient.initialize(); this.logDebug('INITIALIZE', 'Google OAuth client initialization completed'); } /** * Make authenticated API request */ async makeRequest(config) { const startTime = Date.now(); this.logDebug('REQUEST_START', 'Making authenticated API request', { method: config.method, path: config.path, hasBody: !!config.body, hasQueryParams: !!config.queryParams }); const accessToken = await this.oauthClient.getValidAccessToken(); this.logDebug('AUTH_TOKEN', 'Retrieved Google OAuth access token', { tokenPreview: accessToken ? accessToken.substring(0, 8) + '...' : 'none' }); const headers = { 'Accept': 'application/json', 'Authorization': `Bearer ${accessToken}`, 'Content-Type': 'application/json', ...config.headers }; const url = this.buildUrl(config.path, config.pathParams, config.queryParams); const response = await fetch(url, { method: config.method, headers, body: config.body ? JSON.stringify(config.body) : undefined }); if (!response.ok) { if (response.status === 401) { // Token might be invalid, try to refresh and retry once await this.oauthClient.getValidAccessToken(); // This will refresh if needed return this.makeRequest(config); // Retry once } const errorText = await response.text(); throw new Error(`API request failed: ${response.status} ${response.statusText} - ${errorText}`); } return response.json(); } /** * Returns the calendars on the user's calendar list */ async listCalendars(args = {}) { const pathParams = {}; const queryParams = { ...(args.maxResults !== undefined && { maxResults: args.maxResults }), ...(args.minAccessRole !== undefined && { minAccessRole: args.minAccessRole }), ...(args.pageToken !== undefined && { pageToken: args.pageToken }), ...(args.showDeleted !== undefined && { showDeleted: args.showDeleted }), ...(args.showHidden !== undefined && { showHidden: args.showHidden }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/users/me/calendarList', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Returns metadata for a calendar */ async getCalendar(args = {}) { const pathParams = { calendarId: args.calendarId, }; const queryParams = {}; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/calendars/{calendarId}', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Creates a secondary calendar */ async createCalendar(args = {}) { const pathParams = {}; const queryParams = {}; const bodyParams = { summary: args.summary, ...(args.description !== undefined && { description: args.description }), ...(args.location !== undefined && { location: args.location }), ...(args.timeZone !== undefined && { timeZone: args.timeZone }), }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'POST', path: '/calendars', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Updates metadata for a calendar */ async updateCalendar(args = {}) { const pathParams = { calendarId: args.calendarId, }; const queryParams = {}; const bodyParams = { ...(args.summary !== undefined && { summary: args.summary }), ...(args.description !== undefined && { description: args.description }), ...(args.location !== undefined && { location: args.location }), ...(args.timeZone !== undefined && { timeZone: args.timeZone }), }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'PUT', path: '/calendars/{calendarId}', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Deletes a secondary calendar */ async deleteCalendar(args = {}) { const pathParams = { calendarId: args.calendarId, }; const queryParams = {}; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'DELETE', path: '/calendars/{calendarId}', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Returns events on the specified calendar */ async listEvents(args = {}) { const pathParams = { calendarId: args.calendarId, }; const queryParams = { ...(args.maxResults !== undefined && { maxResults: args.maxResults }), ...(args.orderBy !== undefined && { orderBy: args.orderBy }), ...(args.pageToken !== undefined && { pageToken: args.pageToken }), ...(args.q !== undefined && { q: args.q }), ...(args.showDeleted !== undefined && { showDeleted: args.showDeleted }), ...(args.singleEvents !== undefined && { singleEvents: args.singleEvents }), ...(args.timeMax !== undefined && { timeMax: args.timeMax }), ...(args.timeMin !== undefined && { timeMin: args.timeMin }), ...(args.timeZone !== undefined && { timeZone: args.timeZone }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/calendars/{calendarId}/events', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Returns an event */ async getEvent(args = {}) { const pathParams = { calendarId: args.calendarId, eventId: args.eventId, }; const queryParams = { ...(args.timeZone !== undefined && { timeZone: args.timeZone }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/calendars/{calendarId}/events/{eventId}', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Creates an event */ async createEvent(args = {}) { const pathParams = { calendarId: args.calendarId, }; const queryParams = { ...(args.sendNotifications !== undefined && { sendNotifications: args.sendNotifications }), ...(args.sendUpdates !== undefined && { sendUpdates: args.sendUpdates }), }; const bodyParams = { summary: args.summary, ...(args.description !== undefined && { description: args.description }), ...(args.location !== undefined && { location: args.location }), start: args.start, end: args.end, ...(args.attendees !== undefined && { attendees: args.attendees }), ...(args.reminders !== undefined && { reminders: args.reminders }), ...(args.recurrence !== undefined && { recurrence: args.recurrence }), }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'POST', path: '/calendars/{calendarId}/events', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Updates an event */ async updateEvent(args = {}) { const pathParams = { calendarId: args.calendarId, eventId: args.eventId, }; const queryParams = { ...(args.sendNotifications !== undefined && { sendNotifications: args.sendNotifications }), ...(args.sendUpdates !== undefined && { sendUpdates: args.sendUpdates }), }; const bodyParams = { ...(args.summary !== undefined && { summary: args.summary }), ...(args.description !== undefined && { description: args.description }), ...(args.location !== undefined && { location: args.location }), ...(args.start !== undefined && { start: args.start }), ...(args.end !== undefined && { end: args.end }), ...(args.attendees !== undefined && { attendees: args.attendees }), }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'PUT', path: '/calendars/{calendarId}/events/{eventId}', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Deletes an event */ async deleteEvent(args = {}) { const pathParams = { calendarId: args.calendarId, eventId: args.eventId, }; const queryParams = { ...(args.sendNotifications !== undefined && { sendNotifications: args.sendNotifications }), ...(args.sendUpdates !== undefined && { sendUpdates: args.sendUpdates }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'DELETE', path: '/calendars/{calendarId}/events/{eventId}', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Creates an event based on a simple text string */ async quickAdd(args = {}) { const pathParams = { calendarId: args.calendarId, }; const queryParams = { text: args.text, ...(args.sendNotifications !== undefined && { sendNotifications: args.sendNotifications }), ...(args.sendUpdates !== undefined && { sendUpdates: args.sendUpdates }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'POST', path: '/calendars/{calendarId}/events/quickAdd', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Returns free/busy information for a set of calendars */ async getFreeBusy(args = {}) { const pathParams = {}; const queryParams = {}; const bodyParams = { timeMin: args.timeMin, timeMax: args.timeMax, ...(args.timeZone !== undefined && { timeZone: args.timeZone }), items: args.items, }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'POST', path: '/freeBusy', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Returns the rules in the access control list for the calendar */ async listAcl(args = {}) { const pathParams = { calendarId: args.calendarId, }; const queryParams = { ...(args.maxResults !== undefined && { maxResults: args.maxResults }), ...(args.pageToken !== undefined && { pageToken: args.pageToken }), ...(args.showDeleted !== undefined && { showDeleted: args.showDeleted }), }; const bodyParams = {}; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'GET', path: '/calendars/{calendarId}/acl', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Creates an access control rule */ async createAcl(args = {}) { const pathParams = { calendarId: args.calendarId, }; const queryParams = { ...(args.sendNotifications !== undefined && { sendNotifications: args.sendNotifications }), }; const bodyParams = { role: args.role, scope: args.scope, }; // Remove undefined values from pathParams Object.keys(pathParams).forEach(key => { if (pathParams[key] === undefined) { delete pathParams[key]; } }); return this.makeRequest({ method: 'POST', path: '/calendars/{calendarId}/acl', pathParams, queryParams: Object.keys(queryParams).length ? queryParams : undefined, body: Object.keys(bodyParams).length ? bodyParams : undefined }); } /** * Build URL with path parameters and query string */ buildUrl(path, pathParams, queryParams) { let url = this.baseUrl + path; // Replace path parameters if (pathParams) { for (const [key, value] of Object.entries(pathParams)) { url = url.replace(`{${key}}`, encodeURIComponent(String(value))); } } // Add query parameters if (queryParams && Object.keys(queryParams).length > 0) { const searchParams = new URLSearchParams(); for (const [key, value] of Object.entries(queryParams)) { if (value !== undefined && value !== null) { searchParams.append(key, String(value)); } } const queryString = searchParams.toString(); if (queryString) { url += `?${queryString}`; } } return url; } /** * Check if client is authenticated */ async isAuthenticated() { return this.oauthClient.isAuthenticated(); } /** * Revoke authentication tokens */ async revokeAuthentication() { await this.oauthClient.revokeTokens(); } } //# sourceMappingURL=google-calendar-client.js.map