@pablosr/mcp-google-calendar 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +1 -1
- package/dist/tools/calendar/calendar-create-event.js +3 -3
- package/dist/tools/calendar/calendar-list-events.js +6 -3
- package/dist/tools/calendar/calendar-update-event.js +8 -5
- package/dist/tools/google-calendar-client.js +7 -3
- package/dist/tools/utils/date-validation.js +45 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { GoogleCalendarClient } from "./tools/google-calendar-client.js";
|
|
|
6
6
|
import { registerGoogleCalendarTools } from "./tools/google-calendar-mcp.js";
|
|
7
7
|
const server = new McpServer({
|
|
8
8
|
name: "mcp-google-calendar",
|
|
9
|
-
version: "1.1.
|
|
9
|
+
version: "1.1.2",
|
|
10
10
|
});
|
|
11
11
|
async function main() {
|
|
12
12
|
console.error('Configurando Google Calendar API...');
|
|
@@ -4,12 +4,12 @@ export function registerGoogleCreateEvent(googleClient, server) {
|
|
|
4
4
|
server.tool('calendar_create_event', 'Crea un nuevo evento en Google Calendar', {
|
|
5
5
|
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
6
6
|
title: z.string().describe('Título del evento'),
|
|
7
|
-
start: z.string().describe('Fecha/hora de inicio en formato ISO (ej: 2023-12-01T10:00:00Z)'),
|
|
8
|
-
end: z.string().describe('Fecha/hora de fin en formato ISO'),
|
|
7
|
+
start: z.string().describe('Fecha/hora de inicio en formato ISO 8601 Zulu time (ej: 2023-12-01T10:00:00Z).'),
|
|
8
|
+
end: z.string().describe('Fecha/hora de fin en formato ISO 8601 Zulu time (ej: 2023-12-01T11:00:00Z).'),
|
|
9
9
|
description: z.string().optional().describe('Descripción del evento (opcional)'),
|
|
10
10
|
location: z.string().optional().describe('Ubicación del evento (opcional)'),
|
|
11
11
|
attendees: z.array(z.string().email()).optional().describe('Lista de emails de los invitados (opcional)'),
|
|
12
|
-
timeZone: z.string().optional().describe('Zona horaria en formato IANA (ej: America/New_York, Atlantic/Canary). Por defecto usa DEFAULT_TIMEZONE del .env'),
|
|
12
|
+
timeZone: z.string().optional().describe('Zona horaria en formato IANA (ej: America/New_York, Atlantic/Canary). Por defecto usa DEFAULT_TIMEZONE del .env. Esta zona se usa para interpretar la hora Zulu en el calendario'),
|
|
13
13
|
}, async (args) => {
|
|
14
14
|
try {
|
|
15
15
|
const { calendarId = 'primary', title, start, end, description, location, attendees, timeZone, } = args;
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { validateAndNormalizeDate } from '../utils/date-validation.js';
|
|
2
3
|
export function registerGoogleListEvents(googleClient, server) {
|
|
3
4
|
server.tool('calendar_list_events', 'Lista eventos de un calendario de Google Calendar', {
|
|
4
5
|
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
5
|
-
timeMin: z.string().optional().describe('Fecha/hora mínima en formato ISO (ej: 2023-12-01T00:00:00Z)'),
|
|
6
|
-
timeMax: z.string().optional().describe('Fecha/hora máxima en formato ISO'),
|
|
6
|
+
timeMin: z.string().optional().describe('Fecha/hora mínima en formato ISO 8601 Zulu time (ej: 2023-12-01T00:00:00Z).'),
|
|
7
|
+
timeMax: z.string().optional().describe('Fecha/hora máxima en formato ISO 8601 Zulu time (ej: 2023-12-31T23:59:59Z).'),
|
|
7
8
|
maxResults: z.number().optional().describe('Número máximo de resultados (por defecto: 10)'),
|
|
8
9
|
}, async (args) => {
|
|
9
10
|
try {
|
|
10
11
|
const { calendarId = 'primary', timeMin, timeMax, maxResults = 10, } = args;
|
|
11
|
-
const
|
|
12
|
+
const normalizedTimeMin = timeMin ? validateAndNormalizeDate(timeMin) : undefined;
|
|
13
|
+
const normalizedTimeMax = timeMax ? validateAndNormalizeDate(timeMax) : undefined;
|
|
14
|
+
const events = await googleClient.listEvents(calendarId, normalizedTimeMin, normalizedTimeMax, maxResults);
|
|
12
15
|
const data = events.map((event) => ({
|
|
13
16
|
id: event.id,
|
|
14
17
|
summary: event.summary,
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { validateAndNormalizeDate } from '../utils/date-validation.js';
|
|
2
3
|
export function registerGoogleUpdateEvent(googleClient, server) {
|
|
3
4
|
server.tool('calendar_update_event', 'Actualiza un evento existente en Google Calendar', {
|
|
4
5
|
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
5
6
|
eventId: z.string().describe('ID del evento a actualizar'),
|
|
6
7
|
title: z.string().optional().describe('Nuevo título del evento'),
|
|
7
|
-
start: z.string().optional().describe('Nueva fecha/hora de inicio en formato ISO (ej: 2023-12-01T10:00:00Z)'),
|
|
8
|
-
end: z.string().optional().describe('Nueva fecha/hora de fin en formato ISO'),
|
|
8
|
+
start: z.string().optional().describe('Nueva fecha/hora de inicio en formato ISO 8601 Zulu time (ej: 2023-12-01T10:00:00Z).'),
|
|
9
|
+
end: z.string().optional().describe('Nueva fecha/hora de fin en formato ISO 8601 Zulu time (ej: 2023-12-01T11:00:00Z).'),
|
|
9
10
|
description: z.string().optional().describe('Nueva descripción del evento'),
|
|
10
11
|
location: z.string().optional().describe('Nueva ubicación del evento'),
|
|
11
12
|
attendees: z.array(z.string().email()).optional().describe('Lista de emails de los invitados (opcional)'),
|
|
12
|
-
timeZone: z.string().optional().describe('Zona horaria en formato IANA (ej: America/New_York, Atlantic/Canary). Por defecto usa DEFAULT_TIMEZONE del .env'),
|
|
13
|
+
timeZone: z.string().optional().describe('Zona horaria en formato IANA (ej: America/New_York, Atlantic/Canary). Por defecto usa DEFAULT_TIMEZONE del .env. Esta zona se usa para interpretar la hora Zulu en el calendario'),
|
|
13
14
|
}, async (args) => {
|
|
14
15
|
try {
|
|
15
16
|
const { calendarId = 'primary', eventId, title, start, end, description, location, attendees, timeZone, } = args;
|
|
@@ -37,14 +38,16 @@ export function registerGoogleUpdateEvent(googleClient, server) {
|
|
|
37
38
|
}
|
|
38
39
|
const defaultTimeZone = timeZone || process.env.DEFAULT_TIMEZONE || 'Atlantic/Canary';
|
|
39
40
|
if (start !== undefined) {
|
|
41
|
+
const normalizedStart = validateAndNormalizeDate(start);
|
|
40
42
|
event.start = {
|
|
41
|
-
dateTime:
|
|
43
|
+
dateTime: normalizedStart,
|
|
42
44
|
timeZone: defaultTimeZone,
|
|
43
45
|
};
|
|
44
46
|
}
|
|
45
47
|
if (end !== undefined) {
|
|
48
|
+
const normalizedEnd = validateAndNormalizeDate(end);
|
|
46
49
|
event.end = {
|
|
47
|
-
dateTime:
|
|
50
|
+
dateTime: normalizedEnd,
|
|
48
51
|
timeZone: defaultTimeZone,
|
|
49
52
|
};
|
|
50
53
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { google } from 'googleapis';
|
|
2
|
+
import { validateAndNormalizeDate } from './utils/date-validation.js';
|
|
2
3
|
export class GoogleCalendarClient {
|
|
3
4
|
auth;
|
|
4
5
|
calendar;
|
|
@@ -89,7 +90,7 @@ export class GoogleCalendarClient {
|
|
|
89
90
|
*/
|
|
90
91
|
async updateEvent(calendarId = 'primary', eventId, event) {
|
|
91
92
|
try {
|
|
92
|
-
const response = await this.calendar.events.
|
|
93
|
+
const response = await this.calendar.events.patch({
|
|
93
94
|
calendarId,
|
|
94
95
|
eventId,
|
|
95
96
|
requestBody: event,
|
|
@@ -237,19 +238,22 @@ export class GoogleCalendarClient {
|
|
|
237
238
|
}
|
|
238
239
|
/**
|
|
239
240
|
* Función helper para crear eventos con formato simplificado
|
|
241
|
+
* Las fechas se normalizan a formato Zulu time (ISO 8601 UTC sin offset)
|
|
240
242
|
*/
|
|
241
243
|
export function createSimpleEvent(title, start, end, description, location, attendees, timeZone) {
|
|
242
244
|
const defaultTimeZone = timeZone || process.env.DEFAULT_TIMEZONE || 'Atlantic/Canary';
|
|
245
|
+
const normalizedStart = validateAndNormalizeDate(start);
|
|
246
|
+
const normalizedEnd = validateAndNormalizeDate(end);
|
|
243
247
|
const event = {
|
|
244
248
|
summary: title,
|
|
245
249
|
description,
|
|
246
250
|
location,
|
|
247
251
|
start: {
|
|
248
|
-
dateTime:
|
|
252
|
+
dateTime: normalizedStart,
|
|
249
253
|
timeZone: defaultTimeZone,
|
|
250
254
|
},
|
|
251
255
|
end: {
|
|
252
|
-
dateTime:
|
|
256
|
+
dateTime: normalizedEnd,
|
|
253
257
|
timeZone: defaultTimeZone,
|
|
254
258
|
},
|
|
255
259
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Valida si una fecha está en formato ISO 8601 Zulu time (sin offset)
|
|
3
|
+
* Formato esperado: YYYY-MM-DDTHH:mm:ss.sssZ o YYYY-MM-DDTHH:mm:ssZ
|
|
4
|
+
* @param dateString - String de fecha a validar
|
|
5
|
+
* @returns true si está en formato Zulu time, false en caso contrario
|
|
6
|
+
*/
|
|
7
|
+
export function isZuluTime(dateString) {
|
|
8
|
+
const zuluTimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d{3})?Z$/;
|
|
9
|
+
return zuluTimeRegex.test(dateString);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Convierte cualquier fecha ISO 8601 a Zulu time (UTC sin offset)
|
|
13
|
+
* Si la fecha ya está en Zulu time, la devuelve sin cambios
|
|
14
|
+
* Si la fecha tiene offset, lo elimina y convierte a UTC
|
|
15
|
+
* @param dateString - String de fecha en formato ISO 8601
|
|
16
|
+
* @returns String de fecha en formato Zulu time
|
|
17
|
+
* @throws Error si la fecha no es válida
|
|
18
|
+
*/
|
|
19
|
+
export function convertToZuluTime(dateString) {
|
|
20
|
+
if (isZuluTime(dateString)) {
|
|
21
|
+
return dateString;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
const date = new Date(dateString);
|
|
25
|
+
if (isNaN(date.getTime())) {
|
|
26
|
+
throw new Error(`Fecha inválida: ${dateString}`);
|
|
27
|
+
}
|
|
28
|
+
return date.toISOString();
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
throw new Error(`Error al convertir fecha a Zulu time: ${dateString}. ${error instanceof Error ? error.message : 'Error desconocido'}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Valida y normaliza una fecha a formato Zulu time
|
|
36
|
+
* @param dateString - String de fecha a validar y normalizar
|
|
37
|
+
* @returns String de fecha en formato Zulu time
|
|
38
|
+
* @throws Error si la fecha no es válida
|
|
39
|
+
*/
|
|
40
|
+
export function validateAndNormalizeDate(dateString) {
|
|
41
|
+
if (!dateString || typeof dateString !== 'string') {
|
|
42
|
+
throw new Error('La fecha debe ser un string no vacío');
|
|
43
|
+
}
|
|
44
|
+
return convertToZuluTime(dateString);
|
|
45
|
+
}
|
package/package.json
CHANGED