@pablosr/mcp-google-calendar 1.0.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/LICENSE +21 -0
- package/README.md +309 -0
- package/dist/index.js +41 -0
- package/dist/tools/google-calendar-mcp.js +16 -0
- package/dist/tools/google-calendar.js +153 -0
- package/dist/tools/google-create-event.js +49 -0
- package/dist/tools/google-delete-event.js +43 -0
- package/dist/tools/google-get-current-datetime.js +88 -0
- package/dist/tools/google-list-calendars.js +34 -0
- package/dist/tools/google-list-events.js +43 -0
- package/dist/tools/google-search-events.js +52 -0
- package/dist/tools/google-update-event.js +71 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Pablo Suarez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
# mcp-google-calendar
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
A Google Calendar (MCP) server to expose calendar operations as tools for LLM.
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [mcp-google-calendar](#mcp-google-calendar)
|
|
10
|
+
- [Table of Contents](#table-of-contents)
|
|
11
|
+
- [Important: Authentication Architecture](#important-authentication-architecture)
|
|
12
|
+
- [Current Implementation](#current-implementation)
|
|
13
|
+
- [Multi-User Scenarios](#multi-user-scenarios)
|
|
14
|
+
- [Setup](#setup)
|
|
15
|
+
- [Prerequisites](#prerequisites)
|
|
16
|
+
- [Step-by-step Configuration](#step-by-step-configuration)
|
|
17
|
+
- [1. Configure Google Cloud Console Project](#1-configure-google-cloud-console-project)
|
|
18
|
+
- [2. Create OAuth 2.0 Credentials](#2-create-oauth-20-credentials)
|
|
19
|
+
- [3. Configure the .env file](#3-configure-the-env-file)
|
|
20
|
+
- [4. Get the refresh token](#4-get-the-refresh-token)
|
|
21
|
+
- [5. Test the configuration](#5-test-the-configuration)
|
|
22
|
+
- [MCP Client Configuration](#mcp-client-configuration)
|
|
23
|
+
- [Usage](#usage)
|
|
24
|
+
- [Testing with MCP Inspector](#testing-with-mcp-inspector)
|
|
25
|
+
- [Security](#security)
|
|
26
|
+
- [Additional Resources](#additional-resources)
|
|
27
|
+
- [Available Tools](#available-tools)
|
|
28
|
+
- [google-create-event](#google-create-event)
|
|
29
|
+
- [google-update-event](#google-update-event)
|
|
30
|
+
- [google-list-events](#google-list-events)
|
|
31
|
+
- [google-search-events](#google-search-events)
|
|
32
|
+
- [google-delete-event](#google-delete-event)
|
|
33
|
+
- [google-list-calendars](#google-list-calendars)
|
|
34
|
+
- [google-get-current-datetime](#google-get-current-datetime)
|
|
35
|
+
- [License](#license)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
## Important: Authentication Architecture
|
|
39
|
+
|
|
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.
|
|
41
|
+
|
|
42
|
+
### Current Implementation
|
|
43
|
+
- **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
|
|
46
|
+
|
|
47
|
+
### Multi-User Scenarios
|
|
48
|
+
If you need **each user to authenticate with their own Google account**, this server would require modifications:
|
|
49
|
+
|
|
50
|
+
1. **Add user authentication layer**: Implement a user authentication system (OAuth2, sessions, JWT, etc.)
|
|
51
|
+
2. **Pass user context**: Modify the MCP tools to use the authenticated user's token instead of the `.env` token
|
|
52
|
+
3. **Token refresh logic**: Implement per-user token refresh and management
|
|
53
|
+
|
|
54
|
+
> **Note**: The current implementation prioritizes simplicity for personal use.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
## Setup
|
|
58
|
+
|
|
59
|
+
### Prerequisites
|
|
60
|
+
|
|
61
|
+
1. A Google account
|
|
62
|
+
2. Access to Google Cloud Console
|
|
63
|
+
3. Node.js installed
|
|
64
|
+
|
|
65
|
+
### Step-by-step Configuration
|
|
66
|
+
|
|
67
|
+
#### 1. Configure Google Cloud Console Project
|
|
68
|
+
|
|
69
|
+
1. Go to [Google Cloud Console](https://console.cloud.google.com/)
|
|
70
|
+
2. Create a new project or select an existing one
|
|
71
|
+
3. Enable the [**Google Calendar API**](https://console.cloud.google.com/apis/library/calendar-json.googleapis.com):
|
|
72
|
+
- Go to "APIs & Services" > "Library"
|
|
73
|
+
- Search for "Google Calendar API"
|
|
74
|
+
- Click "Enable"
|
|
75
|
+
|
|
76
|
+
#### 2. Create OAuth 2.0 Credentials
|
|
77
|
+
|
|
78
|
+
1. Go to "APIs & Services" > "Credentials"
|
|
79
|
+
2. Click "Create Credentials" > "OAuth 2.0 Client ID"
|
|
80
|
+
3. If it's your first time, configure the OAuth consent screen:
|
|
81
|
+
- Select "External" (or "Internal" if you have Google Workspace)
|
|
82
|
+
- Complete the basic app information
|
|
83
|
+
- In "Scopes", don't add any scope (we'll do this programmatically)
|
|
84
|
+
- Add your email as a test user
|
|
85
|
+
4. Create the OAuth 2.0 Client ID:
|
|
86
|
+
- Application type: "Web application"
|
|
87
|
+
- Name: "Google Calendar MCP Server"
|
|
88
|
+
- Authorized redirect URIs: `http://localhost:8080/oauth/callback`
|
|
89
|
+
|
|
90
|
+
#### 3. Configure the .env file
|
|
91
|
+
|
|
92
|
+
1. Copy the credentials from Google Cloud Console
|
|
93
|
+
2. Create a `.env` file in the project root:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
# Google Cloud Console credentials
|
|
97
|
+
GOOGLE_CLIENT_ID=your_client_id_here.apps.googleusercontent.com
|
|
98
|
+
GOOGLE_CLIENT_SECRET=your_client_secret_here
|
|
99
|
+
GOOGLE_REDIRECT_URL=http://localhost:8080/oauth/callback
|
|
100
|
+
|
|
101
|
+
# This will be generated in the next step
|
|
102
|
+
GOOGLE_REFRESH_TOKEN=
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
#### 4. Get the refresh token
|
|
106
|
+
|
|
107
|
+
Run the setup script:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm run setup:google
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
This script will:
|
|
114
|
+
1. Start a temporary server on port 8080
|
|
115
|
+
2. Show you a URL to authorize the application
|
|
116
|
+
3. Open that URL in your browser
|
|
117
|
+
4. After authorizing, give you a `refresh_token`
|
|
118
|
+
5. Return token `GOOGLE_REFRESH_TOKEN`
|
|
119
|
+
|
|
120
|
+
#### 5. Test the configuration
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npm run build
|
|
124
|
+
node dist/index.js
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
If everything is configured correctly, you'll see:
|
|
128
|
+
```
|
|
129
|
+
✅ Google Calendar configured correctly
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### MCP Client Configuration
|
|
133
|
+
|
|
134
|
+
Add this to your MCP client configuration (e.g., Claude Desktop config):
|
|
135
|
+
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"mcpServers": {
|
|
139
|
+
"google-calendar": {
|
|
140
|
+
"command": "npx",
|
|
141
|
+
//WIP: "args": ["mcp-google-calendar"],
|
|
142
|
+
"env": {
|
|
143
|
+
"GOOGLE_CLIENT_ID": "<your-client-id>",
|
|
144
|
+
"GOOGLE_CLIENT_SECRET": "<your-client-secret>",
|
|
145
|
+
"GOOGLE_REDIRECT_URL": "http://localhost:8080/oauth/callback",
|
|
146
|
+
"GOOGLE_REFRESH_TOKEN": "<your-refresh-token>"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Usage
|
|
154
|
+
|
|
155
|
+
1. Compile TypeScript to JavaScript:
|
|
156
|
+
```bash
|
|
157
|
+
npm run build
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
2. Run the MCP server:
|
|
161
|
+
```bash
|
|
162
|
+
node dist/index.js
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Testing with MCP Inspector
|
|
166
|
+
|
|
167
|
+
You can test and debug this MCP server using the [MCP Inspector](https://github.com/modelcontextprotocol/inspector):
|
|
168
|
+
|
|
169
|
+
1. Make sure you have built the project:
|
|
170
|
+
```bash
|
|
171
|
+
npm run build
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
2. Set up your environment variables in `.env` file:
|
|
175
|
+
```bash
|
|
176
|
+
GOOGLE_CLIENT_ID=your-client-id
|
|
177
|
+
GOOGLE_CLIENT_SECRET=your-client-secret
|
|
178
|
+
GOOGLE_REDIRECT_URL=http://localhost:8080/oauth/callback
|
|
179
|
+
GOOGLE_REFRESH_TOKEN=your-refresh-token
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
3. Update `mcp-inspector-config.json` with your project path:
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"mcpServers": {
|
|
186
|
+
"mcp-google-calendar": {
|
|
187
|
+
"command": "node",
|
|
188
|
+
"args": ["dist/index.js"],
|
|
189
|
+
"env": {
|
|
190
|
+
"GOOGLE_CLIENT_ID": "${GOOGLE_CLIENT_ID}",
|
|
191
|
+
"GOOGLE_CLIENT_SECRET": "${GOOGLE_CLIENT_SECRET}",
|
|
192
|
+
"GOOGLE_REDIRECT_URL": "${GOOGLE_REDIRECT_URL}",
|
|
193
|
+
"GOOGLE_REFRESH_TOKEN": "${GOOGLE_REFRESH_TOKEN}"
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
4. Run the MCP Inspector:
|
|
201
|
+
```bash
|
|
202
|
+
npx @modelcontextprotocol/inspector --config mcp-inspector-config.json
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
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.
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
## Security
|
|
209
|
+
|
|
210
|
+
- **Never** share your `client_secret` or `refresh_token`
|
|
211
|
+
- Add `.env` to your `.gitignore` (already included)
|
|
212
|
+
- Tokens have limited permissions only for Google Calendar
|
|
213
|
+
|
|
214
|
+
## Additional Resources
|
|
215
|
+
|
|
216
|
+
- [Google Calendar API Documentation](https://developers.google.com/calendar/api)
|
|
217
|
+
- [OAuth 2.0 for Web Server Applications](https://developers.google.com/identity/protocols/oauth2/web-server)
|
|
218
|
+
- [Google Cloud Console](https://console.cloud.google.com/)
|
|
219
|
+
- [Model Context Protocol Documentation](https://modelcontextprotocol.io)
|
|
220
|
+
|
|
221
|
+
## Available Tools
|
|
222
|
+
|
|
223
|
+
### google-create-event
|
|
224
|
+
|
|
225
|
+
Creates a new calendar event in Google Calendar.
|
|
226
|
+
|
|
227
|
+
Parameters:
|
|
228
|
+
- `title`: String - Event title/summary
|
|
229
|
+
- `start`: DateTime string - Event start time (ISO 8601 format)
|
|
230
|
+
- `end`: DateTime string - Event end time (ISO 8601 format)
|
|
231
|
+
- `description`: String (optional) - Event description
|
|
232
|
+
- `location`: String (optional) - Event location
|
|
233
|
+
- `calendarId`: String (optional) - Calendar ID (defaults to 'primary')
|
|
234
|
+
|
|
235
|
+
Returns:
|
|
236
|
+
- The unique ID and details of the created event
|
|
237
|
+
|
|
238
|
+
### google-update-event
|
|
239
|
+
|
|
240
|
+
Updates an existing event in Google Calendar.
|
|
241
|
+
|
|
242
|
+
Parameters:
|
|
243
|
+
- `eventId`: String - The unique ID of the event to update
|
|
244
|
+
- `title`: String (optional) - New event title/summary
|
|
245
|
+
- `start`: DateTime string (optional) - New event start time (ISO 8601 format)
|
|
246
|
+
- `end`: DateTime string (optional) - New event end time (ISO 8601 format)
|
|
247
|
+
- `description`: String (optional) - New event description
|
|
248
|
+
- `location`: String (optional) - New event location
|
|
249
|
+
- `calendarId`: String (optional) - Calendar ID (defaults to 'primary')
|
|
250
|
+
|
|
251
|
+
Returns:
|
|
252
|
+
- The updated event details
|
|
253
|
+
|
|
254
|
+
### google-list-events
|
|
255
|
+
|
|
256
|
+
Lists events within a specified timeframe from Google Calendar.
|
|
257
|
+
|
|
258
|
+
Parameters:
|
|
259
|
+
- `timeMin`: DateTime string (optional) - Start of the timeframe (ISO 8601 format)
|
|
260
|
+
- `timeMax`: DateTime string (optional) - End of the timeframe (ISO 8601 format)
|
|
261
|
+
- `calendarId`: String (optional) - Calendar ID (defaults to 'primary')
|
|
262
|
+
- `maxResults`: Number (optional) - Maximum number of events to return (default: 10)
|
|
263
|
+
|
|
264
|
+
Returns:
|
|
265
|
+
- A list of events that fall within the given timeframe
|
|
266
|
+
|
|
267
|
+
### google-search-events
|
|
268
|
+
|
|
269
|
+
Searches for events in Google Calendar by text query.
|
|
270
|
+
|
|
271
|
+
Parameters:
|
|
272
|
+
- `query`: String - Search query
|
|
273
|
+
- `calendarId`: String (optional) - Calendar ID (defaults to 'primary')
|
|
274
|
+
- `maxResults`: Number (optional) - Maximum number of events to return (default: 10)
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
- A list of events matching the search query
|
|
278
|
+
|
|
279
|
+
### google-delete-event
|
|
280
|
+
|
|
281
|
+
Deletes an event from Google Calendar.
|
|
282
|
+
|
|
283
|
+
Parameters:
|
|
284
|
+
- `eventId`: String - The unique ID of the event to delete
|
|
285
|
+
- `calendarId`: String (optional) - Calendar ID (defaults to 'primary')
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
- Confirmation of deletion
|
|
289
|
+
|
|
290
|
+
### google-list-calendars
|
|
291
|
+
|
|
292
|
+
Lists all calendars available to the user.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
- A list of calendars with their IDs and names
|
|
296
|
+
|
|
297
|
+
### google-get-current-datetime
|
|
298
|
+
|
|
299
|
+
Gets the current date and time in ISO 8601 format. Useful for references when creating or searching for events.
|
|
300
|
+
|
|
301
|
+
Parameters:
|
|
302
|
+
- `timezone`: String (optional) - Timezone in IANA format (e.g., America/New_York, Europe/Madrid). Defaults to 'Atlantic/Canary'.
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
- Current date and time information including ISO 8601 format, timestamp, local time, and timezone details
|
|
306
|
+
|
|
307
|
+
## License
|
|
308
|
+
|
|
309
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "dotenv/config";
|
|
3
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { GoogleCalendarClient } from "./tools/google-calendar.js";
|
|
6
|
+
import { registerGoogleCalendarTools } from "./tools/google-calendar-mcp.js";
|
|
7
|
+
const server = new McpServer({
|
|
8
|
+
name: "google-calendar-mcp",
|
|
9
|
+
version: "1.0.0",
|
|
10
|
+
});
|
|
11
|
+
async function main() {
|
|
12
|
+
console.error('Configurando Google Calendar API...');
|
|
13
|
+
// Verificar que las credenciales de Google estén configuradas
|
|
14
|
+
if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET) {
|
|
15
|
+
console.error('Error: GOOGLE_CLIENT_ID y GOOGLE_CLIENT_SECRET son requeridos');
|
|
16
|
+
process.exit(1);
|
|
17
|
+
}
|
|
18
|
+
const googleClient = new GoogleCalendarClient({
|
|
19
|
+
clientId: process.env.GOOGLE_CLIENT_ID,
|
|
20
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
|
|
21
|
+
redirectUrl: process.env.GOOGLE_REDIRECT_URL || 'http://localhost:8080/oauth/callback',
|
|
22
|
+
refreshToken: process.env.GOOGLE_REFRESH_TOKEN,
|
|
23
|
+
});
|
|
24
|
+
// Si no tenemos refresh token, mostrar URL de autorización
|
|
25
|
+
if (!process.env.GOOGLE_REFRESH_TOKEN) {
|
|
26
|
+
console.error('\n🔑 AUTORIZACIÓN REQUERIDA');
|
|
27
|
+
console.error('Visita la siguiente URL para autorizar la aplicación:');
|
|
28
|
+
console.error(googleClient.getAuthUrl());
|
|
29
|
+
console.error('\nDespués de autorizar, agrega el refresh_token al archivo .env como GOOGLE_REFRESH_TOKEN=...\n');
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
registerGoogleCalendarTools(googleClient, server);
|
|
33
|
+
console.log('Google Calendar configurado correctamente');
|
|
34
|
+
// Start receiving messages on stdin and sending messages on stdout
|
|
35
|
+
const transport = new StdioServerTransport();
|
|
36
|
+
await server.connect(transport);
|
|
37
|
+
}
|
|
38
|
+
main().catch((error) => {
|
|
39
|
+
console.error('Error al iniciar el servidor:', error);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { registerGoogleListCalendars } from './google-list-calendars.js';
|
|
2
|
+
import { registerGoogleListEvents } from './google-list-events.js';
|
|
3
|
+
import { registerGoogleCreateEvent } from './google-create-event.js';
|
|
4
|
+
import { registerGoogleUpdateEvent } from './google-update-event.js';
|
|
5
|
+
import { registerGoogleDeleteEvent } from './google-delete-event.js';
|
|
6
|
+
import { registerGoogleSearchEvents } from './google-search-events.js';
|
|
7
|
+
import { registerGoogleGetCurrentDatetime } from './google-get-current-datetime.js';
|
|
8
|
+
export function registerGoogleCalendarTools(googleClient, server) {
|
|
9
|
+
registerGoogleListCalendars(googleClient, server);
|
|
10
|
+
registerGoogleListEvents(googleClient, server);
|
|
11
|
+
registerGoogleCreateEvent(googleClient, server);
|
|
12
|
+
registerGoogleUpdateEvent(googleClient, server);
|
|
13
|
+
registerGoogleDeleteEvent(googleClient, server);
|
|
14
|
+
registerGoogleSearchEvents(googleClient, server);
|
|
15
|
+
registerGoogleGetCurrentDatetime(googleClient, server);
|
|
16
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { google } from 'googleapis';
|
|
2
|
+
export class GoogleCalendarClient {
|
|
3
|
+
auth;
|
|
4
|
+
calendar;
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.auth = new google.auth.OAuth2(config.clientId, config.clientSecret, config.redirectUrl);
|
|
7
|
+
if (config.refreshToken) {
|
|
8
|
+
this.auth.setCredentials({
|
|
9
|
+
refresh_token: config.refreshToken,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
this.calendar = google.calendar({ version: 'v3', auth: this.auth });
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Genera URL de autorización para obtener el token inicial
|
|
16
|
+
*/
|
|
17
|
+
getAuthUrl() {
|
|
18
|
+
const scopes = [
|
|
19
|
+
'https://www.googleapis.com/auth/calendar',
|
|
20
|
+
'https://www.googleapis.com/auth/calendar.events'
|
|
21
|
+
];
|
|
22
|
+
return this.auth.generateAuthUrl({
|
|
23
|
+
access_type: 'offline',
|
|
24
|
+
scope: scopes,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Intercambia el código de autorización por tokens
|
|
29
|
+
*/
|
|
30
|
+
async getTokens(code) {
|
|
31
|
+
const { tokens } = await this.auth.getToken(code);
|
|
32
|
+
this.auth.setCredentials(tokens);
|
|
33
|
+
return tokens;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Lista todos los calendarios del usuario
|
|
37
|
+
*/
|
|
38
|
+
async listCalendars() {
|
|
39
|
+
try {
|
|
40
|
+
const response = await this.calendar.calendarList.list();
|
|
41
|
+
return response.data.items || [];
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error('Error al listar calendarios:', error);
|
|
45
|
+
throw error;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Lista eventos de un calendario específico
|
|
50
|
+
*/
|
|
51
|
+
async listEvents(calendarId = 'primary', timeMin, timeMax, maxResults = 10) {
|
|
52
|
+
try {
|
|
53
|
+
const response = await this.calendar.events.list({
|
|
54
|
+
calendarId,
|
|
55
|
+
timeMin: timeMin || new Date().toISOString(),
|
|
56
|
+
timeMax,
|
|
57
|
+
maxResults,
|
|
58
|
+
singleEvents: true,
|
|
59
|
+
orderBy: 'startTime',
|
|
60
|
+
});
|
|
61
|
+
return response.data.items || [];
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
console.error('Error al listar eventos:', error);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Crea un nuevo evento
|
|
70
|
+
*/
|
|
71
|
+
async createEvent(calendarId = 'primary', event) {
|
|
72
|
+
try {
|
|
73
|
+
const response = await this.calendar.events.insert({
|
|
74
|
+
calendarId,
|
|
75
|
+
requestBody: event,
|
|
76
|
+
});
|
|
77
|
+
return response.data;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
console.error('Error al crear evento:', error);
|
|
81
|
+
throw error;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Actualiza un evento existente
|
|
86
|
+
*/
|
|
87
|
+
async updateEvent(calendarId = 'primary', eventId, event) {
|
|
88
|
+
try {
|
|
89
|
+
const response = await this.calendar.events.update({
|
|
90
|
+
calendarId,
|
|
91
|
+
eventId,
|
|
92
|
+
requestBody: event,
|
|
93
|
+
});
|
|
94
|
+
return response.data;
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
console.error('Error al actualizar evento:', error);
|
|
98
|
+
throw error;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Elimina un evento
|
|
103
|
+
*/
|
|
104
|
+
async deleteEvent(calendarId = 'primary', eventId) {
|
|
105
|
+
try {
|
|
106
|
+
await this.calendar.events.delete({
|
|
107
|
+
calendarId,
|
|
108
|
+
eventId,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
console.error('Error al eliminar evento:', error);
|
|
113
|
+
throw error;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Busca eventos por texto
|
|
118
|
+
*/
|
|
119
|
+
async searchEvents(calendarId = 'primary', query, maxResults = 10) {
|
|
120
|
+
try {
|
|
121
|
+
const response = await this.calendar.events.list({
|
|
122
|
+
calendarId,
|
|
123
|
+
q: query,
|
|
124
|
+
maxResults,
|
|
125
|
+
singleEvents: true,
|
|
126
|
+
orderBy: 'startTime',
|
|
127
|
+
});
|
|
128
|
+
return response.data.items || [];
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error('Error al buscar eventos:', error);
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Función helper para crear eventos con formato simplificado
|
|
138
|
+
*/
|
|
139
|
+
export function createSimpleEvent(title, start, end, description, location) {
|
|
140
|
+
return {
|
|
141
|
+
summary: title,
|
|
142
|
+
description,
|
|
143
|
+
location,
|
|
144
|
+
start: {
|
|
145
|
+
dateTime: start,
|
|
146
|
+
timeZone: 'America/Mexico_City', // Ajusta según tu zona horaria
|
|
147
|
+
},
|
|
148
|
+
end: {
|
|
149
|
+
dateTime: end,
|
|
150
|
+
timeZone: 'America/Mexico_City',
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createSimpleEvent } from './google-calendar.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
export function registerGoogleCreateEvent(googleClient, server) {
|
|
4
|
+
server.tool('google_create_event', 'Crea un nuevo evento en Google Calendar', {
|
|
5
|
+
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
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'),
|
|
9
|
+
description: z.string().optional().describe('Descripción del evento (opcional)'),
|
|
10
|
+
location: z.string().optional().describe('Ubicación del evento (opcional)'),
|
|
11
|
+
}, async (args) => {
|
|
12
|
+
try {
|
|
13
|
+
const { calendarId = 'primary', title, start, end, description, location, } = args;
|
|
14
|
+
if (!title || !start || !end) {
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: 'text',
|
|
19
|
+
text: 'Error: title, start y end son requeridos',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
isError: true,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const event = createSimpleEvent(title, start, end, description, location);
|
|
26
|
+
const createdEvent = await googleClient.createEvent(calendarId, event);
|
|
27
|
+
return {
|
|
28
|
+
content: [
|
|
29
|
+
{
|
|
30
|
+
type: 'text',
|
|
31
|
+
text: `Evento creado exitosamente:\nID: ${createdEvent.id}\nTítulo: ${createdEvent.summary}\nInicio: ${createdEvent.start?.dateTime}\nFin: ${createdEvent.end?.dateTime}`,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
|
|
38
|
+
return {
|
|
39
|
+
content: [
|
|
40
|
+
{
|
|
41
|
+
type: 'text',
|
|
42
|
+
text: `Error al crear evento: ${errorMessage}`,
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
isError: true,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerGoogleDeleteEvent(googleClient, server) {
|
|
3
|
+
server.tool('google_delete_event', 'Elimina un evento de Google Calendar', {
|
|
4
|
+
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
5
|
+
eventId: z.string().describe('ID del evento a eliminar'),
|
|
6
|
+
}, async (args) => {
|
|
7
|
+
try {
|
|
8
|
+
const { calendarId = 'primary', eventId } = args;
|
|
9
|
+
if (!eventId) {
|
|
10
|
+
return {
|
|
11
|
+
content: [
|
|
12
|
+
{
|
|
13
|
+
type: 'text',
|
|
14
|
+
text: 'Error: eventId es requerido',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
isError: true,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
await googleClient.deleteEvent(calendarId, eventId);
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
type: 'text',
|
|
25
|
+
text: `Evento ${eventId} eliminado exitosamente`,
|
|
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 eliminar evento: ${errorMessage}`,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
isError: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerGoogleGetCurrentDatetime(googleClient, server) {
|
|
3
|
+
server.tool('google_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, Europe/Madrid). Por defecto usa Atlantic/Canary.'),
|
|
5
|
+
}, async (args) => {
|
|
6
|
+
try {
|
|
7
|
+
const { timezone = 'Atlantic/Canary' } = args;
|
|
8
|
+
const now = new Date();
|
|
9
|
+
// Información básica
|
|
10
|
+
const isoString = now.toISOString();
|
|
11
|
+
const timestamp = now.getTime();
|
|
12
|
+
// Si se proporciona una zona horaria, formatear en esa zona
|
|
13
|
+
let localTime;
|
|
14
|
+
let formattedTime;
|
|
15
|
+
try {
|
|
16
|
+
localTime = now.toLocaleString('es-ES', {
|
|
17
|
+
timeZone: timezone,
|
|
18
|
+
year: 'numeric',
|
|
19
|
+
month: '2-digit',
|
|
20
|
+
day: '2-digit',
|
|
21
|
+
hour: '2-digit',
|
|
22
|
+
minute: '2-digit',
|
|
23
|
+
second: '2-digit',
|
|
24
|
+
hour12: false,
|
|
25
|
+
});
|
|
26
|
+
formattedTime = now.toLocaleString('es-ES', {
|
|
27
|
+
timeZone: timezone,
|
|
28
|
+
weekday: 'long',
|
|
29
|
+
year: 'numeric',
|
|
30
|
+
month: 'long',
|
|
31
|
+
day: 'numeric',
|
|
32
|
+
hour: '2-digit',
|
|
33
|
+
minute: '2-digit',
|
|
34
|
+
second: '2-digit',
|
|
35
|
+
hour12: false,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return {
|
|
40
|
+
content: [
|
|
41
|
+
{
|
|
42
|
+
type: 'text',
|
|
43
|
+
text: `Error: Zona horaria inválida '${timezone}'. Usa formato IANA (ej: America/New_York, Europe/Madrid, Atlantic/Canary)`,
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
isError: true,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const result = {
|
|
50
|
+
iso8601: isoString,
|
|
51
|
+
timestamp: timestamp,
|
|
52
|
+
timezone: timezone,
|
|
53
|
+
localTime: localTime,
|
|
54
|
+
formatted: formattedTime,
|
|
55
|
+
unix: Math.floor(timestamp / 1000),
|
|
56
|
+
date: {
|
|
57
|
+
year: now.getUTCFullYear(),
|
|
58
|
+
month: now.getUTCMonth() + 1,
|
|
59
|
+
day: now.getUTCDate(),
|
|
60
|
+
},
|
|
61
|
+
time: {
|
|
62
|
+
hour: now.getUTCHours(),
|
|
63
|
+
minute: now.getUTCMinutes(),
|
|
64
|
+
second: now.getUTCSeconds(),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
return {
|
|
68
|
+
content: [
|
|
69
|
+
{
|
|
70
|
+
type: 'text',
|
|
71
|
+
text: JSON.stringify(result, null, 2),
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return {
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: 'text',
|
|
81
|
+
text: `Error al obtener la fecha y hora actual: ${error instanceof Error ? error.message : String(error)}`,
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
isError: true,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function registerGoogleListCalendars(googleClient, server) {
|
|
2
|
+
server.tool('google_list_calendars', 'Lista todos los calendarios disponibles en Google Calendar', async () => {
|
|
3
|
+
try {
|
|
4
|
+
const calendars = await googleClient.listCalendars();
|
|
5
|
+
const data = calendars.map((cal) => ({
|
|
6
|
+
id: cal.id,
|
|
7
|
+
summary: cal.summary,
|
|
8
|
+
description: cal.description,
|
|
9
|
+
timeZone: cal.timeZone,
|
|
10
|
+
accessRole: cal.accessRole,
|
|
11
|
+
}));
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: 'text',
|
|
16
|
+
text: JSON.stringify(data, null, 2),
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
catch (error) {
|
|
22
|
+
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{
|
|
26
|
+
type: 'text',
|
|
27
|
+
text: `Error al listar calendarios: ${errorMessage}`,
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
isError: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerGoogleListEvents(googleClient, server) {
|
|
3
|
+
server.tool('google_list_events', 'Lista eventos de un calendario de Google Calendar', {
|
|
4
|
+
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'),
|
|
7
|
+
maxResults: z.number().optional().describe('Número máximo de resultados (por defecto: 10)'),
|
|
8
|
+
}, async (args) => {
|
|
9
|
+
try {
|
|
10
|
+
const { calendarId = 'primary', timeMin, timeMax, maxResults = 10, } = args;
|
|
11
|
+
const events = await googleClient.listEvents(calendarId, timeMin, timeMax, maxResults);
|
|
12
|
+
const data = events.map((event) => ({
|
|
13
|
+
id: event.id,
|
|
14
|
+
summary: event.summary,
|
|
15
|
+
description: event.description,
|
|
16
|
+
location: event.location,
|
|
17
|
+
start: event.start,
|
|
18
|
+
end: event.end,
|
|
19
|
+
status: event.status,
|
|
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 eventos: ${errorMessage}`,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
isError: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerGoogleSearchEvents(googleClient, server) {
|
|
3
|
+
server.tool('google_search_events', 'Busca eventos en Google Calendar por texto', {
|
|
4
|
+
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
5
|
+
query: z.string().describe('Texto a buscar en los eventos'),
|
|
6
|
+
maxResults: z.number().optional().describe('Número máximo de resultados (por defecto: 10)'),
|
|
7
|
+
}, async (args) => {
|
|
8
|
+
try {
|
|
9
|
+
const { calendarId = 'primary', query, maxResults = 10, } = args;
|
|
10
|
+
if (!query) {
|
|
11
|
+
return {
|
|
12
|
+
content: [
|
|
13
|
+
{
|
|
14
|
+
type: 'text',
|
|
15
|
+
text: 'Error: query es requerido',
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
isError: true,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
const events = await googleClient.searchEvents(calendarId, query, maxResults);
|
|
22
|
+
const data = events.map((event) => ({
|
|
23
|
+
id: event.id,
|
|
24
|
+
summary: event.summary,
|
|
25
|
+
description: event.description,
|
|
26
|
+
location: event.location,
|
|
27
|
+
start: event.start,
|
|
28
|
+
end: event.end,
|
|
29
|
+
}));
|
|
30
|
+
return {
|
|
31
|
+
content: [
|
|
32
|
+
{
|
|
33
|
+
type: 'text',
|
|
34
|
+
text: JSON.stringify(data, null, 2),
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: 'text',
|
|
45
|
+
text: `Error al buscar eventos: ${errorMessage}`,
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
isError: true,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export function registerGoogleUpdateEvent(googleClient, server) {
|
|
3
|
+
server.tool('google_update_event', 'Actualiza un evento existente en Google Calendar', {
|
|
4
|
+
calendarId: z.string().optional().describe('ID del calendario (por defecto: primary)'),
|
|
5
|
+
eventId: z.string().describe('ID del evento a actualizar'),
|
|
6
|
+
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'),
|
|
9
|
+
description: z.string().optional().describe('Nueva descripción del evento'),
|
|
10
|
+
location: z.string().optional().describe('Nueva ubicación del evento'),
|
|
11
|
+
}, async (args) => {
|
|
12
|
+
try {
|
|
13
|
+
const { calendarId = 'primary', eventId, title, start, end, description, location, } = args;
|
|
14
|
+
if (!eventId) {
|
|
15
|
+
return {
|
|
16
|
+
content: [
|
|
17
|
+
{
|
|
18
|
+
type: 'text',
|
|
19
|
+
text: 'Error: eventId es requerido',
|
|
20
|
+
},
|
|
21
|
+
],
|
|
22
|
+
isError: true,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
// Construir el objeto de evento con solo los campos proporcionados
|
|
26
|
+
const event = {};
|
|
27
|
+
if (title !== undefined) {
|
|
28
|
+
event.summary = title;
|
|
29
|
+
}
|
|
30
|
+
if (description !== undefined) {
|
|
31
|
+
event.description = description;
|
|
32
|
+
}
|
|
33
|
+
if (location !== undefined) {
|
|
34
|
+
event.location = location;
|
|
35
|
+
}
|
|
36
|
+
if (start !== undefined) {
|
|
37
|
+
event.start = {
|
|
38
|
+
dateTime: start,
|
|
39
|
+
timeZone: 'America/Mexico_City',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (end !== undefined) {
|
|
43
|
+
event.end = {
|
|
44
|
+
dateTime: end,
|
|
45
|
+
timeZone: 'America/Mexico_City',
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const updatedEvent = await googleClient.updateEvent(calendarId, eventId, event);
|
|
49
|
+
return {
|
|
50
|
+
content: [
|
|
51
|
+
{
|
|
52
|
+
type: 'text',
|
|
53
|
+
text: `Evento actualizado exitosamente:\nID: ${updatedEvent.id}\nTítulo: ${updatedEvent.summary}\nInicio: ${updatedEvent.start?.dateTime}\nFin: ${updatedEvent.end?.dateTime}`,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
const errorMessage = error instanceof Error ? error.message : 'Error desconocido';
|
|
60
|
+
return {
|
|
61
|
+
content: [
|
|
62
|
+
{
|
|
63
|
+
type: 'text',
|
|
64
|
+
text: `Error al actualizar evento: ${errorMessage}`,
|
|
65
|
+
},
|
|
66
|
+
],
|
|
67
|
+
isError: true,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@pablosr/mcp-google-calendar",
|
|
3
|
+
"description": "A Google Calendar (MCP) server to expose calendar operations as tools for LLM.",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"mcp-google-calendar": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc && shx chmod +x dist/*.js",
|
|
15
|
+
"watch": "tsc --watch",
|
|
16
|
+
"lint": "eslint src/",
|
|
17
|
+
"lint:fix": "eslint src/ --fix",
|
|
18
|
+
"format": "prettier --write src/.",
|
|
19
|
+
"format:check": "prettier --check src/.",
|
|
20
|
+
"setup:google": "node scripts/setup-google-oauth.js"
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/PabloSR06/mcp-google-calendar.git"
|
|
25
|
+
},
|
|
26
|
+
"bugs": {
|
|
27
|
+
"url": "https://github.com/PabloSR06/mcp-google-calendar/issues"
|
|
28
|
+
},
|
|
29
|
+
"homepage": "https://github.com/PabloSR06/mcp-google-calendar#readme",
|
|
30
|
+
"author": "",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@google-cloud/local-auth": "^3.0.1",
|
|
34
|
+
"@modelcontextprotocol/sdk": "^1.13.0",
|
|
35
|
+
"dotenv": "^16.5.0",
|
|
36
|
+
"googleapis": "^162.0.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@eslint/js": "^9.29.0",
|
|
40
|
+
"@types/node": "^24.0.3",
|
|
41
|
+
"eslint": "^9.29.0",
|
|
42
|
+
"eslint-config-prettier": "^10.1.5",
|
|
43
|
+
"prettier": "3.5.3",
|
|
44
|
+
"shx": "^0.4.0",
|
|
45
|
+
"typescript": "^5.8.3",
|
|
46
|
+
"typescript-eslint": "^8.34.1"
|
|
47
|
+
}
|
|
48
|
+
}
|