From 203d2a18b339e9a37d29c74fec69091128fff3c6 Mon Sep 17 00:00:00 2001 From: Steven Le Date: Sun, 15 Feb 2026 15:08:45 -0800 Subject: [PATCH 1/2] feat: add MCP server support to `root dev` command Adds a `--mcp` CLI flag that starts an MCP (Model Context Protocol) server on stdio alongside the dev server. Plugins can register MCP tools via the new `configureMcpServer` hook on the Plugin interface. --- packages/root/package.json | 4 ++- packages/root/src/cli/cli.ts | 6 +++- packages/root/src/cli/dev.ts | 39 +++++++++++++++++++--- packages/root/src/cli/mcp.ts | 54 +++++++++++++++++++++++++++++++ packages/root/src/core/plugin.ts | 16 ++++++++++ pnpm-lock.yaml | 55 ++++++++++++++++---------------- 6 files changed, 140 insertions(+), 34 deletions(-) create mode 100644 packages/root/src/cli/mcp.ts diff --git a/packages/root/package.json b/packages/root/package.json index 8e6423549..713b8d4fb 100644 --- a/packages/root/package.json +++ b/packages/root/package.json @@ -64,6 +64,7 @@ "@types/micromatch": "4.0.6", "bundle-require": "4.0.2", "busboy": "1.6.0", + "@modelcontextprotocol/sdk": "1.12.0", "commander": "11.0.0", "compression": "1.7.4", "cookie-parser": "1.4.6", @@ -81,7 +82,8 @@ "source-map-support": "0.5.21", "tiny-glob": "0.2.9", "vite": "7.1.4", - "workspace-tools": "0.37.0" + "workspace-tools": "0.37.0", + "zod": "3.25.0" }, "peerDependencies": { "firebase-admin": ">=11", diff --git a/packages/root/src/cli/cli.ts b/packages/root/src/cli/cli.ts index c8c62dac9..ed601771d 100644 --- a/packages/root/src/cli/cli.ts +++ b/packages/root/src/cli/cli.ts @@ -77,7 +77,11 @@ class CliRunner { '--host ', 'network address the server should listen on, e.g. 127.0.0.1' ) - .action(dev); + .option('--mcp', 'start an MCP server for AI tool integration') + .action((rootPackageDir, options) => { + options.version = this.version; + dev(rootPackageDir, options); + }); program .command('gae-deploy ') .description( diff --git a/packages/root/src/cli/dev.ts b/packages/root/src/cli/dev.ts index 5996a4d1a..d7d10058a 100644 --- a/packages/root/src/cli/dev.ts +++ b/packages/root/src/cli/dev.ts @@ -34,6 +34,8 @@ type RenderModule = typeof import('../render/render.js'); export interface DevOptions { host?: string; + mcp?: boolean; + version?: string; } export async function dev(rootProjectDir?: string, options?: DevOptions) { @@ -42,9 +44,11 @@ export async function dev(rootProjectDir?: string, options?: DevOptions) { const defaultPort = parseInt(process.env.PORT || '4007'); const host = options?.host || 'localhost'; const port = await findOpenPort(defaultPort, defaultPort + 10); + const mcpMode = options?.mcp ?? false; let currentServer: http.Server | null = null; let currentViteServer: ViteDevServer | null = null; + let currentMcpHandle: {close: () => Promise} | null = null; async function start() { const server = await createDevServer({rootDir, port}); @@ -62,8 +66,17 @@ export async function dev(rootProjectDir?: string, options?: DevOptions) { } console.log(`${dim('┃')} mode: development`); console.log(); + currentServer = server.listen(port, host); + if (mcpMode) { + const {startMcpServer} = await import('./mcp.js'); + currentMcpHandle = await startMcpServer({ + rootConfig, + version: options?.version || '1.0.0', + }); + } + // Watch for config changes. const rootConfigDependencies: string[] = server.get( 'rootConfigDependencies' @@ -75,15 +88,23 @@ export async function dev(rootProjectDir?: string, options?: DevOptions) { viteServer.watcher.add(dependencies); viteServer.watcher.on('change', async (file) => { if (dependencies.includes(file)) { - console.log( - `\n${dim('┃')} root.config.ts changed. restarting server...` - ); + if (mcpMode) { + console.error('root.config.ts changed. restarting server...'); + } else { + console.log( + `\n${dim('┃')} root.config.ts changed. restarting server...` + ); + } await restart(); } }); } - async function restart() { + async function shutdown() { + if (currentMcpHandle) { + await currentMcpHandle.close(); + currentMcpHandle = null; + } if (currentServer) { currentServer.close(); currentServer = null; @@ -92,9 +113,19 @@ export async function dev(rootProjectDir?: string, options?: DevOptions) { await currentViteServer.close(); currentViteServer = null; } + } + + async function restart() { + await shutdown(); await start(); } + process.on('SIGINT', async () => { + await shutdown(); + // eslint-disable-next-line no-process-exit + process.exit(0); + }); + await start(); } diff --git a/packages/root/src/cli/mcp.ts b/packages/root/src/cli/mcp.ts new file mode 100644 index 000000000..4ccbba686 --- /dev/null +++ b/packages/root/src/cli/mcp.ts @@ -0,0 +1,54 @@ +import {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; +import {StdioServerTransport} from '@modelcontextprotocol/sdk/server/stdio.js'; +import {RootConfig} from '../core/config.js'; + +export interface McpServerOptions { + rootConfig: RootConfig; + version: string; +} + +/** + * Creates and returns a configured McpServer instance. Does not connect + * the transport — the caller is responsible for that. + */ +export async function createMcpServer( + options: McpServerOptions +): Promise { + const {rootConfig} = options; + const plugins = rootConfig.plugins || []; + + const server = new McpServer({ + name: 'root-ai', + version: options.version, + }); + + for (const plugin of plugins) { + if (plugin.configureMcpServer) { + await plugin.configureMcpServer(server, {rootConfig}); + } + } + + return server; +} + +export interface McpServerHandle { + close: () => Promise; +} + +/** + * Starts the MCP server with stdio transport. This function should be + * called when `root dev --mcp` is invoked. + */ +export async function startMcpServer( + options: McpServerOptions +): Promise { + const server = await createMcpServer(options); + const transport = new StdioServerTransport(); + await server.connect(transport); + console.error('Root.js MCP server running on stdio'); + return { + close: async () => { + await server.close(); + }, + }; +} diff --git a/packages/root/src/core/plugin.ts b/packages/root/src/core/plugin.ts index 4cd0d537a..d729e40b4 100644 --- a/packages/root/src/core/plugin.ts +++ b/packages/root/src/core/plugin.ts @@ -1,3 +1,4 @@ +import type {McpServer} from '@modelcontextprotocol/sdk/server/mcp.js'; import {ViteDevServer, PluginOption as VitePlugin} from 'vite'; import {RootConfig} from './config.js'; import {NextFunction, Request, Response, Server} from './types.js'; @@ -16,6 +17,15 @@ export interface ConfigureServerOptions { rootConfig: RootConfig; } +export interface ConfigureMcpServerOptions { + rootConfig: RootConfig; +} + +export type ConfigureMcpServerHook = ( + server: McpServer, + options: ConfigureMcpServerOptions +) => MaybePromise; + export interface PluginHooks { /** * Hook that runs before the build starts. @@ -68,6 +78,12 @@ export interface Plugin { res: Response, next: NextFunction ) => void | Promise; + /** + * Configures the MCP (Model Context Protocol) server. Plugins can use + * this hook to register MCP tools that will be available when the dev + * server is started with `--mcp`. + */ + configureMcpServer?: ConfigureMcpServerHook; } /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f7e416695..bfd09aa68 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -222,6 +222,9 @@ importers: packages/root: dependencies: + '@modelcontextprotocol/sdk': + specifier: 1.12.0 + version: 1.12.0 '@types/js-yaml': specifier: 4.0.9 version: 4.0.9 @@ -288,6 +291,9 @@ importers: workspace-tools: specifier: 0.37.0 version: 0.37.0 + zod: + specifier: 3.25.0 + version: 3.25.0 devDependencies: '@types/busboy': specifier: 1.5.0 @@ -2573,8 +2579,8 @@ packages: express: 4.21.2 get-port: 5.1.1 json-schema: 0.4.0 - zod: 3.25.76 - zod-to-json-schema: 3.24.6(zod@3.25.76) + zod: 3.25.0 + zod-to-json-schema: 3.24.6(zod@3.25.0) optionalDependencies: '@genkit-ai/firebase': 1.18.0(@google-cloud/firestore@7.11.3)(firebase-admin@13.5.0)(firebase@12.2.1)(genkit@1.26.0) transitivePeerDependencies: @@ -3647,22 +3653,22 @@ packages: '@types/gapi.client.discovery-v1': 0.0.4 dev: true - /@maxim_mazurok/gapi.client.drive-v3@0.1.20260118: - resolution: {integrity: sha512-AapBEaj1ztX+AnUBvBnypCC8mWf6q72+4n0oXDopdXmggwYqDISocyoXrQ/iSCk237gFsBSb6WUYTxg7LEiojw==} + /@maxim_mazurok/gapi.client.drive-v3@0.1.20260210: + resolution: {integrity: sha512-+AbG6C4goi1oWUkutBVHkIv2gbcYBzdoDZ7MzvFCNME9vLsh5U3OKurXe4Tek8D9Cj7h+/umsKh/YLuVoePeqQ==} dependencies: '@types/gapi.client': 1.0.8 '@types/gapi.client.discovery-v1': 0.0.4 dev: true - /@maxim_mazurok/gapi.client.sheets-v4@0.1.20260114: - resolution: {integrity: sha512-rhqbdPctKpTA9EsmLVe+FlbL2wiMd+SGBEgTedPi8IuLbmO7Dcg7+c50eQanHuFVx/rDHiol1k/LiTvDpQ/l2Q==} + /@maxim_mazurok/gapi.client.sheets-v4@0.1.20260210: + resolution: {integrity: sha512-ZT7QUSLJoEHugtsRocviV21maMHsNA4jMx0g3lx+buVHKqptQsz43wCePEJ36gkoCsYgPNZ5Yw/6TwTGwaRStQ==} dependencies: '@types/gapi.client': 1.0.8 '@types/gapi.client.discovery-v1': 0.0.4 dev: true - /@modelcontextprotocol/sdk@1.17.5: - resolution: {integrity: sha512-QakrKIGniGuRVfWBdMsDea/dx1PNE739QJ7gCM41s9q+qaCYTHCdsIBXQVVXry3mfWAiaM9kT22Hyz53Uw8mfg==} + /@modelcontextprotocol/sdk@1.12.0: + resolution: {integrity: sha512-m//7RlINx1F3sz3KqwY1WWzVgTcYX52HYk4bJ1hkBXV3zccAEth+jRvG8DBRrdaQuRsPAJOx2MH3zaHNCKL7Zg==} engines: {node: '>=18'} dependencies: ajv: 6.12.6 @@ -3670,16 +3676,14 @@ packages: cors: 2.8.5 cross-spawn: 7.0.6 eventsource: 3.0.7 - eventsource-parser: 3.0.6 express: 5.1.0 express-rate-limit: 7.5.1(express@5.1.0) pkce-challenge: 5.0.0 raw-body: 3.0.1 - zod: 3.25.76 - zod-to-json-schema: 3.24.6(zod@3.25.76) + zod: 3.25.0 + zod-to-json-schema: 3.24.6(zod@3.25.0) transitivePeerDependencies: - supports-color - dev: true /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -4958,7 +4962,7 @@ packages: resolution: {integrity: sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 fast-glob: 3.3.1 is-glob: 4.0.3 open: 9.1.0 @@ -5625,13 +5629,13 @@ packages: /@types/gapi.client.drive-v3@0.0.4: resolution: {integrity: sha512-jE37dJ0EzAdY0aJPFOp20xmec/aO0P4HtUIA9k07RMPyedFDOcuMlSac1r0PklwQdgXF7BHaMoObNHNAnwSQUQ==} dependencies: - '@maxim_mazurok/gapi.client.drive-v3': 0.1.20260118 + '@maxim_mazurok/gapi.client.drive-v3': 0.1.20260210 dev: true /@types/gapi.client.sheets-v4@0.0.4: resolution: {integrity: sha512-6kTJ7aDMAElfdQV1XzVJmZWjgbibpa84DMuKuaN8Cwqci/dkglPyHXKvsGrRugmuYvgFYr35AQqwz6j3q8R0dw==} dependencies: - '@maxim_mazurok/gapi.client.sheets-v4': 0.1.20260114 + '@maxim_mazurok/gapi.client.sheets-v4': 0.1.20260210 dev: true /@types/gapi.client@1.0.8: @@ -7513,7 +7517,6 @@ packages: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - dev: true /crypto-random-string@2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} @@ -8641,14 +8644,12 @@ packages: /eventsource-parser@3.0.6: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} - dev: true /eventsource@3.0.7: resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} engines: {node: '>=18.0.0'} dependencies: eventsource-parser: 3.0.6 - dev: true /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} @@ -8668,7 +8669,7 @@ packages: resolution: {integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==} engines: {node: ^14.18.0 || ^16.14.0 || >=18.0.0} dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 6.0.1 human-signals: 4.3.1 is-stream: 3.0.0 @@ -8755,7 +8756,6 @@ packages: express: '>= 4.11' dependencies: express: 5.1.0 - dev: true /express@4.18.2: resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==} @@ -9324,7 +9324,7 @@ packages: '@google-cloud/cloud-sql-connector': 1.7.0 '@google-cloud/pubsub': 4.10.0 '@inquirer/prompts': 7.8.4(@types/node@24.3.1) - '@modelcontextprotocol/sdk': 1.17.5 + '@modelcontextprotocol/sdk': 1.12.0 abort-controller: 3.0.0 ajv: 8.17.1 ajv-formats: 3.0.1 @@ -9391,8 +9391,8 @@ packages: winston-transport: 4.9.0 ws: 7.5.10 yaml: 2.7.0 - zod: 3.25.76 - zod-to-json-schema: 3.24.6(zod@3.25.76) + zod: 3.25.0 + zod-to-json-schema: 3.24.6(zod@3.25.0) transitivePeerDependencies: - '@types/node' - bufferutil @@ -12960,7 +12960,6 @@ packages: /pkce-challenge@5.0.0: resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} engines: {node: '>=16.20.0'} - dev: true /pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} @@ -16541,15 +16540,15 @@ packages: readable-stream: 4.7.0 dev: true - /zod-to-json-schema@3.24.6(zod@3.25.76): + /zod-to-json-schema@3.24.6(zod@3.25.0): resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} peerDependencies: zod: ^3.24.1 dependencies: - zod: 3.25.76 + zod: 3.25.0 - /zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + /zod@3.25.0: + resolution: {integrity: sha512-ficnZKUW0mlNivqeJkosTEkGbJ6NKCtSaOHGx5aXbtfeWMdRyzXLbAIn19my4C/KB7WPY/p9vlGPt+qpOp6c4Q==} /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} From c3b3391cbf8c4187714d7f4b607e21471ee976da Mon Sep 17 00:00:00 2001 From: Steven Le Date: Fri, 20 Feb 2026 14:25:29 -0800 Subject: [PATCH 2/2] feat: add MCP tools to root-cms plugin Register cms.listCollections, cms.listDocs, cms.getDoc, and cms.saveDoc MCP tools via the configureMcpServer hook, enabling AI tools to interact with CMS content through the MCP protocol. Co-Authored-By: Claude Opus 4.6 --- packages/root-cms/core/plugin.ts | 109 +++++++++++++++++++++++++++++++ packages/root-cms/package.json | 4 +- pnpm-lock.yaml | 22 +++++-- 3 files changed, 128 insertions(+), 7 deletions(-) diff --git a/packages/root-cms/core/plugin.ts b/packages/root-cms/core/plugin.ts index 4e70974ab..3f2d5a4a3 100644 --- a/packages/root-cms/core/plugin.ts +++ b/packages/root-cms/core/plugin.ts @@ -23,6 +23,8 @@ import {getAuth, DecodedIdToken} from 'firebase-admin/auth'; import {Firestore, getFirestore} from 'firebase-admin/firestore'; import * as jsonwebtoken from 'jsonwebtoken'; import sirv from 'sirv'; +import glob from 'tiny-glob'; +import {z} from 'zod'; import {SSEEvent, SSESchemaChangedEvent} from '../shared/sse.js'; import {type RootAiModel} from './ai.js'; import {api} from './api.js'; @@ -723,6 +725,113 @@ export function cmsPlugin(options: CMSPluginOptions): CMSPlugin { } }); }, + + configureMcpServer: async (server, {rootConfig}) => { + const cmsClient = new RootCMSClient(rootConfig); + + server.tool( + 'cms.listCollections', + 'Lists all CMS collections defined in the project', + {}, + async () => { + const collectionFileNames = await glob('*.schema.ts', { + cwd: path.join(rootConfig.rootDir, 'collections'), + }); + const collectionIds = collectionFileNames.map((filename) => + filename.slice(0, -'.schema.ts'.length) + ); + return { + content: [ + {type: 'text', text: JSON.stringify(collectionIds, null, 2)}, + ], + }; + } + ); + + server.tool( + 'cms.listDocs', + 'Lists documents in a CMS collection', + { + collectionId: z.string().describe('The collection ID'), + mode: z + .enum(['draft', 'published']) + .default('draft') + .describe('Document mode'), + limit: z + .number() + .optional() + .describe('Max number of documents to return'), + }, + async ({collectionId, mode, limit}) => { + const result = await cmsClient.listDocs(collectionId, { + mode, + limit, + }); + const docs = result.docs.map((doc: any) => ({ + id: doc.id, + slug: doc.slug, + sys: { + modifiedAt: doc.sys?.modifiedAt, + modifiedBy: doc.sys?.modifiedBy, + }, + })); + return { + content: [{type: 'text', text: JSON.stringify(docs, null, 2)}], + }; + } + ); + + server.tool( + 'cms.getDoc', + 'Reads a single document from the CMS', + { + collectionId: z.string().describe('The collection ID'), + slug: z.string().describe('The document slug'), + mode: z + .enum(['draft', 'published']) + .default('draft') + .describe('Document mode'), + }, + async ({collectionId, slug, mode}) => { + const doc = await cmsClient.getDoc(collectionId, slug, {mode}); + if (!doc) { + return { + content: [ + { + type: 'text', + text: `Document not found: ${collectionId}/${slug}`, + }, + ], + isError: true, + }; + } + return { + content: [{type: 'text', text: JSON.stringify(doc, null, 2)}], + }; + } + ); + + server.tool( + 'cms.saveDoc', + 'Saves draft data to a CMS document', + { + docId: z + .string() + .describe('The document ID (e.g. "Pages/home")'), + data: z + .record(z.string(), z.any()) + .describe('The fields data to save'), + }, + async ({docId, data}) => { + await cmsClient.saveDraftData(docId, data); + return { + content: [ + {type: 'text', text: `Successfully saved draft: ${docId}`}, + ], + }; + } + ); + }, }; if (process.env.NODE_ENV === 'development' && options.watch !== false) { diff --git a/packages/root-cms/package.json b/packages/root-cms/package.json index b132669be..e26066fe2 100644 --- a/packages/root-cms/package.json +++ b/packages/root-cms/package.json @@ -69,6 +69,7 @@ "@ag-grid-community/core": "32.3.9", "@ag-grid-community/react": "32.3.9", "@ag-grid-community/styles": "32.3.9", + "@modelcontextprotocol/sdk": "1.12.0", "@genkit-ai/ai": "1.26.0", "@genkit-ai/core": "1.26.0", "@genkit-ai/google-genai": "1.26.0", @@ -90,7 +91,8 @@ "kleur": "4.1.5", "react-easy-crop": "5.5.6", "sirv": "2.0.3", - "tiny-glob": "0.2.9" + "tiny-glob": "0.2.9", + "zod": "3.24.4" }, "//": "NOTE(stevenle): due to compat issues with mantine and preact, mantine is pinned to v4.2.12", "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bfd09aa68..01ff87e34 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -379,6 +379,9 @@ importers: '@hello-pangea/dnd': specifier: 18.0.1 version: 18.0.1(@preact/compat@18.3.1)(@preact/compat@18.3.1) + '@modelcontextprotocol/sdk': + specifier: 1.12.0 + version: 1.12.0 '@types/cli-progress': specifier: 3.11.6 version: 3.11.6 @@ -430,6 +433,9 @@ importers: tiny-glob: specifier: 0.2.9 version: 0.2.9 + zod: + specifier: 3.24.4 + version: 3.24.4 devDependencies: '@babel/core': specifier: 7.17.9 @@ -3653,15 +3659,15 @@ packages: '@types/gapi.client.discovery-v1': 0.0.4 dev: true - /@maxim_mazurok/gapi.client.drive-v3@0.1.20260210: - resolution: {integrity: sha512-+AbG6C4goi1oWUkutBVHkIv2gbcYBzdoDZ7MzvFCNME9vLsh5U3OKurXe4Tek8D9Cj7h+/umsKh/YLuVoePeqQ==} + /@maxim_mazurok/gapi.client.drive-v3@0.1.20260215: + resolution: {integrity: sha512-u2FW7c54o1aA4HDXiQSc28urNQwLfegS9FzfD3TnLfAHcTC8OO83Dq03nAUJFuWbThQNdo7QmRskZjB9EIx7Sw==} dependencies: '@types/gapi.client': 1.0.8 '@types/gapi.client.discovery-v1': 0.0.4 dev: true - /@maxim_mazurok/gapi.client.sheets-v4@0.1.20260210: - resolution: {integrity: sha512-ZT7QUSLJoEHugtsRocviV21maMHsNA4jMx0g3lx+buVHKqptQsz43wCePEJ36gkoCsYgPNZ5Yw/6TwTGwaRStQ==} + /@maxim_mazurok/gapi.client.sheets-v4@0.1.20260217: + resolution: {integrity: sha512-G7U1X/mzA6dhkZ4vez/RB1MoKhB4PB0XGVyxcjicjkHwzOnRRUbc8z+LktKAfIVvj1aKlJAf/TgYwHX+JC93Mw==} dependencies: '@types/gapi.client': 1.0.8 '@types/gapi.client.discovery-v1': 0.0.4 @@ -5629,13 +5635,13 @@ packages: /@types/gapi.client.drive-v3@0.0.4: resolution: {integrity: sha512-jE37dJ0EzAdY0aJPFOp20xmec/aO0P4HtUIA9k07RMPyedFDOcuMlSac1r0PklwQdgXF7BHaMoObNHNAnwSQUQ==} dependencies: - '@maxim_mazurok/gapi.client.drive-v3': 0.1.20260210 + '@maxim_mazurok/gapi.client.drive-v3': 0.1.20260215 dev: true /@types/gapi.client.sheets-v4@0.0.4: resolution: {integrity: sha512-6kTJ7aDMAElfdQV1XzVJmZWjgbibpa84DMuKuaN8Cwqci/dkglPyHXKvsGrRugmuYvgFYr35AQqwz6j3q8R0dw==} dependencies: - '@maxim_mazurok/gapi.client.sheets-v4': 0.1.20260210 + '@maxim_mazurok/gapi.client.sheets-v4': 0.1.20260217 dev: true /@types/gapi.client@1.0.8: @@ -16547,6 +16553,10 @@ packages: dependencies: zod: 3.25.0 + /zod@3.24.4: + resolution: {integrity: sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==} + dev: false + /zod@3.25.0: resolution: {integrity: sha512-ficnZKUW0mlNivqeJkosTEkGbJ6NKCtSaOHGx5aXbtfeWMdRyzXLbAIn19my4C/KB7WPY/p9vlGPt+qpOp6c4Q==}