├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── logo.jpg ├── package-lock.json ├── package.json ├── smithery.yaml ├── src ├── Tasks.ts └── index.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .gtasks-server-credentials.json 4 | gcp-oauth.keys.json 5 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | # Stage 1: Build the TypeScript project 3 | FROM node:18-alpine AS builder 4 | 5 | # Set the working directory 6 | WORKDIR /app 7 | 8 | # Copy package.json and package-lock.json 9 | COPY package.json package-lock.json ./ 10 | 11 | # Install dependencies 12 | RUN npm install --ignore-scripts 13 | 14 | # Copy the rest of the application code 15 | COPY . . 16 | 17 | # Build the application 18 | RUN npm run build 19 | 20 | # Stage 2: Create the final image for running the app 21 | FROM node:18-alpine 22 | 23 | # Set the working directory 24 | WORKDIR /app 25 | 26 | # Copy built files from the builder stage 27 | COPY --from=builder /app/dist /app/dist 28 | COPY --from=builder /app/package.json /app/package-lock.json /app 29 | 30 | # Install only production dependencies 31 | RUN npm ci --omit=dev 32 | 33 | # Set the command to run the app 34 | CMD ["node", "dist/index.js"] 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Zach Caceres 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Google Tasks MCP Server 2 | 3 | ![gtasks mcp logo](./logo.jpg) 4 | [![smithery badge](https://smithery.ai/badge/@zcaceres/gtasks)](https://smithery.ai/server/@zcaceres/gtasks) 5 | 6 | This MCP server integrates with Google Tasks to allow listing, reading, searching, creating, updating, and deleting tasks. 7 | 8 | ## Components 9 | 10 | ### Tools 11 | 12 | - **search** 13 | - Search for tasks in Google Tasks 14 | - Input: `query` (string): Search query 15 | - Returns matching tasks with details 16 | 17 | - **list** 18 | - List all tasks in Google Tasks 19 | - Optional input: `cursor` (string): Cursor for pagination 20 | - Returns a list of all tasks 21 | 22 | - **create** 23 | - Create a new task in Google Tasks 24 | - Input: 25 | - `taskListId` (string, optional): Task list ID 26 | - `title` (string, required): Task title 27 | - `notes` (string, optional): Task notes 28 | - `due` (string, optional): Due date 29 | - Returns confirmation of task creation 30 | 31 | - **update** 32 | - Update an existing task in Google Tasks 33 | - Input: 34 | - `taskListId` (string, optional): Task list ID 35 | - `id` (string, required): Task ID 36 | - `uri` (string, required): Task URI 37 | - `title` (string, optional): New task title 38 | - `notes` (string, optional): New task notes 39 | - `status` (string, optional): New task status ("needsAction" or "completed") 40 | - `due` (string, optional): New due date 41 | - Returns confirmation of task update 42 | 43 | - **delete** 44 | - Delete a task in Google Tasks 45 | - Input: 46 | - `taskListId` (string, required): Task list ID 47 | - `id` (string, required): Task ID 48 | - Returns confirmation of task deletion 49 | 50 | - **clear** 51 | - Clear completed tasks from a Google Tasks task list 52 | - Input: `taskListId` (string, required): Task list ID 53 | - Returns confirmation of cleared tasks 54 | 55 | ### Resources 56 | 57 | The server provides access to Google Tasks resources: 58 | 59 | - **Tasks** (`gtasks:///`) 60 | - Represents individual tasks in Google Tasks 61 | - Supports reading task details including title, status, due date, notes, and other metadata 62 | - Can be listed, read, created, updated, and deleted using the provided tools 63 | 64 | ## Getting started 65 | 66 | 1. [Create a new Google Cloud project](https://console.cloud.google.com/projectcreate) 67 | 2. [Enable the Google Tasks API](https://console.cloud.google.com/workspace-api/products) 68 | 3. [Configure an OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) ("internal" is fine for testing) 69 | 4. Add scopes `https://www.googleapis.com/auth/tasks` 70 | 5. [Create an OAuth Client ID](https://console.cloud.google.com/apis/credentials/oauthclient) for application type "Desktop App" 71 | 6. Download the JSON file of your client's OAuth keys 72 | 7. Rename the key file to `gcp-oauth.keys.json` and place into the root of this repo (i.e. `gcp-oauth.keys.json`) 73 | 74 | Make sure to build the server with either `npm run build` or `npm run watch`. 75 | 76 | ### Installing via Smithery 77 | 78 | To install Google Tasks Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@zcaceres/gtasks): 79 | 80 | ```bash 81 | npx -y @smithery/cli install @zcaceres/gtasks --client claude 82 | ``` 83 | 84 | ### Authentication 85 | 86 | To authenticate and save credentials: 87 | 88 | 1. Run the server with the `auth` argument: `npm run start auth` 89 | 2. This will open an authentication flow in your system browser 90 | 3. Complete the authentication process 91 | 4. Credentials will be saved in the root of this repo (i.e. `.gdrive-server-credentials.json`) 92 | 93 | ### Usage with Desktop App 94 | 95 | To integrate this server with the desktop app, add the following to your app's server configuration: 96 | 97 | ```json 98 | { 99 | "mcpServers": { 100 | "gtasks": { 101 | "command": "/opt/homebrew/bin/node", 102 | "args": [ 103 | "{ABSOLUTE PATH TO FILE HERE}/dist/index.js" 104 | ] 105 | } 106 | } 107 | } 108 | ``` 109 | -------------------------------------------------------------------------------- /logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zcaceres/gtasks-mcp/c4c903f3de42aecbbb0df61f4cb39a93229d957f/logo.jpg -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modelcontextprotocol/server-gdrive", 3 | "version": "0.6.2", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "@modelcontextprotocol/server-gdrive", 9 | "version": "0.6.2", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@google-cloud/local-auth": "^3.0.1", 13 | "@modelcontextprotocol/sdk": "1.0.1", 14 | "googleapis": "^144.0.0" 15 | }, 16 | "bin": { 17 | "mcp-server-gdrive": "dist/index.js" 18 | }, 19 | "devDependencies": { 20 | "@types/node": "^22.9.3", 21 | "shx": "^0.3.4", 22 | "typescript": "^5.6.2" 23 | } 24 | }, 25 | "node_modules/@google-cloud/local-auth": { 26 | "version": "3.0.1", 27 | "resolved": "https://registry.npmjs.org/@google-cloud/local-auth/-/local-auth-3.0.1.tgz", 28 | "integrity": "sha512-YJ3GFbksfHyEarbVHPSCzhKpjbnlAhdzg2SEf79l6ODukrSM1qUOqfopY232Xkw26huKSndyzmJz+A6b2WYn7Q==", 29 | "license": "Apache-2.0", 30 | "dependencies": { 31 | "arrify": "^2.0.1", 32 | "google-auth-library": "^9.0.0", 33 | "open": "^7.0.3", 34 | "server-destroy": "^1.0.1" 35 | }, 36 | "engines": { 37 | "node": ">=14.0.0" 38 | } 39 | }, 40 | "node_modules/@modelcontextprotocol/sdk": { 41 | "version": "1.0.1", 42 | "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.0.1.tgz", 43 | "integrity": "sha512-slLdFaxQJ9AlRg+hw28iiTtGvShAOgOKXcD0F91nUcRYiOMuS9ZBYjcdNZRXW9G5JQ511GRTdUy1zQVZDpJ+4w==", 44 | "license": "MIT", 45 | "dependencies": { 46 | "content-type": "^1.0.5", 47 | "raw-body": "^3.0.0", 48 | "zod": "^3.23.8" 49 | } 50 | }, 51 | "node_modules/@types/node": { 52 | "version": "22.10.2", 53 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", 54 | "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", 55 | "dev": true, 56 | "license": "MIT", 57 | "dependencies": { 58 | "undici-types": "~6.20.0" 59 | } 60 | }, 61 | "node_modules/agent-base": { 62 | "version": "7.1.3", 63 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", 64 | "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", 65 | "license": "MIT", 66 | "engines": { 67 | "node": ">= 14" 68 | } 69 | }, 70 | "node_modules/arrify": { 71 | "version": "2.0.1", 72 | "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", 73 | "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", 74 | "license": "MIT", 75 | "engines": { 76 | "node": ">=8" 77 | } 78 | }, 79 | "node_modules/balanced-match": { 80 | "version": "1.0.2", 81 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", 82 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", 83 | "dev": true, 84 | "license": "MIT" 85 | }, 86 | "node_modules/base64-js": { 87 | "version": "1.5.1", 88 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 89 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 90 | "funding": [ 91 | { 92 | "type": "github", 93 | "url": "https://github.com/sponsors/feross" 94 | }, 95 | { 96 | "type": "patreon", 97 | "url": "https://www.patreon.com/feross" 98 | }, 99 | { 100 | "type": "consulting", 101 | "url": "https://feross.org/support" 102 | } 103 | ], 104 | "license": "MIT" 105 | }, 106 | "node_modules/bignumber.js": { 107 | "version": "9.1.2", 108 | "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.1.2.tgz", 109 | "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==", 110 | "license": "MIT", 111 | "engines": { 112 | "node": "*" 113 | } 114 | }, 115 | "node_modules/brace-expansion": { 116 | "version": "1.1.11", 117 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 118 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 119 | "dev": true, 120 | "license": "MIT", 121 | "dependencies": { 122 | "balanced-match": "^1.0.0", 123 | "concat-map": "0.0.1" 124 | } 125 | }, 126 | "node_modules/buffer-equal-constant-time": { 127 | "version": "1.0.1", 128 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", 129 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", 130 | "license": "BSD-3-Clause" 131 | }, 132 | "node_modules/bytes": { 133 | "version": "3.1.2", 134 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 135 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 136 | "license": "MIT", 137 | "engines": { 138 | "node": ">= 0.8" 139 | } 140 | }, 141 | "node_modules/call-bind-apply-helpers": { 142 | "version": "1.0.1", 143 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", 144 | "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", 145 | "license": "MIT", 146 | "dependencies": { 147 | "es-errors": "^1.3.0", 148 | "function-bind": "^1.1.2" 149 | }, 150 | "engines": { 151 | "node": ">= 0.4" 152 | } 153 | }, 154 | "node_modules/call-bound": { 155 | "version": "1.0.3", 156 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", 157 | "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", 158 | "license": "MIT", 159 | "dependencies": { 160 | "call-bind-apply-helpers": "^1.0.1", 161 | "get-intrinsic": "^1.2.6" 162 | }, 163 | "engines": { 164 | "node": ">= 0.4" 165 | }, 166 | "funding": { 167 | "url": "https://github.com/sponsors/ljharb" 168 | } 169 | }, 170 | "node_modules/concat-map": { 171 | "version": "0.0.1", 172 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 173 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", 174 | "dev": true, 175 | "license": "MIT" 176 | }, 177 | "node_modules/content-type": { 178 | "version": "1.0.5", 179 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 180 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 181 | "license": "MIT", 182 | "engines": { 183 | "node": ">= 0.6" 184 | } 185 | }, 186 | "node_modules/debug": { 187 | "version": "4.4.0", 188 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 189 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 190 | "license": "MIT", 191 | "dependencies": { 192 | "ms": "^2.1.3" 193 | }, 194 | "engines": { 195 | "node": ">=6.0" 196 | }, 197 | "peerDependenciesMeta": { 198 | "supports-color": { 199 | "optional": true 200 | } 201 | } 202 | }, 203 | "node_modules/depd": { 204 | "version": "2.0.0", 205 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 206 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 207 | "license": "MIT", 208 | "engines": { 209 | "node": ">= 0.8" 210 | } 211 | }, 212 | "node_modules/dunder-proto": { 213 | "version": "1.0.1", 214 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 215 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 216 | "license": "MIT", 217 | "dependencies": { 218 | "call-bind-apply-helpers": "^1.0.1", 219 | "es-errors": "^1.3.0", 220 | "gopd": "^1.2.0" 221 | }, 222 | "engines": { 223 | "node": ">= 0.4" 224 | } 225 | }, 226 | "node_modules/ecdsa-sig-formatter": { 227 | "version": "1.0.11", 228 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", 229 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", 230 | "license": "Apache-2.0", 231 | "dependencies": { 232 | "safe-buffer": "^5.0.1" 233 | } 234 | }, 235 | "node_modules/es-define-property": { 236 | "version": "1.0.1", 237 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 238 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 239 | "license": "MIT", 240 | "engines": { 241 | "node": ">= 0.4" 242 | } 243 | }, 244 | "node_modules/es-errors": { 245 | "version": "1.3.0", 246 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 247 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 248 | "license": "MIT", 249 | "engines": { 250 | "node": ">= 0.4" 251 | } 252 | }, 253 | "node_modules/es-object-atoms": { 254 | "version": "1.0.0", 255 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", 256 | "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", 257 | "license": "MIT", 258 | "dependencies": { 259 | "es-errors": "^1.3.0" 260 | }, 261 | "engines": { 262 | "node": ">= 0.4" 263 | } 264 | }, 265 | "node_modules/extend": { 266 | "version": "3.0.2", 267 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", 268 | "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", 269 | "license": "MIT" 270 | }, 271 | "node_modules/fs.realpath": { 272 | "version": "1.0.0", 273 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 274 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", 275 | "dev": true, 276 | "license": "ISC" 277 | }, 278 | "node_modules/function-bind": { 279 | "version": "1.1.2", 280 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 281 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 282 | "license": "MIT", 283 | "funding": { 284 | "url": "https://github.com/sponsors/ljharb" 285 | } 286 | }, 287 | "node_modules/gaxios": { 288 | "version": "6.7.1", 289 | "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", 290 | "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", 291 | "license": "Apache-2.0", 292 | "dependencies": { 293 | "extend": "^3.0.2", 294 | "https-proxy-agent": "^7.0.1", 295 | "is-stream": "^2.0.0", 296 | "node-fetch": "^2.6.9", 297 | "uuid": "^9.0.1" 298 | }, 299 | "engines": { 300 | "node": ">=14" 301 | } 302 | }, 303 | "node_modules/gcp-metadata": { 304 | "version": "6.1.0", 305 | "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.0.tgz", 306 | "integrity": "sha512-Jh/AIwwgaxan+7ZUUmRLCjtchyDiqh4KjBJ5tW3plBZb5iL/BPcso8A5DlzeD9qlw0duCamnNdpFjxwaT0KyKg==", 307 | "license": "Apache-2.0", 308 | "dependencies": { 309 | "gaxios": "^6.0.0", 310 | "json-bigint": "^1.0.0" 311 | }, 312 | "engines": { 313 | "node": ">=14" 314 | } 315 | }, 316 | "node_modules/get-intrinsic": { 317 | "version": "1.2.6", 318 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz", 319 | "integrity": "sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA==", 320 | "license": "MIT", 321 | "dependencies": { 322 | "call-bind-apply-helpers": "^1.0.1", 323 | "dunder-proto": "^1.0.0", 324 | "es-define-property": "^1.0.1", 325 | "es-errors": "^1.3.0", 326 | "es-object-atoms": "^1.0.0", 327 | "function-bind": "^1.1.2", 328 | "gopd": "^1.2.0", 329 | "has-symbols": "^1.1.0", 330 | "hasown": "^2.0.2", 331 | "math-intrinsics": "^1.0.0" 332 | }, 333 | "engines": { 334 | "node": ">= 0.4" 335 | }, 336 | "funding": { 337 | "url": "https://github.com/sponsors/ljharb" 338 | } 339 | }, 340 | "node_modules/glob": { 341 | "version": "7.2.3", 342 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", 343 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", 344 | "deprecated": "Glob versions prior to v9 are no longer supported", 345 | "dev": true, 346 | "license": "ISC", 347 | "dependencies": { 348 | "fs.realpath": "^1.0.0", 349 | "inflight": "^1.0.4", 350 | "inherits": "2", 351 | "minimatch": "^3.1.1", 352 | "once": "^1.3.0", 353 | "path-is-absolute": "^1.0.0" 354 | }, 355 | "engines": { 356 | "node": "*" 357 | }, 358 | "funding": { 359 | "url": "https://github.com/sponsors/isaacs" 360 | } 361 | }, 362 | "node_modules/google-auth-library": { 363 | "version": "9.15.0", 364 | "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.0.tgz", 365 | "integrity": "sha512-7ccSEJFDFO7exFbO6NRyC+xH8/mZ1GZGG2xxx9iHxZWcjUjJpjWxIMw3cofAKcueZ6DATiukmmprD7yavQHOyQ==", 366 | "license": "Apache-2.0", 367 | "dependencies": { 368 | "base64-js": "^1.3.0", 369 | "ecdsa-sig-formatter": "^1.0.11", 370 | "gaxios": "^6.1.1", 371 | "gcp-metadata": "^6.1.0", 372 | "gtoken": "^7.0.0", 373 | "jws": "^4.0.0" 374 | }, 375 | "engines": { 376 | "node": ">=14" 377 | } 378 | }, 379 | "node_modules/googleapis": { 380 | "version": "144.0.0", 381 | "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-144.0.0.tgz", 382 | "integrity": "sha512-ELcWOXtJxjPX4vsKMh+7V+jZvgPwYMlEhQFiu2sa9Qmt5veX8nwXPksOWGGN6Zk4xCiLygUyaz7xGtcMO+Onxw==", 383 | "license": "Apache-2.0", 384 | "dependencies": { 385 | "google-auth-library": "^9.0.0", 386 | "googleapis-common": "^7.0.0" 387 | }, 388 | "engines": { 389 | "node": ">=14.0.0" 390 | } 391 | }, 392 | "node_modules/googleapis-common": { 393 | "version": "7.2.0", 394 | "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", 395 | "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", 396 | "license": "Apache-2.0", 397 | "dependencies": { 398 | "extend": "^3.0.2", 399 | "gaxios": "^6.0.3", 400 | "google-auth-library": "^9.7.0", 401 | "qs": "^6.7.0", 402 | "url-template": "^2.0.8", 403 | "uuid": "^9.0.0" 404 | }, 405 | "engines": { 406 | "node": ">=14.0.0" 407 | } 408 | }, 409 | "node_modules/gopd": { 410 | "version": "1.2.0", 411 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 412 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 413 | "license": "MIT", 414 | "engines": { 415 | "node": ">= 0.4" 416 | }, 417 | "funding": { 418 | "url": "https://github.com/sponsors/ljharb" 419 | } 420 | }, 421 | "node_modules/gtoken": { 422 | "version": "7.1.0", 423 | "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", 424 | "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", 425 | "license": "MIT", 426 | "dependencies": { 427 | "gaxios": "^6.0.0", 428 | "jws": "^4.0.0" 429 | }, 430 | "engines": { 431 | "node": ">=14.0.0" 432 | } 433 | }, 434 | "node_modules/has-symbols": { 435 | "version": "1.1.0", 436 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 437 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 438 | "license": "MIT", 439 | "engines": { 440 | "node": ">= 0.4" 441 | }, 442 | "funding": { 443 | "url": "https://github.com/sponsors/ljharb" 444 | } 445 | }, 446 | "node_modules/hasown": { 447 | "version": "2.0.2", 448 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 449 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 450 | "license": "MIT", 451 | "dependencies": { 452 | "function-bind": "^1.1.2" 453 | }, 454 | "engines": { 455 | "node": ">= 0.4" 456 | } 457 | }, 458 | "node_modules/http-errors": { 459 | "version": "2.0.0", 460 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 461 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 462 | "license": "MIT", 463 | "dependencies": { 464 | "depd": "2.0.0", 465 | "inherits": "2.0.4", 466 | "setprototypeof": "1.2.0", 467 | "statuses": "2.0.1", 468 | "toidentifier": "1.0.1" 469 | }, 470 | "engines": { 471 | "node": ">= 0.8" 472 | } 473 | }, 474 | "node_modules/https-proxy-agent": { 475 | "version": "7.0.6", 476 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", 477 | "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", 478 | "license": "MIT", 479 | "dependencies": { 480 | "agent-base": "^7.1.2", 481 | "debug": "4" 482 | }, 483 | "engines": { 484 | "node": ">= 14" 485 | } 486 | }, 487 | "node_modules/iconv-lite": { 488 | "version": "0.6.3", 489 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 490 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 491 | "license": "MIT", 492 | "dependencies": { 493 | "safer-buffer": ">= 2.1.2 < 3.0.0" 494 | }, 495 | "engines": { 496 | "node": ">=0.10.0" 497 | } 498 | }, 499 | "node_modules/inflight": { 500 | "version": "1.0.6", 501 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 502 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", 503 | "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", 504 | "dev": true, 505 | "license": "ISC", 506 | "dependencies": { 507 | "once": "^1.3.0", 508 | "wrappy": "1" 509 | } 510 | }, 511 | "node_modules/inherits": { 512 | "version": "2.0.4", 513 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 514 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 515 | "license": "ISC" 516 | }, 517 | "node_modules/interpret": { 518 | "version": "1.4.0", 519 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", 520 | "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", 521 | "dev": true, 522 | "license": "MIT", 523 | "engines": { 524 | "node": ">= 0.10" 525 | } 526 | }, 527 | "node_modules/is-core-module": { 528 | "version": "2.16.0", 529 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", 530 | "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", 531 | "dev": true, 532 | "license": "MIT", 533 | "dependencies": { 534 | "hasown": "^2.0.2" 535 | }, 536 | "engines": { 537 | "node": ">= 0.4" 538 | }, 539 | "funding": { 540 | "url": "https://github.com/sponsors/ljharb" 541 | } 542 | }, 543 | "node_modules/is-docker": { 544 | "version": "2.2.1", 545 | "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", 546 | "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", 547 | "license": "MIT", 548 | "bin": { 549 | "is-docker": "cli.js" 550 | }, 551 | "engines": { 552 | "node": ">=8" 553 | }, 554 | "funding": { 555 | "url": "https://github.com/sponsors/sindresorhus" 556 | } 557 | }, 558 | "node_modules/is-stream": { 559 | "version": "2.0.1", 560 | "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", 561 | "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", 562 | "license": "MIT", 563 | "engines": { 564 | "node": ">=8" 565 | }, 566 | "funding": { 567 | "url": "https://github.com/sponsors/sindresorhus" 568 | } 569 | }, 570 | "node_modules/is-wsl": { 571 | "version": "2.2.0", 572 | "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", 573 | "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", 574 | "license": "MIT", 575 | "dependencies": { 576 | "is-docker": "^2.0.0" 577 | }, 578 | "engines": { 579 | "node": ">=8" 580 | } 581 | }, 582 | "node_modules/json-bigint": { 583 | "version": "1.0.0", 584 | "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", 585 | "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", 586 | "license": "MIT", 587 | "dependencies": { 588 | "bignumber.js": "^9.0.0" 589 | } 590 | }, 591 | "node_modules/jwa": { 592 | "version": "2.0.0", 593 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", 594 | "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", 595 | "license": "MIT", 596 | "dependencies": { 597 | "buffer-equal-constant-time": "1.0.1", 598 | "ecdsa-sig-formatter": "1.0.11", 599 | "safe-buffer": "^5.0.1" 600 | } 601 | }, 602 | "node_modules/jws": { 603 | "version": "4.0.0", 604 | "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", 605 | "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", 606 | "license": "MIT", 607 | "dependencies": { 608 | "jwa": "^2.0.0", 609 | "safe-buffer": "^5.0.1" 610 | } 611 | }, 612 | "node_modules/math-intrinsics": { 613 | "version": "1.0.0", 614 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.0.0.tgz", 615 | "integrity": "sha512-4MqMiKP90ybymYvsut0CH2g4XWbfLtmlCkXmtmdcDCxNB+mQcu1w/1+L/VD7vi/PSv7X2JYV7SCcR+jiPXnQtA==", 616 | "license": "MIT", 617 | "engines": { 618 | "node": ">= 0.4" 619 | } 620 | }, 621 | "node_modules/minimatch": { 622 | "version": "3.1.2", 623 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", 624 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", 625 | "dev": true, 626 | "license": "ISC", 627 | "dependencies": { 628 | "brace-expansion": "^1.1.7" 629 | }, 630 | "engines": { 631 | "node": "*" 632 | } 633 | }, 634 | "node_modules/minimist": { 635 | "version": "1.2.8", 636 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", 637 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", 638 | "dev": true, 639 | "license": "MIT", 640 | "funding": { 641 | "url": "https://github.com/sponsors/ljharb" 642 | } 643 | }, 644 | "node_modules/ms": { 645 | "version": "2.1.3", 646 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 647 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 648 | "license": "MIT" 649 | }, 650 | "node_modules/node-fetch": { 651 | "version": "2.7.0", 652 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", 653 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", 654 | "license": "MIT", 655 | "dependencies": { 656 | "whatwg-url": "^5.0.0" 657 | }, 658 | "engines": { 659 | "node": "4.x || >=6.0.0" 660 | }, 661 | "peerDependencies": { 662 | "encoding": "^0.1.0" 663 | }, 664 | "peerDependenciesMeta": { 665 | "encoding": { 666 | "optional": true 667 | } 668 | } 669 | }, 670 | "node_modules/object-inspect": { 671 | "version": "1.13.3", 672 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", 673 | "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", 674 | "license": "MIT", 675 | "engines": { 676 | "node": ">= 0.4" 677 | }, 678 | "funding": { 679 | "url": "https://github.com/sponsors/ljharb" 680 | } 681 | }, 682 | "node_modules/once": { 683 | "version": "1.4.0", 684 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 685 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 686 | "dev": true, 687 | "license": "ISC", 688 | "dependencies": { 689 | "wrappy": "1" 690 | } 691 | }, 692 | "node_modules/open": { 693 | "version": "7.4.2", 694 | "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", 695 | "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", 696 | "license": "MIT", 697 | "dependencies": { 698 | "is-docker": "^2.0.0", 699 | "is-wsl": "^2.1.1" 700 | }, 701 | "engines": { 702 | "node": ">=8" 703 | }, 704 | "funding": { 705 | "url": "https://github.com/sponsors/sindresorhus" 706 | } 707 | }, 708 | "node_modules/path-is-absolute": { 709 | "version": "1.0.1", 710 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 711 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", 712 | "dev": true, 713 | "license": "MIT", 714 | "engines": { 715 | "node": ">=0.10.0" 716 | } 717 | }, 718 | "node_modules/path-parse": { 719 | "version": "1.0.7", 720 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", 721 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", 722 | "dev": true, 723 | "license": "MIT" 724 | }, 725 | "node_modules/qs": { 726 | "version": "6.13.1", 727 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz", 728 | "integrity": "sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg==", 729 | "license": "BSD-3-Clause", 730 | "dependencies": { 731 | "side-channel": "^1.0.6" 732 | }, 733 | "engines": { 734 | "node": ">=0.6" 735 | }, 736 | "funding": { 737 | "url": "https://github.com/sponsors/ljharb" 738 | } 739 | }, 740 | "node_modules/raw-body": { 741 | "version": "3.0.0", 742 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", 743 | "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", 744 | "license": "MIT", 745 | "dependencies": { 746 | "bytes": "3.1.2", 747 | "http-errors": "2.0.0", 748 | "iconv-lite": "0.6.3", 749 | "unpipe": "1.0.0" 750 | }, 751 | "engines": { 752 | "node": ">= 0.8" 753 | } 754 | }, 755 | "node_modules/rechoir": { 756 | "version": "0.6.2", 757 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", 758 | "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", 759 | "dev": true, 760 | "dependencies": { 761 | "resolve": "^1.1.6" 762 | }, 763 | "engines": { 764 | "node": ">= 0.10" 765 | } 766 | }, 767 | "node_modules/resolve": { 768 | "version": "1.22.9", 769 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz", 770 | "integrity": "sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==", 771 | "dev": true, 772 | "license": "MIT", 773 | "dependencies": { 774 | "is-core-module": "^2.16.0", 775 | "path-parse": "^1.0.7", 776 | "supports-preserve-symlinks-flag": "^1.0.0" 777 | }, 778 | "bin": { 779 | "resolve": "bin/resolve" 780 | }, 781 | "funding": { 782 | "url": "https://github.com/sponsors/ljharb" 783 | } 784 | }, 785 | "node_modules/safe-buffer": { 786 | "version": "5.2.1", 787 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 788 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 789 | "funding": [ 790 | { 791 | "type": "github", 792 | "url": "https://github.com/sponsors/feross" 793 | }, 794 | { 795 | "type": "patreon", 796 | "url": "https://www.patreon.com/feross" 797 | }, 798 | { 799 | "type": "consulting", 800 | "url": "https://feross.org/support" 801 | } 802 | ], 803 | "license": "MIT" 804 | }, 805 | "node_modules/safer-buffer": { 806 | "version": "2.1.2", 807 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 808 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 809 | "license": "MIT" 810 | }, 811 | "node_modules/server-destroy": { 812 | "version": "1.0.1", 813 | "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", 814 | "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", 815 | "license": "ISC" 816 | }, 817 | "node_modules/setprototypeof": { 818 | "version": "1.2.0", 819 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 820 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 821 | "license": "ISC" 822 | }, 823 | "node_modules/shelljs": { 824 | "version": "0.8.5", 825 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", 826 | "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", 827 | "dev": true, 828 | "license": "BSD-3-Clause", 829 | "dependencies": { 830 | "glob": "^7.0.0", 831 | "interpret": "^1.0.0", 832 | "rechoir": "^0.6.2" 833 | }, 834 | "bin": { 835 | "shjs": "bin/shjs" 836 | }, 837 | "engines": { 838 | "node": ">=4" 839 | } 840 | }, 841 | "node_modules/shx": { 842 | "version": "0.3.4", 843 | "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz", 844 | "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==", 845 | "dev": true, 846 | "license": "MIT", 847 | "dependencies": { 848 | "minimist": "^1.2.3", 849 | "shelljs": "^0.8.5" 850 | }, 851 | "bin": { 852 | "shx": "lib/cli.js" 853 | }, 854 | "engines": { 855 | "node": ">=6" 856 | } 857 | }, 858 | "node_modules/side-channel": { 859 | "version": "1.1.0", 860 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", 861 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", 862 | "license": "MIT", 863 | "dependencies": { 864 | "es-errors": "^1.3.0", 865 | "object-inspect": "^1.13.3", 866 | "side-channel-list": "^1.0.0", 867 | "side-channel-map": "^1.0.1", 868 | "side-channel-weakmap": "^1.0.2" 869 | }, 870 | "engines": { 871 | "node": ">= 0.4" 872 | }, 873 | "funding": { 874 | "url": "https://github.com/sponsors/ljharb" 875 | } 876 | }, 877 | "node_modules/side-channel-list": { 878 | "version": "1.0.0", 879 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", 880 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", 881 | "license": "MIT", 882 | "dependencies": { 883 | "es-errors": "^1.3.0", 884 | "object-inspect": "^1.13.3" 885 | }, 886 | "engines": { 887 | "node": ">= 0.4" 888 | }, 889 | "funding": { 890 | "url": "https://github.com/sponsors/ljharb" 891 | } 892 | }, 893 | "node_modules/side-channel-map": { 894 | "version": "1.0.1", 895 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", 896 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", 897 | "license": "MIT", 898 | "dependencies": { 899 | "call-bound": "^1.0.2", 900 | "es-errors": "^1.3.0", 901 | "get-intrinsic": "^1.2.5", 902 | "object-inspect": "^1.13.3" 903 | }, 904 | "engines": { 905 | "node": ">= 0.4" 906 | }, 907 | "funding": { 908 | "url": "https://github.com/sponsors/ljharb" 909 | } 910 | }, 911 | "node_modules/side-channel-weakmap": { 912 | "version": "1.0.2", 913 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", 914 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", 915 | "license": "MIT", 916 | "dependencies": { 917 | "call-bound": "^1.0.2", 918 | "es-errors": "^1.3.0", 919 | "get-intrinsic": "^1.2.5", 920 | "object-inspect": "^1.13.3", 921 | "side-channel-map": "^1.0.1" 922 | }, 923 | "engines": { 924 | "node": ">= 0.4" 925 | }, 926 | "funding": { 927 | "url": "https://github.com/sponsors/ljharb" 928 | } 929 | }, 930 | "node_modules/statuses": { 931 | "version": "2.0.1", 932 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 933 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 934 | "license": "MIT", 935 | "engines": { 936 | "node": ">= 0.8" 937 | } 938 | }, 939 | "node_modules/supports-preserve-symlinks-flag": { 940 | "version": "1.0.0", 941 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", 942 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", 943 | "dev": true, 944 | "license": "MIT", 945 | "engines": { 946 | "node": ">= 0.4" 947 | }, 948 | "funding": { 949 | "url": "https://github.com/sponsors/ljharb" 950 | } 951 | }, 952 | "node_modules/toidentifier": { 953 | "version": "1.0.1", 954 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 955 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 956 | "license": "MIT", 957 | "engines": { 958 | "node": ">=0.6" 959 | } 960 | }, 961 | "node_modules/tr46": { 962 | "version": "0.0.3", 963 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", 964 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", 965 | "license": "MIT" 966 | }, 967 | "node_modules/typescript": { 968 | "version": "5.7.2", 969 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", 970 | "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", 971 | "dev": true, 972 | "license": "Apache-2.0", 973 | "bin": { 974 | "tsc": "bin/tsc", 975 | "tsserver": "bin/tsserver" 976 | }, 977 | "engines": { 978 | "node": ">=14.17" 979 | } 980 | }, 981 | "node_modules/undici-types": { 982 | "version": "6.20.0", 983 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", 984 | "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", 985 | "dev": true, 986 | "license": "MIT" 987 | }, 988 | "node_modules/unpipe": { 989 | "version": "1.0.0", 990 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 991 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 992 | "license": "MIT", 993 | "engines": { 994 | "node": ">= 0.8" 995 | } 996 | }, 997 | "node_modules/url-template": { 998 | "version": "2.0.8", 999 | "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", 1000 | "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", 1001 | "license": "BSD" 1002 | }, 1003 | "node_modules/uuid": { 1004 | "version": "9.0.1", 1005 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", 1006 | "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", 1007 | "funding": [ 1008 | "https://github.com/sponsors/broofa", 1009 | "https://github.com/sponsors/ctavan" 1010 | ], 1011 | "license": "MIT", 1012 | "bin": { 1013 | "uuid": "dist/bin/uuid" 1014 | } 1015 | }, 1016 | "node_modules/webidl-conversions": { 1017 | "version": "3.0.1", 1018 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", 1019 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", 1020 | "license": "BSD-2-Clause" 1021 | }, 1022 | "node_modules/whatwg-url": { 1023 | "version": "5.0.0", 1024 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", 1025 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", 1026 | "license": "MIT", 1027 | "dependencies": { 1028 | "tr46": "~0.0.3", 1029 | "webidl-conversions": "^3.0.0" 1030 | } 1031 | }, 1032 | "node_modules/wrappy": { 1033 | "version": "1.0.2", 1034 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1035 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 1036 | "dev": true, 1037 | "license": "ISC" 1038 | }, 1039 | "node_modules/zod": { 1040 | "version": "3.24.1", 1041 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", 1042 | "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", 1043 | "license": "MIT", 1044 | "funding": { 1045 | "url": "https://github.com/sponsors/colinhacks" 1046 | } 1047 | } 1048 | } 1049 | } 1050 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@modelcontextprotocol/server-gtasks", 3 | "version": "0.0.1", 4 | "description": "MCP server for interacting with Google Tasks", 5 | "license": "MIT", 6 | "author": "zcaceres (@zachcaceres zach.dev)", 7 | "homepage": "https://modelcontextprotocol.io", 8 | "bugs": "https://github.com/modelcontextprotocol/servers/issues", 9 | "type": "module", 10 | "bin": { 11 | "mcp-server-gtasks": "dist/index.js" 12 | }, 13 | "files": [ 14 | "dist" 15 | ], 16 | "scripts": { 17 | "build": "tsc && shx chmod +x dist/*.js", 18 | "prepare": "npm run build", 19 | "dev": "tsc --watch", 20 | "start": "node dist/index.js" 21 | }, 22 | "dependencies": { 23 | "@google-cloud/local-auth": "^3.0.1", 24 | "@modelcontextprotocol/sdk": "1.0.1", 25 | "googleapis": "^144.0.0" 26 | }, 27 | "devDependencies": { 28 | "@types/node": "^22.9.3", 29 | "shx": "^0.3.4", 30 | "typescript": "^5.6.2" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | required: 9 | - oauthCredentialsPath 10 | properties: 11 | oauthCredentialsPath: 12 | type: string 13 | default: ./gcp-oauth.keys.json 14 | description: The path to the OAuth credentials JSON file. 15 | commandFunction: 16 | # A function that produces the CLI command to start the MCP on stdio. 17 | |- 18 | config => ({ command: 'node', args: ['dist/index.js'], env: { GCP_OAUTH_KEYS: config.oauthCredentialsPath } }) 19 | -------------------------------------------------------------------------------- /src/Tasks.ts: -------------------------------------------------------------------------------- 1 | import { 2 | CallToolRequest, 3 | CallToolResult, 4 | ListResourcesRequest, 5 | ReadResourceRequest, 6 | } from "@modelcontextprotocol/sdk/types.js"; 7 | import { GaxiosResponse } from "gaxios"; 8 | import { tasks_v1 } from "googleapis"; 9 | 10 | const MAX_TASK_RESULTS = 100; 11 | 12 | export class TaskResources { 13 | static async read(request: ReadResourceRequest, tasks: tasks_v1.Tasks) { 14 | const taskId = request.params.uri.replace("gtasks:///", ""); 15 | 16 | const taskListsResponse: GaxiosResponse = 17 | await tasks.tasklists.list({ 18 | maxResults: MAX_TASK_RESULTS, 19 | }); 20 | 21 | const taskLists = taskListsResponse.data.items || []; 22 | let task: tasks_v1.Schema$Task | null = null; 23 | 24 | for (const taskList of taskLists) { 25 | if (taskList.id) { 26 | try { 27 | const taskResponse: GaxiosResponse = 28 | await tasks.tasks.get({ 29 | tasklist: taskList.id, 30 | task: taskId, 31 | }); 32 | task = taskResponse.data; 33 | break; 34 | } catch (error) { 35 | // Task not found in this list, continue to the next one 36 | } 37 | } 38 | } 39 | 40 | if (!task) { 41 | throw new Error("Task not found"); 42 | } 43 | 44 | return task; 45 | } 46 | 47 | static async list( 48 | request: ListResourcesRequest, 49 | tasks: tasks_v1.Tasks, 50 | ): Promise<[tasks_v1.Schema$Task[], string | null]> { 51 | const pageSize = 10; 52 | const params: any = { 53 | maxResults: pageSize, 54 | }; 55 | 56 | if (request.params?.cursor) { 57 | params.pageToken = request.params.cursor; 58 | } 59 | 60 | const taskListsResponse = await tasks.tasklists.list({ 61 | maxResults: MAX_TASK_RESULTS, 62 | }); 63 | 64 | const taskLists = taskListsResponse.data.items || []; 65 | 66 | let allTasks: tasks_v1.Schema$Task[] = []; 67 | let nextPageToken = null; 68 | 69 | for (const taskList of taskLists) { 70 | const tasksResponse = await tasks.tasks.list({ 71 | tasklist: taskList.id, 72 | ...params, 73 | }); 74 | 75 | const taskItems = tasksResponse.data.items || []; 76 | allTasks = allTasks.concat(taskItems); 77 | 78 | if (tasksResponse.data.nextPageToken) { 79 | nextPageToken = tasksResponse.data.nextPageToken; 80 | } 81 | } 82 | 83 | return [allTasks, nextPageToken]; 84 | } 85 | } 86 | 87 | export class TaskActions { 88 | private static formatTask(task: tasks_v1.Schema$Task) { 89 | return `${task.title}\n (Due: ${task.due || "Not set"}) - Notes: ${task.notes} - ID: ${task.id} - Status: ${task.status} - URI: ${task.selfLink} - Hidden: ${task.hidden} - Parent: ${task.parent} - Deleted?: ${task.deleted} - Completed Date: ${task.completed} - Position: ${task.position} - Updated Date: ${task.updated} - ETag: ${task.etag} - Links: ${task.links} - Kind: ${task.kind}}`; 90 | } 91 | 92 | private static formatTaskList(taskList: tasks_v1.Schema$Task[]) { 93 | return taskList.map((task) => this.formatTask(task)).join("\n"); 94 | } 95 | 96 | private static async _list(request: CallToolRequest, tasks: tasks_v1.Tasks) { 97 | const taskListsResponse = await tasks.tasklists.list({ 98 | maxResults: MAX_TASK_RESULTS, 99 | }); 100 | 101 | const taskLists = taskListsResponse.data.items || []; 102 | let allTasks: tasks_v1.Schema$Task[] = []; 103 | 104 | for (const taskList of taskLists) { 105 | if (taskList.id) { 106 | try { 107 | const tasksResponse = await tasks.tasks.list({ 108 | tasklist: taskList.id, 109 | maxResults: MAX_TASK_RESULTS, 110 | }); 111 | 112 | const items = tasksResponse.data.items || []; 113 | allTasks = allTasks.concat(items); 114 | } catch (error) { 115 | console.error(`Error fetching tasks for list ${taskList.id}:`, error); 116 | } 117 | } 118 | } 119 | return allTasks; 120 | } 121 | 122 | static async create(request: CallToolRequest, tasks: tasks_v1.Tasks) { 123 | const taskListId = 124 | (request.params.arguments?.taskListId as string) || "@default"; 125 | const taskTitle = request.params.arguments?.title as string; 126 | const taskNotes = request.params.arguments?.notes as string; 127 | const taskStatus = request.params.arguments?.status as string; 128 | const taskDue = request.params.arguments?.due as string; 129 | 130 | if (!taskTitle) { 131 | throw new Error("Task title is required"); 132 | } 133 | 134 | const task = { 135 | title: taskTitle, 136 | notes: taskNotes, 137 | due: taskDue, 138 | }; 139 | 140 | const taskResponse = await tasks.tasks.insert({ 141 | tasklist: taskListId, 142 | requestBody: task, 143 | }); 144 | 145 | return { 146 | content: [ 147 | { 148 | type: "text", 149 | text: `Task created: ${taskResponse.data.title}`, 150 | }, 151 | ], 152 | isError: false, 153 | }; 154 | } 155 | 156 | static async update(request: CallToolRequest, tasks: tasks_v1.Tasks) { 157 | const taskListId = 158 | (request.params.arguments?.taskListId as string) || "@default"; 159 | const taskUri = request.params.arguments?.uri as string; 160 | const taskId = request.params.arguments?.id as string; 161 | const taskTitle = request.params.arguments?.title as string; 162 | const taskNotes = request.params.arguments?.notes as string; 163 | const taskStatus = request.params.arguments?.status as string; 164 | const taskDue = request.params.arguments?.due as string; 165 | 166 | if (!taskUri) { 167 | throw new Error("Task URI is required"); 168 | } 169 | 170 | if (!taskId) { 171 | throw new Error("Task ID is required"); 172 | } 173 | 174 | const task = { 175 | id: taskId, 176 | title: taskTitle, 177 | notes: taskNotes, 178 | status: taskStatus, 179 | due: taskDue, 180 | }; 181 | 182 | const taskResponse = await tasks.tasks.update({ 183 | tasklist: taskListId, 184 | task: taskUri, 185 | requestBody: task, 186 | }); 187 | 188 | return { 189 | content: [ 190 | { 191 | type: "text", 192 | text: `Task updated: ${taskResponse.data.title}`, 193 | }, 194 | ], 195 | isError: false, 196 | }; 197 | } 198 | 199 | static async list(request: CallToolRequest, tasks: tasks_v1.Tasks) { 200 | const allTasks = await this._list(request, tasks); 201 | const taskList = this.formatTaskList(allTasks); 202 | 203 | return { 204 | content: [ 205 | { 206 | type: "text", 207 | text: `Found ${allTasks.length} tasks:\n${taskList}`, 208 | }, 209 | ], 210 | isError: false, 211 | }; 212 | } 213 | 214 | static async delete(request: CallToolRequest, tasks: tasks_v1.Tasks) { 215 | const taskListId = 216 | (request.params.arguments?.taskListId as string) || "@default"; 217 | const taskId = request.params.arguments?.id as string; 218 | 219 | if (!taskId) { 220 | throw new Error("Task URI is required"); 221 | } 222 | 223 | await tasks.tasks.delete({ 224 | tasklist: taskListId, 225 | task: taskId, 226 | }); 227 | 228 | return { 229 | content: [ 230 | { 231 | type: "text", 232 | text: `Task ${taskId} deleted`, 233 | }, 234 | ], 235 | isError: false, 236 | }; 237 | } 238 | 239 | static async search(request: CallToolRequest, tasks: tasks_v1.Tasks) { 240 | const userQuery = request.params.arguments?.query as string; 241 | 242 | const allTasks = await this._list(request, tasks); 243 | const filteredItems = allTasks.filter( 244 | (task) => 245 | task.title?.toLowerCase().includes(userQuery.toLowerCase()) || 246 | task.notes?.toLowerCase().includes(userQuery.toLowerCase()), 247 | ); 248 | 249 | const taskList = this.formatTaskList(filteredItems); 250 | 251 | return { 252 | content: [ 253 | { 254 | type: "text", 255 | text: `Found ${allTasks.length} tasks:\n${taskList}`, 256 | }, 257 | ], 258 | isError: false, 259 | }; 260 | } 261 | 262 | static async clear(request: CallToolRequest, tasks: tasks_v1.Tasks) { 263 | const taskListId = 264 | (request.params.arguments?.taskListId as string) || "@default"; 265 | 266 | await tasks.tasks.clear({ 267 | tasklist: taskListId, 268 | }); 269 | 270 | return { 271 | content: [ 272 | { 273 | type: "text", 274 | text: `Tasks from tasklist ${taskListId} cleared`, 275 | }, 276 | ], 277 | isError: false, 278 | }; 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | import { authenticate } from "@google-cloud/local-auth"; 4 | import { Server } from "@modelcontextprotocol/sdk/server/index.js"; 5 | 6 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; 7 | import { 8 | CallToolRequestSchema, 9 | ListResourcesRequestSchema, 10 | ListToolsRequestSchema, 11 | ReadResourceRequestSchema, 12 | } from "@modelcontextprotocol/sdk/types.js"; 13 | import fs from "fs"; 14 | import { google, tasks_v1 } from "googleapis"; 15 | import path from "path"; 16 | import { TaskActions, TaskResources } from "./Tasks.js"; 17 | 18 | const tasks = google.tasks("v1"); 19 | 20 | const server = new Server( 21 | { 22 | name: "example-servers/gtasks", 23 | version: "0.1.0", 24 | }, 25 | { 26 | capabilities: { 27 | resources: {}, 28 | tools: {}, 29 | }, 30 | }, 31 | ); 32 | 33 | server.setRequestHandler(ListResourcesRequestSchema, async (request) => { 34 | const [allTasks, nextPageToken] = await TaskResources.list(request, tasks); 35 | return { 36 | resources: allTasks.map((task) => ({ 37 | uri: `gtasks:///${task.id}`, 38 | mimeType: "text/plain", 39 | name: task.title, 40 | })), 41 | nextCursor: nextPageToken, 42 | }; 43 | }); 44 | 45 | server.setRequestHandler(ReadResourceRequestSchema, async (request) => { 46 | const task = await TaskResources.read(request, tasks); 47 | 48 | const taskDetails = [ 49 | `Title: ${task.title || "No title"}`, 50 | `Status: ${task.status || "Unknown"}`, 51 | `Due: ${task.due || "Not set"}`, 52 | `Notes: ${task.notes || "No notes"}`, 53 | `Hidden: ${task.hidden || "Unknown"}`, 54 | `Parent: ${task.parent || "Unknown"}`, 55 | `Deleted?: ${task.deleted || "Unknown"}`, 56 | `Completed Date: ${task.completed || "Unknown"}`, 57 | `Position: ${task.position || "Unknown"}`, 58 | `ETag: ${task.etag || "Unknown"}`, 59 | `Links: ${task.links || "Unknown"}`, 60 | `Kind: ${task.kind || "Unknown"}`, 61 | `Status: ${task.status || "Unknown"}`, 62 | `Created: ${task.updated || "Unknown"}`, 63 | `Updated: ${task.updated || "Unknown"}`, 64 | ].join("\n"); 65 | 66 | return { 67 | contents: [ 68 | { 69 | uri: request.params.uri, 70 | mimeType: "text/plain", 71 | text: taskDetails, 72 | }, 73 | ], 74 | }; 75 | }); 76 | 77 | server.setRequestHandler(ListToolsRequestSchema, async () => { 78 | return { 79 | tools: [ 80 | { 81 | name: "search", 82 | description: "Search for a task in Google Tasks", 83 | inputSchema: { 84 | type: "object", 85 | properties: { 86 | query: { 87 | type: "string", 88 | description: "Search query", 89 | }, 90 | }, 91 | required: ["query"], 92 | }, 93 | }, 94 | { 95 | name: "list", 96 | description: "List all tasks in Google Tasks", 97 | inputSchema: { 98 | type: "object", 99 | properties: { 100 | cursor: { 101 | type: "string", 102 | description: "Cursor for pagination", 103 | }, 104 | }, 105 | }, 106 | }, 107 | { 108 | name: "create", 109 | description: "Create a new task in Google Tasks", 110 | inputSchema: { 111 | type: "object", 112 | properties: { 113 | taskListId: { 114 | type: "string", 115 | description: "Task list ID", 116 | }, 117 | title: { 118 | type: "string", 119 | description: "Task title", 120 | }, 121 | notes: { 122 | type: "string", 123 | description: "Task notes", 124 | }, 125 | due: { 126 | type: "string", 127 | description: "Due date", 128 | }, 129 | }, 130 | required: ["title"], 131 | }, 132 | }, 133 | { 134 | name: "clear", 135 | description: "Clear completed tasks from a Google Tasks task list", 136 | inputSchema: { 137 | type: "object", 138 | properties: { 139 | taskListId: { 140 | type: "string", 141 | description: "Task list ID", 142 | }, 143 | }, 144 | required: ["taskListId"], 145 | }, 146 | }, 147 | { 148 | name: "delete", 149 | description: "Delete a task in Google Tasks", 150 | inputSchema: { 151 | type: "object", 152 | properties: { 153 | taskListId: { 154 | type: "string", 155 | description: "Task list ID", 156 | }, 157 | id: { 158 | type: "string", 159 | description: "Task id", 160 | }, 161 | }, 162 | required: ["id", "taskListId"], 163 | }, 164 | }, 165 | { 166 | name: "update", 167 | description: "Update a task in Google Tasks", 168 | inputSchema: { 169 | type: "object", 170 | properties: { 171 | taskListId: { 172 | type: "string", 173 | description: "Task list ID", 174 | }, 175 | id: { 176 | type: "string", 177 | description: "Task ID", 178 | }, 179 | uri: { 180 | type: "string", 181 | description: "Task URI", 182 | }, 183 | title: { 184 | type: "string", 185 | description: "Task title", 186 | }, 187 | notes: { 188 | type: "string", 189 | description: "Task notes", 190 | }, 191 | status: { 192 | type: "string", 193 | enum: ["needsAction", "completed"], 194 | description: "Task status (needsAction or completed)", 195 | }, 196 | due: { 197 | type: "string", 198 | description: "Due date", 199 | }, 200 | }, 201 | required: ["id", "uri"], 202 | }, 203 | }, 204 | ], 205 | }; 206 | }); 207 | 208 | server.setRequestHandler(CallToolRequestSchema, async (request) => { 209 | if (request.params.name === "search") { 210 | const taskResult = await TaskActions.search(request, tasks); 211 | return taskResult; 212 | } 213 | if (request.params.name === "list") { 214 | const taskResult = await TaskActions.list(request, tasks); 215 | return taskResult; 216 | } 217 | if (request.params.name === "create") { 218 | const taskResult = await TaskActions.create(request, tasks); 219 | return taskResult; 220 | } 221 | if (request.params.name === "update") { 222 | const taskResult = await TaskActions.update(request, tasks); 223 | return taskResult; 224 | } 225 | if (request.params.name === "delete") { 226 | const taskResult = await TaskActions.delete(request, tasks); 227 | return taskResult; 228 | } 229 | if (request.params.name === "clear") { 230 | const taskResult = await TaskActions.clear(request, tasks); 231 | return taskResult; 232 | } 233 | throw new Error("Tool not found"); 234 | }); 235 | 236 | const credentialsPath = path.join( 237 | path.dirname(new URL(import.meta.url).pathname), 238 | "../.gtasks-server-credentials.json", 239 | ); 240 | 241 | async function authenticateAndSaveCredentials() { 242 | console.log("Launching auth flow…"); 243 | const p = path.join( 244 | path.dirname(new URL(import.meta.url).pathname), 245 | "../gcp-oauth.keys.json", 246 | ); 247 | 248 | console.log(p); 249 | const auth = await authenticate({ 250 | keyfilePath: p, 251 | scopes: ["https://www.googleapis.com/auth/tasks"], 252 | }); 253 | fs.writeFileSync(credentialsPath, JSON.stringify(auth.credentials)); 254 | console.log("Credentials saved. You can now run the server."); 255 | } 256 | 257 | async function loadCredentialsAndRunServer() { 258 | if (!fs.existsSync(credentialsPath)) { 259 | console.error( 260 | "Credentials not found. Please run with 'auth' argument first.", 261 | ); 262 | process.exit(1); 263 | } 264 | 265 | const credentials = JSON.parse(fs.readFileSync(credentialsPath, "utf-8")); 266 | const auth = new google.auth.OAuth2(); 267 | auth.setCredentials(credentials); 268 | google.options({ auth }); 269 | 270 | const transport = new StdioServerTransport(); 271 | await server.connect(transport); 272 | } 273 | 274 | if (process.argv[2] === "auth") { 275 | authenticateAndSaveCredentials().catch(console.error); 276 | } else { 277 | loadCredentialsAndRunServer().catch(console.error); 278 | } 279 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "outDir": "./dist", 7 | "rootDir": "./src", 8 | "strict": true, 9 | "esModuleInterop": true, 10 | "skipLibCheck": true, 11 | "forceConsistentCasingInFileNames": true 12 | }, 13 | "include": ["src/**/*"], 14 | "exclude": ["node_modules"] 15 | } 16 | --------------------------------------------------------------------------------