@pablosr/mcp-google-calendar 1.0.2 → 1.1.0
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/README.md +132 -30
- package/dist/index.js +2 -3
- package/dist/tools/{google-create-event.js → calendar/calendar-create-event.js} +11 -5
- package/dist/tools/{google-delete-event.js → calendar/calendar-delete-event.js} +1 -1
- package/dist/tools/{google-get-current-datetime.js → calendar/calendar-get-current-datetime.js} +4 -4
- package/dist/tools/{google-list-calendars.js → calendar/calendar-list-calendars.js} +1 -1
- package/dist/tools/{google-list-events.js → calendar/calendar-list-events.js} +1 -1
- package/dist/tools/{google-search-events.js → calendar/calendar-search-events.js} +1 -1
- package/dist/tools/{google-update-event.js → calendar/calendar-update-event.js} +18 -5
- package/dist/tools/{google-calendar.js → google-calendar-client.js} +116 -6
- package/dist/tools/google-calendar-mcp.js +19 -7
- package/dist/tools/tasks/tasks-create-task.js +47 -0
- package/dist/tools/tasks/tasks-delete-task.js +32 -0
- package/dist/tools/tasks/tasks-list-task-lists.js +32 -0
- package/dist/tools/tasks/tasks-list-tasks.js +43 -0
- package/dist/tools/tasks/tasks-update-task.js +57 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# mcp-google-calendar
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
A Google Calendar (MCP) server to expose calendar operations as tools for LLM.
|
|
4
|
+
A Google Calendar and Google Tasks (MCP) server to expose calendar and task operations as tools for LLM.
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
@@ -22,27 +22,35 @@ A Google Calendar (MCP) server to expose calendar operations as tools for LLM.
|
|
|
22
22
|
- [MCP Client Configuration](#mcp-client-configuration)
|
|
23
23
|
- [Usage](#usage)
|
|
24
24
|
- [Testing with MCP Inspector](#testing-with-mcp-inspector)
|
|
25
|
+
- [Timezone Configuration](#timezone-configuration)
|
|
25
26
|
- [Security](#security)
|
|
26
27
|
- [Additional Resources](#additional-resources)
|
|
27
28
|
- [Available Tools](#available-tools)
|
|
28
|
-
- [
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
- [Calendar Tools](#calendar-tools)
|
|
30
|
+
- [calendar-create-event](#calendar-create-event)
|
|
31
|
+
- [calendar-update-event](#calendar-update-event)
|
|
32
|
+
- [calendar-list-events](#calendar-list-events)
|
|
33
|
+
- [calendar-search-events](#calendar-search-events)
|
|
34
|
+
- [calendar-delete-event](#calendar-delete-event)
|
|
35
|
+
- [calendar-list-calendars](#calendar-list-calendars)
|
|
36
|
+
- [calendar-get-current-datetime](#calendar-get-current-datetime)
|
|
37
|
+
- [Tasks Tools](#tasks-tools)
|
|
38
|
+
- [tasks-list-task-lists](#tasks-list-task-lists)
|
|
39
|
+
- [tasks-list-tasks](#tasks-list-tasks)
|
|
40
|
+
- [tasks-create-task](#tasks-create-task)
|
|
41
|
+
- [tasks-update-task](#tasks-update-task)
|
|
42
|
+
- [tasks-delete-task](#tasks-delete-task)
|
|
35
43
|
- [License](#license)
|
|
36
44
|
|
|
37
45
|
|
|
38
46
|
## Important: Authentication Architecture
|
|
39
47
|
|
|
40
|
-
**This MCP server is configured to use a single Google account for all operations.** The authentication credentials (client ID, client secret, and refresh token) are stored in environment variables (`.env` file), meaning all users of this MCP server will interact with the same Google Calendar account.
|
|
48
|
+
**This MCP server is configured to use a single Google account for all operations.** The authentication credentials (client ID, client secret, and refresh token) are stored in environment variables (`.env` file), meaning all users of this MCP server will interact with the same Google Calendar and Google Tasks account.
|
|
41
49
|
|
|
42
50
|
### Current Implementation
|
|
43
51
|
- **Single Account Mode**: One Google account's credentials are configured in the `.env` file
|
|
44
|
-
- **All operations** (create, read, update, delete events) are performed on this single account
|
|
45
|
-
- **Best for**: Personal use, single-user applications, or shared team calendars
|
|
52
|
+
- **All operations** (create, read, update, delete events and tasks) are performed on this single account
|
|
53
|
+
- **Best for**: Personal use, single-user applications, or shared team calendars and task lists
|
|
46
54
|
|
|
47
55
|
### Multi-User Scenarios
|
|
48
56
|
If you need **each user to authenticate with their own Google account**, this server would require modifications:
|
|
@@ -68,10 +76,9 @@ If you need **each user to authenticate with their own Google account**, this se
|
|
|
68
76
|
|
|
69
77
|
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
70
78
|
2. Create a new project or select an existing one
|
|
71
|
-
3. Enable the
|
|
72
|
-
- Go to "APIs & Services" > "Library"
|
|
73
|
-
-
|
|
74
|
-
- Click "Enable"
|
|
79
|
+
3. Enable the required APIs:
|
|
80
|
+
- **Google Calendar API**: Go to "APIs & Services" > "Library", search for "Google Calendar API" and click "Enable"
|
|
81
|
+
- **Google Tasks API**: Go to "APIs & Services" > "Library", search for "Google Tasks API" and click "Enable"
|
|
75
82
|
|
|
76
83
|
#### 2. Create OAuth 2.0 Credentials
|
|
77
84
|
|
|
@@ -96,10 +103,12 @@ If you need **each user to authenticate with their own Google account**, this se
|
|
|
96
103
|
# Google Cloud Console credentials
|
|
97
104
|
GOOGLE_CLIENT_ID=your_client_id_here.apps.googleusercontent.com
|
|
98
105
|
GOOGLE_CLIENT_SECRET=your_client_secret_here
|
|
99
|
-
GOOGLE_REDIRECT_URL=http://localhost:8080/oauth/callback
|
|
100
106
|
|
|
101
107
|
# This will be generated in the next step
|
|
102
108
|
GOOGLE_REFRESH_TOKEN=
|
|
109
|
+
|
|
110
|
+
# Default timezone for calendar operations
|
|
111
|
+
DEFAULT_TIMEZONE=Atlantic/Canary
|
|
103
112
|
```
|
|
104
113
|
|
|
105
114
|
#### 4. Get the refresh token
|
|
@@ -138,8 +147,8 @@ Add this to your MCP client configuration (e.g., Claude Desktop config):
|
|
|
138
147
|
"env": {
|
|
139
148
|
"GOOGLE_CLIENT_ID": "<your-client-id>",
|
|
140
149
|
"GOOGLE_CLIENT_SECRET": "<your-client-secret>",
|
|
141
|
-
"
|
|
142
|
-
"
|
|
150
|
+
"GOOGLE_REFRESH_TOKEN": "<your-refresh-token>",
|
|
151
|
+
"DEFAULT_TIMEZONE": "Atlantic/Canary"
|
|
143
152
|
}
|
|
144
153
|
}
|
|
145
154
|
}
|
|
@@ -171,8 +180,8 @@ npm run build
|
|
|
171
180
|
```bash
|
|
172
181
|
GOOGLE_CLIENT_ID=your-client-id
|
|
173
182
|
GOOGLE_CLIENT_SECRET=your-client-secret
|
|
174
|
-
GOOGLE_REDIRECT_URL=http://localhost:8080/oauth/callback
|
|
175
183
|
GOOGLE_REFRESH_TOKEN=your-refresh-token
|
|
184
|
+
DEFAULT_TIMEZONE=Atlantic/Canary
|
|
176
185
|
```
|
|
177
186
|
|
|
178
187
|
3. Update `mcp-inspector-config.json` with your project path:
|
|
@@ -185,8 +194,8 @@ GOOGLE_REFRESH_TOKEN=your-refresh-token
|
|
|
185
194
|
"env": {
|
|
186
195
|
"GOOGLE_CLIENT_ID": "${GOOGLE_CLIENT_ID}",
|
|
187
196
|
"GOOGLE_CLIENT_SECRET": "${GOOGLE_CLIENT_SECRET}",
|
|
188
|
-
"
|
|
189
|
-
"
|
|
197
|
+
"GOOGLE_REFRESH_TOKEN": "${GOOGLE_REFRESH_TOKEN}",
|
|
198
|
+
"DEFAULT_TIMEZONE": "${DEFAULT_TIMEZONE}"
|
|
190
199
|
}
|
|
191
200
|
}
|
|
192
201
|
}
|
|
@@ -201,22 +210,44 @@ npx @modelcontextprotocol/inspector --config mcp-inspector-config.json
|
|
|
201
210
|
1. Open your browser to the URL shown in the terminal (default should be http://localhost:6277) to interact with the MCP server through the Inspector UI.
|
|
202
211
|
|
|
203
212
|
|
|
213
|
+
## Timezone Configuration
|
|
214
|
+
|
|
215
|
+
This MCP server supports flexible timezone configuration:
|
|
216
|
+
|
|
217
|
+
1. **Default Timezone**: Set `DEFAULT_TIMEZONE` in your `.env` file using IANA timezone format (e.g., `Atlantic/Canary`, `America/New_York`, `Asia/Tokyo`)
|
|
218
|
+
2. **Per-Operation Override**: When creating or updating events, you can specify a different timezone for that specific operation
|
|
219
|
+
3. **Fallback**: If no timezone is configured, the server defaults to `Atlantic/Canary`
|
|
220
|
+
|
|
221
|
+
**Timezone Priority:**
|
|
222
|
+
1. Timezone parameter passed to the tool (highest priority)
|
|
223
|
+
2. `DEFAULT_TIMEZONE` environment variable from `.env`
|
|
224
|
+
3. Hardcoded default: `Atlantic/Canary` (lowest priority)
|
|
225
|
+
|
|
226
|
+
**Common IANA Timezones:**
|
|
227
|
+
- Europe: `Atlantic/Canary`, `Europe/London`, `Europe/Paris`, `Europe/Berlin`
|
|
228
|
+
- Americas: `America/New_York`, `America/Chicago`, `America/Los_Angeles`, `America/Mexico_City`
|
|
229
|
+
- Asia: `Asia/Tokyo`, `Asia/Shanghai`, `Asia/Dubai`, `Asia/Kolkata`
|
|
230
|
+
- Other: `Atlantic/Canary`, `Pacific/Auckland`, `Australia/Sydney`
|
|
231
|
+
|
|
204
232
|
## Security
|
|
205
233
|
|
|
206
234
|
- **Never** share your `client_secret` or `refresh_token`
|
|
207
235
|
- Add `.env` to your `.gitignore` (already included)
|
|
208
|
-
- Tokens have limited permissions only for Google Calendar
|
|
236
|
+
- Tokens have limited permissions only for Google Calendar and Google Tasks
|
|
209
237
|
|
|
210
238
|
## Additional Resources
|
|
211
239
|
|
|
212
240
|
- [Google Calendar API Documentation](https://developers.google.com/calendar/api)
|
|
241
|
+
- [Google Tasks API Documentation](https://developers.google.com/tasks)
|
|
213
242
|
- [OAuth 2.0 for Web Server Applications](https://developers.google.com/identity/protocols/oauth2/web-server)
|
|
214
243
|
- [Google Cloud Console](https://console.cloud.google.com/)
|
|
215
244
|
- [Model Context Protocol Documentation](https://modelcontextprotocol.io)
|
|
216
245
|
|
|
217
246
|
## Available Tools
|
|
218
247
|
|
|
219
|
-
###
|
|
248
|
+
### Calendar Tools
|
|
249
|
+
|
|
250
|
+
#### calendar-create-event
|
|
220
251
|
|
|
221
252
|
Creates a new calendar event in Google Calendar.
|
|
222
253
|
|
|
@@ -226,12 +257,14 @@ Parameters:
|
|
|
226
257
|
- `end`: DateTime string - Event end time (ISO 8601 format)
|
|
227
258
|
- `description`: String (optional) - Event description
|
|
228
259
|
- `location`: String (optional) - Event location
|
|
260
|
+
- `attendees`: Array of email strings (optional) - List of attendee email addresses
|
|
261
|
+
- `timeZone`: String (optional) - Timezone in IANA format (e.g., Atlantic/Canary, America/New_York). Defaults to `DEFAULT_TIMEZONE` from .env or 'Atlantic/Canary'
|
|
229
262
|
- `calendarId`: String (optional) - Calendar ID (defaults to 'primary')
|
|
230
263
|
|
|
231
264
|
Returns:
|
|
232
265
|
- The unique ID and details of the created event
|
|
233
266
|
|
|
234
|
-
|
|
267
|
+
#### calendar-update-event
|
|
235
268
|
|
|
236
269
|
Updates an existing event in Google Calendar.
|
|
237
270
|
|
|
@@ -242,12 +275,14 @@ Parameters:
|
|
|
242
275
|
- `end`: DateTime string (optional) - New event end time (ISO 8601 format)
|
|
243
276
|
- `description`: String (optional) - New event description
|
|
244
277
|
- `location`: String (optional) - New event location
|
|
278
|
+
- `attendees`: Array of email strings (optional) - List of attendee email addresses
|
|
279
|
+
- `timeZone`: String (optional) - Timezone in IANA format (e.g., Atlantic/Canary, America/New_York). Defaults to `DEFAULT_TIMEZONE` from .env or 'Atlantic/Canary'
|
|
245
280
|
- `calendarId`: String (optional) - Calendar ID (defaults to 'primary')
|
|
246
281
|
|
|
247
282
|
Returns:
|
|
248
283
|
- The updated event details
|
|
249
284
|
|
|
250
|
-
|
|
285
|
+
#### calendar-list-events
|
|
251
286
|
|
|
252
287
|
Lists events within a specified timeframe from Google Calendar.
|
|
253
288
|
|
|
@@ -260,7 +295,7 @@ Parameters:
|
|
|
260
295
|
Returns:
|
|
261
296
|
- A list of events that fall within the given timeframe
|
|
262
297
|
|
|
263
|
-
|
|
298
|
+
#### calendar-search-events
|
|
264
299
|
|
|
265
300
|
Searches for events in Google Calendar by text query.
|
|
266
301
|
|
|
@@ -272,7 +307,7 @@ Parameters:
|
|
|
272
307
|
Returns:
|
|
273
308
|
- A list of events matching the search query
|
|
274
309
|
|
|
275
|
-
|
|
310
|
+
#### calendar-delete-event
|
|
276
311
|
|
|
277
312
|
Deletes an event from Google Calendar.
|
|
278
313
|
|
|
@@ -283,23 +318,90 @@ Parameters:
|
|
|
283
318
|
Returns:
|
|
284
319
|
- Confirmation of deletion
|
|
285
320
|
|
|
286
|
-
|
|
321
|
+
#### calendar-list-calendars
|
|
287
322
|
|
|
288
323
|
Lists all calendars available to the user.
|
|
289
324
|
|
|
290
325
|
Returns:
|
|
291
326
|
- A list of calendars with their IDs and names
|
|
292
327
|
|
|
293
|
-
|
|
328
|
+
#### calendar-get-current-datetime
|
|
294
329
|
|
|
295
330
|
Gets the current date and time in ISO 8601 format. Useful for references when creating or searching for events.
|
|
296
331
|
|
|
297
332
|
Parameters:
|
|
298
|
-
- `timezone`: String (optional) - Timezone in IANA format (e.g., America/New_York,
|
|
333
|
+
- `timezone`: String (optional) - Timezone in IANA format (e.g., Atlantic/Canary, America/New_York, Asia/Tokyo). Defaults to `DEFAULT_TIMEZONE` from .env or 'Atlantic/Canary'.
|
|
299
334
|
|
|
300
335
|
Returns:
|
|
301
336
|
- Current date and time information including ISO 8601 format, timestamp, local time, and timezone details
|
|
302
337
|
|
|
338
|
+
### Tasks Tools
|
|
339
|
+
|
|
340
|
+
#### tasks-list-task-lists
|
|
341
|
+
|
|
342
|
+
Lists all task lists available in Google Tasks.
|
|
343
|
+
|
|
344
|
+
Parameters: None
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
- A list of all task lists with their IDs and titles
|
|
348
|
+
|
|
349
|
+
Example use case:
|
|
350
|
+
- Get the list of all your task lists to find the `taskListId` for creating or managing tasks
|
|
351
|
+
|
|
352
|
+
#### tasks-list-tasks
|
|
353
|
+
|
|
354
|
+
Lists all tasks in a specific task list.
|
|
355
|
+
|
|
356
|
+
Parameters:
|
|
357
|
+
- `taskListId`: String - The ID of the task list (use `tasks-list-task-lists` to get this)
|
|
358
|
+
- `showCompleted`: Boolean (optional) - Whether to show completed tasks (default: true)
|
|
359
|
+
- `maxResults`: Number (optional) - Maximum number of tasks to return (default: 100)
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
- A list of tasks with details including ID, title, notes, status, due date, and completion date
|
|
363
|
+
|
|
364
|
+
#### tasks-create-task
|
|
365
|
+
|
|
366
|
+
Creates a new task in Google Tasks.
|
|
367
|
+
|
|
368
|
+
Parameters:
|
|
369
|
+
- `taskListId`: String - The ID of the task list where the task will be created
|
|
370
|
+
- `title`: String - Task title/summary
|
|
371
|
+
- `notes`: String (optional) - Task notes or description
|
|
372
|
+
- `due`: DateTime string (optional) - Due date in ISO 8601 format (e.g., "2024-12-31T23:59:59Z")
|
|
373
|
+
|
|
374
|
+
Returns:
|
|
375
|
+
- The created task details including ID, title, notes, status, and due date
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
#### tasks-update-task
|
|
379
|
+
|
|
380
|
+
Updates an existing task in Google Tasks.
|
|
381
|
+
|
|
382
|
+
Parameters:
|
|
383
|
+
- `taskListId`: String - The ID of the task list containing the task
|
|
384
|
+
- `taskId`: String - The ID of the task to update
|
|
385
|
+
- `title`: String (optional) - New task title
|
|
386
|
+
- `notes`: String (optional) - New task notes or description
|
|
387
|
+
- `due`: DateTime string (optional) - New due date in ISO 8601 format
|
|
388
|
+
- `status`: String (optional) - Task status: either "needsAction" or "completed"
|
|
389
|
+
|
|
390
|
+
Returns:
|
|
391
|
+
- The updated task details
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
#### tasks-delete-task
|
|
395
|
+
|
|
396
|
+
Deletes a task from Google Tasks.
|
|
397
|
+
|
|
398
|
+
Parameters:
|
|
399
|
+
- `taskListId`: String - The ID of the task list containing the task
|
|
400
|
+
- `taskId`: String - The ID of the task to delete
|
|
401
|
+
|
|
402
|
+
Returns:
|
|
403
|
+
- Confirmation message of deletion
|
|
404
|
+
|
|
303
405
|
## License
|
|
304
406
|
|
|
305
407
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
import "dotenv/config";
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
import { GoogleCalendarClient } from "./tools/google-calendar.js";
|
|
5
|
+
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.0
|
|
9
|
+
version: "1.1.0",
|
|
10
10
|
});
|
|
11
11
|
async function main() {
|
|
12
12
|
console.error('Configurando Google Calendar API...');
|
|
@@ -18,7 +18,6 @@ async function main() {
|
|
|
18
18
|
const googleClient = new GoogleCalendarClient({
|
|
19
19
|
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
20
20
|
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
21
|
-
redirectUrl: process.env.GOOGLE_REDIRECT_URL || 'http://localhost:8080/oauth/callback',
|
|
22
21
|
refreshToken: process.env.GOOGLE_REFRESH_TOKEN,
|
|
23
22
|
});
|
|
24
23
|
// Si no tenemos refresh token, mostrar URL de autorización
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import { createSimpleEvent } from '
|
|
1
|
+
import { createSimpleEvent } from '../google-calendar-client.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
export function registerGoogleCreateEvent(googleClient, server) {
|
|
4
|
-
server.tool('
|
|
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
7
|
start: z.string().describe('Fecha/hora de inicio en formato ISO (ej: 2023-12-01T10:00:00Z)'),
|
|
8
8
|
end: z.string().describe('Fecha/hora de fin en formato ISO'),
|
|
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
|
+
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'),
|
|
11
13
|
}, async (args) => {
|
|
12
14
|
try {
|
|
13
|
-
const { calendarId = 'primary', title, start, end, description, location, } = args;
|
|
15
|
+
const { calendarId = 'primary', title, start, end, description, location, attendees, timeZone, } = args;
|
|
14
16
|
if (!title || !start || !end) {
|
|
15
17
|
return {
|
|
16
18
|
content: [
|
|
@@ -22,13 +24,17 @@ export function registerGoogleCreateEvent(googleClient, server) {
|
|
|
22
24
|
isError: true,
|
|
23
25
|
};
|
|
24
26
|
}
|
|
25
|
-
const event = createSimpleEvent(title, start, end, description, location);
|
|
27
|
+
const event = createSimpleEvent(title, start, end, description, location, attendees, timeZone);
|
|
26
28
|
const createdEvent = await googleClient.createEvent(calendarId, event);
|
|
29
|
+
let successMessage = `Evento creado exitosamente:\nID: ${createdEvent.id}\nTítulo: ${createdEvent.summary}\nInicio: ${createdEvent.start?.dateTime}\nFin: ${createdEvent.end?.dateTime}`;
|
|
30
|
+
if (attendees && attendees.length > 0) {
|
|
31
|
+
successMessage += `\nInvitados: ${attendees.join(', ')}`;
|
|
32
|
+
}
|
|
27
33
|
return {
|
|
28
34
|
content: [
|
|
29
35
|
{
|
|
30
36
|
type: 'text',
|
|
31
|
-
text:
|
|
37
|
+
text: successMessage,
|
|
32
38
|
},
|
|
33
39
|
],
|
|
34
40
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export function registerGoogleDeleteEvent(googleClient, server) {
|
|
3
|
-
server.tool('
|
|
3
|
+
server.tool('calendar_delete_event', 'Elimina un evento de Google Calendar', {
|
|
4
4
|
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
5
5
|
eventId: z.string().describe('ID del evento a eliminar'),
|
|
6
6
|
}, async (args) => {
|
package/dist/tools/{google-get-current-datetime.js → calendar/calendar-get-current-datetime.js}
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export function registerGoogleGetCurrentDatetime(googleClient, server) {
|
|
3
|
-
server.tool('
|
|
4
|
-
timezone: z.string().optional().describe('Zona horaria en formato IANA (ej: America/New_York,
|
|
3
|
+
server.tool('calendar_get_current_datetime', 'Obtiene la fecha y hora actual en formato ISO 8601. Útil para referencias temporales al crear o buscar eventos.', {
|
|
4
|
+
timezone: z.string().optional().describe('Zona horaria en formato IANA (ej: America/New_York, Atlantic/Canary). Por defecto usa DEFAULT_TIMEZONE del .env'),
|
|
5
5
|
}, async (args) => {
|
|
6
6
|
try {
|
|
7
|
-
const { timezone = 'Atlantic/Canary' } = args;
|
|
7
|
+
const { timezone = process.env.DEFAULT_TIMEZONE || 'Atlantic/Canary' } = args;
|
|
8
8
|
const now = new Date();
|
|
9
9
|
// Información básica
|
|
10
10
|
const isoString = now.toISOString();
|
|
@@ -40,7 +40,7 @@ export function registerGoogleGetCurrentDatetime(googleClient, server) {
|
|
|
40
40
|
content: [
|
|
41
41
|
{
|
|
42
42
|
type: 'text',
|
|
43
|
-
text: `Error: Zona horaria inválida '${timezone}'. Usa formato IANA (ej: America/New_York,
|
|
43
|
+
text: `Error: Zona horaria inválida '${timezone}'. Usa formato IANA (ej: America/New_York, Atlantic/Canary, Atlantic/Canary)`,
|
|
44
44
|
},
|
|
45
45
|
],
|
|
46
46
|
isError: true,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export function registerGoogleListCalendars(googleClient, server) {
|
|
2
|
-
server.tool('
|
|
2
|
+
server.tool('calendar_list_calendars', 'Lista todos los calendarios disponibles en Google Calendar', async () => {
|
|
3
3
|
try {
|
|
4
4
|
const calendars = await googleClient.listCalendars();
|
|
5
5
|
const data = calendars.map((cal) => ({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export function registerGoogleListEvents(googleClient, server) {
|
|
3
|
-
server.tool('
|
|
3
|
+
server.tool('calendar_list_events', 'Lista eventos de un calendario de Google Calendar', {
|
|
4
4
|
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
5
5
|
timeMin: z.string().optional().describe('Fecha/hora mínima en formato ISO (ej: 2023-12-01T00:00:00Z)'),
|
|
6
6
|
timeMax: z.string().optional().describe('Fecha/hora máxima en formato ISO'),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export function registerGoogleSearchEvents(googleClient, server) {
|
|
3
|
-
server.tool('
|
|
3
|
+
server.tool('calendar_search_events', 'Busca eventos en Google Calendar por texto', {
|
|
4
4
|
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
5
5
|
query: z.string().describe('Texto a buscar en los eventos'),
|
|
6
6
|
maxResults: z.number().optional().describe('Número máximo de resultados (por defecto: 10)'),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
export function registerGoogleUpdateEvent(googleClient, server) {
|
|
3
|
-
server.tool('
|
|
3
|
+
server.tool('calendar_update_event', 'Actualiza un evento existente en Google Calendar', {
|
|
4
4
|
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
5
5
|
eventId: z.string().describe('ID del evento a actualizar'),
|
|
6
6
|
title: z.string().optional().describe('Nuevo título del evento'),
|
|
@@ -8,9 +8,11 @@ export function registerGoogleUpdateEvent(googleClient, server) {
|
|
|
8
8
|
end: z.string().optional().describe('Nueva fecha/hora de fin en formato ISO'),
|
|
9
9
|
description: z.string().optional().describe('Nueva descripción del evento'),
|
|
10
10
|
location: z.string().optional().describe('Nueva ubicación del evento'),
|
|
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'),
|
|
11
13
|
}, async (args) => {
|
|
12
14
|
try {
|
|
13
|
-
const { calendarId = 'primary', eventId, title, start, end, description, location, } = args;
|
|
15
|
+
const { calendarId = 'primary', eventId, title, start, end, description, location, attendees, timeZone, } = args;
|
|
14
16
|
if (!eventId) {
|
|
15
17
|
return {
|
|
16
18
|
content: [
|
|
@@ -33,24 +35,35 @@ export function registerGoogleUpdateEvent(googleClient, server) {
|
|
|
33
35
|
if (location !== undefined) {
|
|
34
36
|
event.location = location;
|
|
35
37
|
}
|
|
38
|
+
const defaultTimeZone = timeZone || process.env.DEFAULT_TIMEZONE || 'Atlantic/Canary';
|
|
36
39
|
if (start !== undefined) {
|
|
37
40
|
event.start = {
|
|
38
41
|
dateTime: start,
|
|
39
|
-
timeZone:
|
|
42
|
+
timeZone: defaultTimeZone,
|
|
40
43
|
};
|
|
41
44
|
}
|
|
42
45
|
if (end !== undefined) {
|
|
43
46
|
event.end = {
|
|
44
47
|
dateTime: end,
|
|
45
|
-
timeZone:
|
|
48
|
+
timeZone: defaultTimeZone,
|
|
46
49
|
};
|
|
47
50
|
}
|
|
51
|
+
if (attendees !== undefined) {
|
|
52
|
+
event.attendees = attendees.map(email => ({
|
|
53
|
+
email: email.trim(),
|
|
54
|
+
responseStatus: 'needsAction'
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
48
57
|
const updatedEvent = await googleClient.updateEvent(calendarId, eventId, event);
|
|
58
|
+
let successMessage = `Evento actualizado exitosamente:\nID: ${updatedEvent.id}\nTítulo: ${updatedEvent.summary}\nInicio: ${updatedEvent.start?.dateTime}\nFin: ${updatedEvent.end?.dateTime}`;
|
|
59
|
+
if (attendees !== undefined && attendees.length > 0) {
|
|
60
|
+
successMessage += `\nInvitados: ${attendees.join(', ')}`;
|
|
61
|
+
}
|
|
49
62
|
return {
|
|
50
63
|
content: [
|
|
51
64
|
{
|
|
52
65
|
type: 'text',
|
|
53
|
-
text:
|
|
66
|
+
text: successMessage,
|
|
54
67
|
},
|
|
55
68
|
],
|
|
56
69
|
};
|
|
@@ -2,14 +2,16 @@ import { google } from 'googleapis';
|
|
|
2
2
|
export class GoogleCalendarClient {
|
|
3
3
|
auth;
|
|
4
4
|
calendar;
|
|
5
|
+
tasks;
|
|
5
6
|
constructor(config) {
|
|
6
|
-
this.auth = new google.auth.OAuth2(config.clientId, config.clientSecret,
|
|
7
|
+
this.auth = new google.auth.OAuth2(config.clientId, config.clientSecret, 'http://localhost:8080/oauth/callback');
|
|
7
8
|
if (config.refreshToken) {
|
|
8
9
|
this.auth.setCredentials({
|
|
9
10
|
refresh_token: config.refreshToken,
|
|
10
11
|
});
|
|
11
12
|
}
|
|
12
13
|
this.calendar = google.calendar({ version: 'v3', auth: this.auth });
|
|
14
|
+
this.tasks = google.tasks({ version: 'v1', auth: this.auth });
|
|
13
15
|
}
|
|
14
16
|
/**
|
|
15
17
|
* Genera URL de autorización para obtener el token inicial
|
|
@@ -17,7 +19,8 @@ export class GoogleCalendarClient {
|
|
|
17
19
|
getAuthUrl() {
|
|
18
20
|
const scopes = [
|
|
19
21
|
'https://www.googleapis.com/auth/calendar',
|
|
20
|
-
'https://www.googleapis.com/auth/calendar.events'
|
|
22
|
+
'https://www.googleapis.com/auth/calendar.events',
|
|
23
|
+
'https://www.googleapis.com/auth/tasks'
|
|
21
24
|
];
|
|
22
25
|
return this.auth.generateAuthUrl({
|
|
23
26
|
access_type: 'offline',
|
|
@@ -132,22 +135,129 @@ export class GoogleCalendarClient {
|
|
|
132
135
|
throw error;
|
|
133
136
|
}
|
|
134
137
|
}
|
|
138
|
+
// ========== Google Tasks Methods ==========
|
|
139
|
+
/**
|
|
140
|
+
* Lista todas las listas de tareas del usuario
|
|
141
|
+
*/
|
|
142
|
+
async listTaskLists() {
|
|
143
|
+
try {
|
|
144
|
+
const response = await this.tasks.tasklists.list();
|
|
145
|
+
return response.data.items || [];
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
console.error('Error al listar listas de tareas:', error);
|
|
149
|
+
throw error;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Lista todas las tareas de una lista específica
|
|
154
|
+
*/
|
|
155
|
+
async listTasks(taskListId, showCompleted = true, maxResults = 100) {
|
|
156
|
+
try {
|
|
157
|
+
const response = await this.tasks.tasks.list({
|
|
158
|
+
tasklist: taskListId,
|
|
159
|
+
showCompleted,
|
|
160
|
+
maxResults,
|
|
161
|
+
});
|
|
162
|
+
return response.data.items || [];
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
console.error('Error al listar tareas:', error);
|
|
166
|
+
throw error;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Crea una nueva tarea
|
|
171
|
+
*/
|
|
172
|
+
async createTask(taskListId, task) {
|
|
173
|
+
try {
|
|
174
|
+
const response = await this.tasks.tasks.insert({
|
|
175
|
+
tasklist: taskListId,
|
|
176
|
+
requestBody: task,
|
|
177
|
+
});
|
|
178
|
+
return response.data;
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.error('Error al crear tarea:', error);
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Actualiza una tarea existente
|
|
187
|
+
*/
|
|
188
|
+
async updateTask(taskListId, taskId, task) {
|
|
189
|
+
try {
|
|
190
|
+
const response = await this.tasks.tasks.update({
|
|
191
|
+
tasklist: taskListId,
|
|
192
|
+
task: taskId,
|
|
193
|
+
requestBody: task,
|
|
194
|
+
});
|
|
195
|
+
return response.data;
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
console.error('Error al actualizar tarea:', error);
|
|
199
|
+
throw error;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Elimina una tarea
|
|
204
|
+
*/
|
|
205
|
+
async deleteTask(taskListId, taskId) {
|
|
206
|
+
try {
|
|
207
|
+
await this.tasks.tasks.delete({
|
|
208
|
+
tasklist: taskListId,
|
|
209
|
+
task: taskId,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
console.error('Error al eliminar tarea:', error);
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Marca una tarea como completada
|
|
219
|
+
*/
|
|
220
|
+
async completeTask(taskListId, taskId) {
|
|
221
|
+
try {
|
|
222
|
+
// Primero obtenemos la tarea para no perder información
|
|
223
|
+
const taskResponse = await this.tasks.tasks.get({
|
|
224
|
+
tasklist: taskListId,
|
|
225
|
+
task: taskId,
|
|
226
|
+
});
|
|
227
|
+
const task = taskResponse.data;
|
|
228
|
+
task.status = 'completed';
|
|
229
|
+
task.completed = new Date().toISOString();
|
|
230
|
+
return await this.updateTask(taskListId, taskId, task);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
console.error('Error al completar tarea:', error);
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
135
237
|
}
|
|
136
238
|
/**
|
|
137
239
|
* Función helper para crear eventos con formato simplificado
|
|
138
240
|
*/
|
|
139
|
-
export function createSimpleEvent(title, start, end, description, location) {
|
|
140
|
-
|
|
241
|
+
export function createSimpleEvent(title, start, end, description, location, attendees, timeZone) {
|
|
242
|
+
const defaultTimeZone = timeZone || process.env.DEFAULT_TIMEZONE || 'Atlantic/Canary';
|
|
243
|
+
const event = {
|
|
141
244
|
summary: title,
|
|
142
245
|
description,
|
|
143
246
|
location,
|
|
144
247
|
start: {
|
|
145
248
|
dateTime: start,
|
|
146
|
-
timeZone:
|
|
249
|
+
timeZone: defaultTimeZone,
|
|
147
250
|
},
|
|
148
251
|
end: {
|
|
149
252
|
dateTime: end,
|
|
150
|
-
timeZone:
|
|
253
|
+
timeZone: defaultTimeZone,
|
|
151
254
|
},
|
|
152
255
|
};
|
|
256
|
+
if (attendees && attendees.length > 0) {
|
|
257
|
+
event.attendees = attendees.map(email => ({
|
|
258
|
+
email: email.trim(),
|
|
259
|
+
responseStatus: 'needsAction'
|
|
260
|
+
}));
|
|
261
|
+
}
|
|
262
|
+
return event;
|
|
153
263
|
}
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
-
import { registerGoogleListCalendars } from './
|
|
2
|
-
import { registerGoogleListEvents } from './
|
|
3
|
-
import { registerGoogleCreateEvent } from './
|
|
4
|
-
import { registerGoogleUpdateEvent } from './
|
|
5
|
-
import { registerGoogleDeleteEvent } from './
|
|
6
|
-
import { registerGoogleSearchEvents } from './
|
|
7
|
-
import { registerGoogleGetCurrentDatetime } from './
|
|
1
|
+
import { registerGoogleListCalendars } from './calendar/calendar-list-calendars.js';
|
|
2
|
+
import { registerGoogleListEvents } from './calendar/calendar-list-events.js';
|
|
3
|
+
import { registerGoogleCreateEvent } from './calendar/calendar-create-event.js';
|
|
4
|
+
import { registerGoogleUpdateEvent } from './calendar/calendar-update-event.js';
|
|
5
|
+
import { registerGoogleDeleteEvent } from './calendar/calendar-delete-event.js';
|
|
6
|
+
import { registerGoogleSearchEvents } from './calendar/calendar-search-events.js';
|
|
7
|
+
import { registerGoogleGetCurrentDatetime } from './calendar/calendar-get-current-datetime.js';
|
|
8
|
+
import { registerTasksListTaskLists } from './tasks/tasks-list-task-lists.js';
|
|
9
|
+
import { registerTasksListTasks } from './tasks/tasks-list-tasks.js';
|
|
10
|
+
import { registerTasksCreateTask } from './tasks/tasks-create-task.js';
|
|
11
|
+
import { registerTasksUpdateTask } from './tasks/tasks-update-task.js';
|
|
12
|
+
import { registerTasksDeleteTask } from './tasks/tasks-delete-task.js';
|
|
8
13
|
export function registerGoogleCalendarTools(googleClient, server) {
|
|
14
|
+
// Calendar tools
|
|
9
15
|
registerGoogleListCalendars(googleClient, server);
|
|
10
16
|
registerGoogleListEvents(googleClient, server);
|
|
11
17
|
registerGoogleCreateEvent(googleClient, server);
|
|
@@ -13,4 +19,10 @@ export function registerGoogleCalendarTools(googleClient, server) {
|
|
|
13
19
|
registerGoogleDeleteEvent(googleClient, server);
|
|
14
20
|
registerGoogleSearchEvents(googleClient, server);
|
|
15
21
|
registerGoogleGetCurrentDatetime(googleClient, server);
|
|
22
|
+
// Tasks tools
|
|
23
|
+
registerTasksListTaskLists(googleClient, server);
|
|
24
|
+
registerTasksListTasks(googleClient, server);
|
|
25
|
+
registerTasksCreateTask(googleClient, server);
|
|
26
|
+
registerTasksUpdateTask(googleClient, server);
|
|
27
|
+
registerTasksDeleteTask(googleClient, server);
|
|
16
28
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerTasksCreateTask(googleClient, server) {
|
|
3
|
+
server.tool('tasks_create_task', 'Crea una nueva tarea en Google Tasks', {
|
|
4
|
+
taskListId: z.string().describe('ID de la lista de tareas (usa tasks_list_task_lists para obtenerlo)'),
|
|
5
|
+
title: z.string().describe('Título de la tarea'),
|
|
6
|
+
notes: z.string().optional().describe('Notas o descripción de la tarea'),
|
|
7
|
+
due: z.string().optional().describe('Fecha de vencimiento en formato ISO (ej: 2023-12-31T23:59:59Z)'),
|
|
8
|
+
}, async (args) => {
|
|
9
|
+
try {
|
|
10
|
+
const { taskListId, title, notes, due, } = args;
|
|
11
|
+
const task = {
|
|
12
|
+
title,
|
|
13
|
+
notes,
|
|
14
|
+
due,
|
|
15
|
+
status: 'needsAction',
|
|
16
|
+
};
|
|
17
|
+
const createdTask = await googleClient.createTask(taskListId, task);
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{
|
|
21
|
+
type: 'text',
|
|
22
|
+
text: JSON.stringify({
|
|
23
|
+
id: createdTask.id,
|
|
24
|
+
title: createdTask.title,
|
|
25
|
+
notes: createdTask.notes,
|
|
26
|
+
status: createdTask.status,
|
|
27
|
+
due: createdTask.due,
|
|
28
|
+
created: createdTask.updated,
|
|
29
|
+
}, null, 2),
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: 'text',
|
|
40
|
+
text: `Error al crear tarea: ${errorMessage}`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
isError: true,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerTasksDeleteTask(googleClient, server) {
|
|
3
|
+
server.tool('tasks_delete_task', 'Elimina una tarea de Google Tasks', {
|
|
4
|
+
taskListId: z.string().describe('ID de la lista de tareas'),
|
|
5
|
+
taskId: z.string().describe('ID de la tarea a eliminar'),
|
|
6
|
+
}, async (args) => {
|
|
7
|
+
try {
|
|
8
|
+
const { taskListId, taskId } = args;
|
|
9
|
+
await googleClient.deleteTask(taskListId, taskId);
|
|
10
|
+
return {
|
|
11
|
+
content: [
|
|
12
|
+
{
|
|
13
|
+
type: 'text',
|
|
14
|
+
text: `Tarea ${taskId} eliminada exitosamente de la lista ${taskListId}`,
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
type: 'text',
|
|
25
|
+
text: `Error al eliminar tarea: ${errorMessage}`,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
isError: true,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function registerTasksListTaskLists(googleClient, server) {
|
|
2
|
+
server.tool('tasks_list_task_lists', 'Lista todas las listas de tareas de Google Tasks del usuario', {}, async () => {
|
|
3
|
+
try {
|
|
4
|
+
const taskLists = await googleClient.listTaskLists();
|
|
5
|
+
const data = taskLists.map((taskList) => ({
|
|
6
|
+
id: taskList.id,
|
|
7
|
+
title: taskList.title,
|
|
8
|
+
updated: taskList.updated,
|
|
9
|
+
}));
|
|
10
|
+
return {
|
|
11
|
+
content: [
|
|
12
|
+
{
|
|
13
|
+
type: 'text',
|
|
14
|
+
text: JSON.stringify(data, null, 2),
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
catch (error) {
|
|
20
|
+
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
type: 'text',
|
|
25
|
+
text: `Error al listar listas de tareas: ${errorMessage}`,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
isError: true,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerTasksListTasks(googleClient, server) {
|
|
3
|
+
server.tool('tasks_list_tasks', 'Lista todas las tareas de una lista de tareas específica en Google Tasks', {
|
|
4
|
+
taskListId: z.string().describe('ID de la lista de tareas (usa tasks_list_task_lists para obtenerlo)'),
|
|
5
|
+
showCompleted: z.boolean().optional().describe('Mostrar tareas completadas (por defecto: true)'),
|
|
6
|
+
maxResults: z.number().optional().describe('Número máximo de resultados (por defecto: 100)'),
|
|
7
|
+
}, async (args) => {
|
|
8
|
+
try {
|
|
9
|
+
const { taskListId, showCompleted = true, maxResults = 100, } = args;
|
|
10
|
+
const tasks = await googleClient.listTasks(taskListId, showCompleted, maxResults);
|
|
11
|
+
const data = tasks.map((task) => ({
|
|
12
|
+
id: task.id,
|
|
13
|
+
title: task.title,
|
|
14
|
+
notes: task.notes,
|
|
15
|
+
status: task.status,
|
|
16
|
+
due: task.due,
|
|
17
|
+
completed: task.completed,
|
|
18
|
+
updated: task.updated,
|
|
19
|
+
parent: task.parent,
|
|
20
|
+
}));
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
type: 'text',
|
|
25
|
+
text: JSON.stringify(data, null, 2),
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: 'text',
|
|
36
|
+
text: `Error al listar tareas: ${errorMessage}`,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
isError: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerTasksUpdateTask(googleClient, server) {
|
|
3
|
+
server.tool('tasks_update_task', 'Actualiza una tarea existente en Google Tasks', {
|
|
4
|
+
taskListId: z.string().describe('ID de la lista de tareas'),
|
|
5
|
+
taskId: z.string().describe('ID de la tarea a actualizar'),
|
|
6
|
+
title: z.string().optional().describe('Nuevo título de la tarea'),
|
|
7
|
+
notes: z.string().optional().describe('Nuevas notas o descripción de la tarea'),
|
|
8
|
+
due: z.string().optional().describe('Nueva fecha de vencimiento en formato ISO (ej: 2023-12-31T23:59:59Z)'),
|
|
9
|
+
status: z.enum(['needsAction', 'completed']).optional().describe('Estado de la tarea'),
|
|
10
|
+
}, async (args) => {
|
|
11
|
+
try {
|
|
12
|
+
const { taskListId, taskId, title, notes, due, status, } = args;
|
|
13
|
+
const task = {};
|
|
14
|
+
if (title !== undefined)
|
|
15
|
+
task.title = title;
|
|
16
|
+
if (notes !== undefined)
|
|
17
|
+
task.notes = notes;
|
|
18
|
+
if (due !== undefined)
|
|
19
|
+
task.due = due;
|
|
20
|
+
if (status !== undefined) {
|
|
21
|
+
task.status = status;
|
|
22
|
+
if (status === 'completed') {
|
|
23
|
+
task.completed = new Date().toISOString();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const updatedTask = await googleClient.updateTask(taskListId, taskId, task);
|
|
27
|
+
return {
|
|
28
|
+
content: [
|
|
29
|
+
{
|
|
30
|
+
type: 'text',
|
|
31
|
+
text: JSON.stringify({
|
|
32
|
+
id: updatedTask.id,
|
|
33
|
+
title: updatedTask.title,
|
|
34
|
+
notes: updatedTask.notes,
|
|
35
|
+
status: updatedTask.status,
|
|
36
|
+
due: updatedTask.due,
|
|
37
|
+
completed: updatedTask.completed,
|
|
38
|
+
updated: updatedTask.updated,
|
|
39
|
+
}, null, 2),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
|
|
46
|
+
return {
|
|
47
|
+
content: [
|
|
48
|
+
{
|
|
49
|
+
type: 'text',
|
|
50
|
+
text: `Error al actualizar tarea: ${errorMessage}`,
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
isError: true,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
package/package.json
CHANGED