@google/clasp 3.0.3-alpha → 3.0.5-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -4,7 +4,6 @@
4
4
  <a href="https://coveralls.io/github/google/clasp?branch=master"><img src="https://coveralls.io/repos/github/google/clasp/badge.svg?branch=master" alt="Coverage Status"></a>
5
5
  <a href="https://www.npmjs.com/package/@google/clasp"><img src="https://img.shields.io/npm/v/@google/clasp.svg" alt="npm Version"></a>
6
6
  <a href="https://npmcharts.com/compare/@google/clasp?minimal=true"><img src="https://img.shields.io/npm/dw/@google/clasp.svg" alt="npm Downloads"></a>
7
- <a href="https://david-dm.org/google/clasp" title="dependencies status"><img src="https://david-dm.org/google/clasp/status.svg"/></a>
8
7
  <a href="https://github.com/google/gts" title="Code Style: Google"><img src="https://img.shields.io/badge/code%20style-google-blueviolet.svg"/></a>
9
8
 
10
9
  > Develop [Apps Script](https://developers.google.com/apps-script/) projects locally using clasp (**C**ommand **L**ine **A**pps **S**cript **P**rojects).
@@ -82,7 +81,7 @@ clasp
82
81
  - [`clasp pull [--versionNumber]`](#pull)
83
82
  - [`clasp push [--watch] [--force]`](#push)
84
83
  - [`clasp show-file-status [--json]`](#status)
85
- - [`clasp open-script](#open)
84
+ - [`clasp open-script`](#open)
86
85
  - [`clasp list-deployments`](#deployments)
87
86
  - [`clasp create-deployment [--versionNumber <version>] [--description <description>] [--deploymentId <id>]`](#deploy)
88
87
  - [`clasp delete-deployment [deploymentId] [--all]`](#undeploy)
@@ -98,7 +97,7 @@ clasp
98
97
  - [`clasp list-apis`](#apis)
99
98
  - [`clasp enable-api<api>`](#apis)
100
99
  - [`clasp disable-api <api>`](#apis)
101
- - [`clasp run-function [function]`](#clas-run)
100
+ - [`clasp run-function [function]`](#clasp-run)
102
101
 
103
102
  ## Guides
104
103
 
@@ -144,7 +143,7 @@ Most command require user authorization. Run `clasp login` to authorize access t
144
143
 
145
144
  #### Multiple user support
146
145
 
147
- Use the global `--user` option to switch between accounts. THis support both running clasp as different users as well as when invoking the `clasp run-function` command.
146
+ Use the global `--user` option to switch between accounts. This supports both running clasp as different users as well as when invoking the `clasp run-function` command.
148
147
 
149
148
  Examples:
150
149
 
@@ -152,7 +151,7 @@ Examples:
152
151
  clasp login # Saves as default credentials
153
152
  clasp clone # User not specified, runs using default credentials
154
153
  clasp login --user testaccount # Authorized new named credentials
155
- claso run-function --user testaccount myFunction # Runs function as test account
154
+ clasp run-function --user testaccount myFunction # Runs function as test account
156
155
  ```
157
156
 
158
157
  ### Bring your own project/credentials
@@ -161,9 +160,9 @@ While clasp includes a default OAuth client, using your own project is recommend
161
160
 
162
161
  1. [Create a new project](https://cloud.google.com/resource-manager/docs/creating-managing-projects) in the Google Cloud Developer Console.
163
162
  1. [Create an OAuth client](https://support.google.com/cloud/answer/15549257?hl=en#:~:text=To%20create%20an%20OAuth%202.0,are%20yet%20to%20do%20so.). The client type must be `Desktop Application`. Download and save the generated client secrets file. This is required when authorizing using the`clasp login --creds <filename>` command.
164
- 1. [Enable services](https://cloud.google.com/endpoints/docs/openapi/enable-api). For full functionaliy, clasp requires the following:
165
- * Apps SCript API - `script.googleapis.com` (required)
166
- * Service Usage API - `serviceusage.googleapis.com` (require to list/enable/disable APIs)
163
+ 1. [Enable services](https://cloud.google.com/endpoints/docs/openapi/enable-api). For full functionality, clasp requires the following:
164
+ * Apps Script API - `script.googleapis.com` (required)
165
+ * Service Usage API - `serviceusage.googleapis.com` (required to list/enable/disable APIs)
167
166
  * Google Drive API - `drive.googleapis.com` (required to list scripts, create container-bound scripts)
168
167
  - Cloud Logging API - `logging.googleapis.com` (required to read logs)
169
168
 
@@ -192,7 +191,7 @@ If your organization restricts authorization for third-party apps, you may eithe
192
191
 
193
192
  ### Service accounts
194
193
 
195
- Use the `--adc` option on any command to read credentials from the environemtn using Google Cloud's [application default credentials](https://cloud.google.com/docs/authentication/application-default-credentials) mechanism.
194
+ Use the `--adc` option on any command to read credentials from the environment using Google Cloud's [application default credentials](https://cloud.google.com/docs/authentication/application-default-credentials) mechanism.
196
195
 
197
196
  Note that if using a service account, service accounts can not own scripts. To use a service account to push or pull files from Apps Script, the scripts must be shared with the service account with the appropriate role (e.g. `Editor` in able to push.)
198
197
 
@@ -395,6 +394,8 @@ Updates local files with Apps Script project.
395
394
  #### Options
396
395
 
397
396
  - `--versionNumber <number>`: The version number of the project to retrieve.
397
+ - `--deleteUnusedFiles`: Deletes local files that would have been pushed that were not returned by the server. Prompts for confirmation
398
+ - `--force`: Used with `--deleteUnusedFiles` to automatically confirm. Use with caution.
398
399
 
399
400
  #### Examples
400
401
 
@@ -542,6 +543,23 @@ Lists your most recent Apps Script projects.
542
543
 
543
544
  - `clasp list-scripts`: Prints `helloworld1 – xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...`
544
545
 
546
+ ### MCP (EXPERIMENTAL)
547
+
548
+ Runs clasp in MCP (model context protocol) mode for use with coding agents. Configure clasp as a local tool using STDIO transport. While running in MCP mode clasp uses the same credentials as
549
+ normal when used as a CLI. Run `clasp login` ahead of time to authorize.
550
+
551
+ When used in MCP mode clasp does not need to be started from the project directory. The project directoy is specified in the tool calls. Switching projects does not require a restart of the MCP server, while switching credentials does.
552
+
553
+ This feature is experimental and currently offers a limited subset of tools for agents. Feedback is welcome.
554
+
555
+ #### Options
556
+
557
+ N/A
558
+
559
+ #### Examples
560
+
561
+ - `clasp mcp`
562
+
545
563
  ## Advanced Commands
546
564
 
547
565
  > **NOTE**: These commands require Project ID/credentials setup ([see below](#projectid-optional)).
@@ -24,17 +24,22 @@ import { command as runCommand } from './run-function.js';
24
24
  import { command as setupLogsCommand } from './setup-logs.js';
25
25
  import { command as authStatusCommand } from './show-authorized-user.js';
26
26
  import { command as filesStatusCommand } from './show-file-status.js';
27
+ import { command as mcpCommand } from './start-mcp.js';
27
28
  import { command as tailLogsCommand } from './tail-logs.js';
28
29
  import { dirname } from 'path';
29
30
  import { fileURLToPath } from 'url';
30
- import { readPackageUpSync } from 'read-pkg-up';
31
+ import { readPackageUpSync } from 'read-package-up';
31
32
  import { initAuth } from '../auth/auth.js';
32
33
  import { initClaspInstance } from '../core/clasp.js';
33
34
  import { intl } from '../intl.js';
34
- export function makeProgram(exitOveride) {
35
+ export function getVersion() {
35
36
  const __dirname = dirname(fileURLToPath(import.meta.url));
36
37
  const manifest = readPackageUpSync({ cwd: __dirname });
37
38
  const version = manifest ? manifest.packageJson.version : 'unknown';
39
+ return version;
40
+ }
41
+ export function makeProgram(exitOveride) {
42
+ const version = getVersion();
38
43
  const program = new Command();
39
44
  program.exitOverride(exitOveride);
40
45
  program.storeOptionsAsProperties(false);
@@ -92,6 +97,7 @@ export function makeProgram(exitOveride) {
92
97
  listCommand,
93
98
  createVersionCommand,
94
99
  listVersionsCommand,
100
+ mcpCommand,
95
101
  ];
96
102
  for (const cmd of commandsToAdd) {
97
103
  program.addCommand(cmd);
@@ -1,19 +1,55 @@
1
1
  import { Command } from 'commander';
2
+ import fs from 'fs/promises';
3
+ import inquirer from 'inquirer';
2
4
  import { intl } from '../intl.js';
3
- import { withSpinner } from './utils.js';
5
+ import { isInteractive, withSpinner } from './utils.js';
4
6
  export const command = new Command('pull')
5
7
  .description('Fetch a remote project')
6
8
  .option('--versionNumber <version>', 'The version number of the project to retrieve.')
9
+ .option('-d, --deleteUnusedFiles ', 'Delete local files that are not in the remote project. Use with caution.')
10
+ .option('-f, --force', 'Forcibly delete local files that are not in the remote project without prompting.')
7
11
  .action(async function (options) {
8
12
  const clasp = this.opts().clasp;
9
13
  const versionNumber = options.versionNumber;
10
- const spinnerMsg = intl.formatMessage({ id: "jilcJH", defaultMessage: [{ type: 0, value: "Pulling files..." }] });
14
+ const forceDelete = options.force;
15
+ let spinnerMsg = intl.formatMessage({ id: "dh7Bw6", defaultMessage: [{ type: 0, value: "Checking local files..." }] });
16
+ const localFiles = await clasp.files.collectLocalFiles();
17
+ spinnerMsg = intl.formatMessage({ id: "jilcJH", defaultMessage: [{ type: 0, value: "Pulling files..." }] });
11
18
  const files = await withSpinner(spinnerMsg, async () => {
12
19
  return await clasp.files.pull(versionNumber);
13
20
  });
21
+ if (options.deleteUnusedFiles) {
22
+ const filesToDelete = localFiles.filter(f => !files.find(p => p.localPath === f.localPath));
23
+ await deleteLocalFiles(filesToDelete, forceDelete);
24
+ }
14
25
  files.forEach(f => console.log(`└─ ${f.localPath}`));
15
26
  const successMessage = intl.formatMessage({ id: "4mRAfN", defaultMessage: [{ type: 0, value: "Pulled " }, { type: 6, value: "count", options: { "=0": { value: [{ type: 0, value: "no files." }] }, one: { value: [{ type: 0, value: "one file." }] }, other: { value: [{ type: 7 }, { type: 0, value: " files" }] } }, offset: 0, pluralType: "cardinal" }, { type: 0, value: "." }] }, {
16
27
  count: files.length,
17
28
  });
18
29
  console.log(successMessage);
19
30
  });
31
+ async function deleteLocalFiles(filesToDelete, forceDelete = false) {
32
+ if (!filesToDelete || filesToDelete.length === 0) {
33
+ return;
34
+ }
35
+ const skipConfirmation = forceDelete;
36
+ if (!isInteractive() && !forceDelete) {
37
+ const msg = intl.formatMessage({ id: "zLuvSg", defaultMessage: [{ type: 0, value: "You are not in an interactive terminal and --force not used. Skipping file deletion." }] });
38
+ console.warn(msg);
39
+ return;
40
+ }
41
+ for (const file of filesToDelete) {
42
+ if (!skipConfirmation) {
43
+ const confirm = await inquirer.prompt({
44
+ type: 'confirm',
45
+ name: 'deleteFile',
46
+ message: intl.formatMessage({ id: "lVx/lI", defaultMessage: [{ type: 0, value: "Delete " }, { type: 1, value: "file" }, { type: 0, value: "?" }] }, { file: file.localPath }),
47
+ });
48
+ if (!confirm.deleteFile) {
49
+ continue;
50
+ }
51
+ }
52
+ await fs.unlink(file.localPath);
53
+ console.log(intl.formatMessage({ id: "Nx315v", defaultMessage: [{ type: 0, value: "Deleted " }, { type: 1, value: "file" }] }, { file: file.localPath }));
54
+ }
55
+ }
@@ -0,0 +1,12 @@
1
+ import { Command } from 'commander';
2
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
3
+ import { buildMcpServer } from '../mcp/server.js';
4
+ export const command = new Command('start-mcp-server')
5
+ .alias('mcp')
6
+ .description('Starts an MCP server for interacting with apps script.')
7
+ .action(async function () {
8
+ const auth = this.opts().auth;
9
+ const server = buildMcpServer(auth);
10
+ const transport = new StdioServerTransport();
11
+ await server.connect(transport);
12
+ });
@@ -63,11 +63,13 @@ export class Clasp {
63
63
  }
64
64
  }
65
65
  export async function initClaspInstance(options) {
66
+ var _a;
66
67
  debug('Initializing clasp instance');
67
68
  const projectRoot = await findProjectRootdDir(options.configFile);
68
69
  if (!projectRoot) {
69
- debug('No project found, defaulting to cwd');
70
- const rootDir = path.resolve(process.cwd());
70
+ const dir = (_a = options.rootDir) !== null && _a !== void 0 ? _a : process.cwd();
71
+ debug(`No project found, defaulting to ${dir}`);
72
+ const rootDir = path.resolve(dir);
71
73
  const configFilePath = path.resolve(rootDir, '.clasp.json');
72
74
  const ignoreFile = await findIgnoreFile(rootDir, options.ignoreFile);
73
75
  const ignoreRules = await loadIgnoreFileOrDefaults(ignoreFile);
@@ -177,7 +177,7 @@ export class Files {
177
177
  const localPath = path.relative(process.cwd(), path.join(contentDir, filename));
178
178
  const resolvedPath = path.relative(contentDir, localPath);
179
179
  const parsedPath = path.parse(resolvedPath);
180
- let remotePath = path.format({ dir: normalizePath(parsedPath.dir), name: parsedPath.name });
180
+ let remotePath = normalizePath(path.format({ dir: parsedPath.dir, name: parsedPath.name }));
181
181
  const type = getFileType(localPath, fileExtensionMap);
182
182
  if (!type) {
183
183
  debug('Ignoring unsupported file %s', localPath);
@@ -270,7 +270,7 @@ export class Files {
270
270
  if (dirsWithIncludedFiles.has(dir)) {
271
271
  break;
272
272
  }
273
- excludedPath = `${dir}/`;
273
+ excludedPath = path.normalize(`${dir}/`);
274
274
  }
275
275
  debug('Found untracked file %s', excludedPath);
276
276
  untrackedFiles.add(excludedPath);
@@ -0,0 +1,329 @@
1
+ import path from 'path';
2
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
+ import { mkdir } from 'fs/promises';
4
+ import { z } from 'zod';
5
+ import { getDefaultProjectName } from '../commands/create-script.js';
6
+ import { getVersion } from '../commands/program.js';
7
+ import { initClaspInstance } from '../core/clasp.js';
8
+ export function buildMcpServer(auth) {
9
+ const server = new McpServer({
10
+ name: 'Clasp',
11
+ version: getVersion(),
12
+ });
13
+ server.tool('push_files', 'Pushes the local Apps Script project to the remote server.', {
14
+ projectDir: z
15
+ .string()
16
+ .describe('The local directory of the Apps Script project to push. Must contain a .clasp.json file containing the project info.'),
17
+ }, {
18
+ title: 'Push project files to Apps Script',
19
+ openWorldHint: false,
20
+ destructiveHint: true,
21
+ idempotentHint: false,
22
+ readOnlyHint: false,
23
+ }, async ({ projectDir }) => {
24
+ if (!projectDir) {
25
+ return {
26
+ isError: true,
27
+ content: [
28
+ {
29
+ type: 'text',
30
+ text: 'Project directory is required.',
31
+ },
32
+ ],
33
+ };
34
+ }
35
+ const clasp = await initClaspInstance({
36
+ credentials: auth.credentials,
37
+ configFile: projectDir,
38
+ rootDir: projectDir,
39
+ });
40
+ try {
41
+ const files = await clasp.files.push();
42
+ const fileList = files.map(file => ({
43
+ type: 'text',
44
+ text: `Updated file: ${path.resolve(file.localPath)}`,
45
+ }));
46
+ return {
47
+ status: 'success',
48
+ content: [
49
+ {
50
+ type: 'text',
51
+ text: `Pushed project in ${projectDir} to remote server successfully.`,
52
+ },
53
+ ...fileList,
54
+ ],
55
+ structuredContent: {
56
+ scriptId: clasp.project.scriptId,
57
+ projectDir: projectDir,
58
+ files: files.map(file => path.resolve(file.localPath)),
59
+ },
60
+ };
61
+ }
62
+ catch (err) {
63
+ return {
64
+ isError: true,
65
+ content: [
66
+ {
67
+ type: 'text',
68
+ text: `Error pushing project: ${err.message}`,
69
+ },
70
+ ],
71
+ };
72
+ }
73
+ });
74
+ server.tool('pull_files', 'Pulls files from Apps Script project to local file system.', {
75
+ projectDir: z
76
+ .string()
77
+ .describe('The local directory of the Apps Script project to update. Must contain a .clasp.json file containing the project info.'),
78
+ }, {
79
+ title: 'Pull project files from Apps Script',
80
+ openWorldHint: false,
81
+ destructiveHint: true,
82
+ idempotentHint: false,
83
+ readOnlyHint: false,
84
+ }, async ({ projectDir }) => {
85
+ if (!projectDir) {
86
+ return {
87
+ isError: true,
88
+ content: [
89
+ {
90
+ type: 'text',
91
+ text: 'Project directory is required.',
92
+ },
93
+ ],
94
+ };
95
+ }
96
+ const clasp = await initClaspInstance({
97
+ credentials: auth.credentials,
98
+ configFile: projectDir,
99
+ rootDir: projectDir,
100
+ });
101
+ try {
102
+ const files = await clasp.files.pull();
103
+ const fileList = files.map(file => ({
104
+ type: 'text',
105
+ text: `Updated file: ${path.resolve(file.localPath)}`,
106
+ }));
107
+ return {
108
+ content: [
109
+ {
110
+ type: 'text',
111
+ text: `Pushed project in ${projectDir} to remote server successfully.`,
112
+ },
113
+ ...fileList,
114
+ ],
115
+ structuredContent: {
116
+ scriptId: clasp.project.scriptId,
117
+ projectDir: projectDir,
118
+ files: files.map(file => path.resolve(file.localPath)),
119
+ },
120
+ };
121
+ }
122
+ catch (err) {
123
+ return {
124
+ isError: true,
125
+ content: [
126
+ {
127
+ type: 'text',
128
+ text: `Error pushing project: ${err.message}`,
129
+ },
130
+ ],
131
+ };
132
+ }
133
+ });
134
+ server.tool('create_project', 'Create a new apps script project.', {
135
+ projectDir: z.string().describe('The local directory where the Apps Script project will be created.'),
136
+ sourceDir: z
137
+ .string()
138
+ .optional()
139
+ .describe('Local directory relative to projectDir where the Apps Script source files are located. If not specified, files are placed in the project directory.'),
140
+ projectName: z
141
+ .string()
142
+ .optional()
143
+ .describe('Name of the project. If not provided, the project name will be infered from the directory.'),
144
+ }, {
145
+ title: 'Create Apps Script project',
146
+ openWorldHint: false,
147
+ destructiveHint: true,
148
+ idempotentHint: false,
149
+ readOnlyHint: false,
150
+ }, async ({ projectDir, sourceDir, projectName }) => {
151
+ if (!projectDir) {
152
+ return {
153
+ isError: true,
154
+ content: [
155
+ {
156
+ type: 'text',
157
+ text: 'Project directory is required.',
158
+ },
159
+ ],
160
+ };
161
+ }
162
+ await mkdir(projectDir, { recursive: true });
163
+ if (!projectName) {
164
+ projectName = getDefaultProjectName(projectDir);
165
+ }
166
+ const clasp = await initClaspInstance({
167
+ credentials: auth.credentials,
168
+ configFile: projectDir,
169
+ rootDir: projectDir,
170
+ });
171
+ clasp.withContentDir(sourceDir !== null && sourceDir !== void 0 ? sourceDir : '.');
172
+ try {
173
+ const id = await clasp.project.createScript(projectName);
174
+ const files = await clasp.files.pull();
175
+ await clasp.project.updateSettings();
176
+ const fileList = files.map(file => ({
177
+ type: 'text',
178
+ text: `Updated file: ${path.resolve(file.localPath)}`,
179
+ }));
180
+ return {
181
+ content: [
182
+ {
183
+ type: 'text',
184
+ text: `Created project ${id} in ${projectDir} successfully.`,
185
+ },
186
+ ...fileList,
187
+ ],
188
+ structuredContent: {
189
+ scriptId: id,
190
+ projectDir: projectDir,
191
+ files: files.map(file => path.resolve(file.localPath)),
192
+ },
193
+ };
194
+ }
195
+ catch (err) {
196
+ return {
197
+ isError: true,
198
+ content: [
199
+ {
200
+ type: 'text',
201
+ text: `Error pushing project: ${err.message}`,
202
+ },
203
+ ],
204
+ };
205
+ }
206
+ });
207
+ server.tool('clone_project', 'Clones and pulls an existing Apps Script project to a local directory.', {
208
+ projectDir: z.string().describe('The local directory where the Apps Script project will be created.'),
209
+ sourceDir: z
210
+ .string()
211
+ .optional()
212
+ .describe('Local directory relative to projectDir where the Apps Script source files are located. If not specified, files are placed in the project directory.'),
213
+ scriptId: z.string().optional().describe('ID of the Apps Script project to clone.'),
214
+ }, {
215
+ title: 'Create Apps Script project',
216
+ openWorldHint: false,
217
+ destructiveHint: true,
218
+ idempotentHint: false,
219
+ readOnlyHint: false,
220
+ }, async ({ projectDir, sourceDir, scriptId }) => {
221
+ if (!projectDir) {
222
+ return {
223
+ isError: true,
224
+ content: [
225
+ {
226
+ type: 'text',
227
+ text: 'Project directory is required.',
228
+ },
229
+ ],
230
+ };
231
+ }
232
+ await mkdir(projectDir, { recursive: true });
233
+ if (!scriptId) {
234
+ return {
235
+ isError: true,
236
+ content: [
237
+ {
238
+ type: 'text',
239
+ text: 'Script ID is required.',
240
+ },
241
+ ],
242
+ };
243
+ }
244
+ const clasp = await initClaspInstance({
245
+ credentials: auth.credentials,
246
+ configFile: projectDir,
247
+ rootDir: projectDir,
248
+ });
249
+ clasp.withContentDir(sourceDir !== null && sourceDir !== void 0 ? sourceDir : '.').withScriptId(scriptId);
250
+ try {
251
+ const files = await clasp.files.pull();
252
+ clasp.project.updateSettings();
253
+ const fileList = files.map(file => ({
254
+ type: 'text',
255
+ text: `Updated file: ${path.resolve(file.localPath)}`,
256
+ }));
257
+ return {
258
+ content: [
259
+ {
260
+ type: 'text',
261
+ text: `Cloned project ${scriptId} in ${projectDir} successfully.`,
262
+ },
263
+ ...fileList,
264
+ ],
265
+ structuredContent: {
266
+ scriptId: scriptId,
267
+ projectDir: projectDir,
268
+ files: files.map(file => path.resolve(file.localPath)),
269
+ },
270
+ };
271
+ }
272
+ catch (err) {
273
+ return {
274
+ isError: true,
275
+ content: [
276
+ {
277
+ type: 'text',
278
+ text: `Error pushing project: ${err.message}`,
279
+ },
280
+ ],
281
+ };
282
+ }
283
+ });
284
+ server.tool('list_projects', 'List Apps Script projects', {}, {
285
+ title: 'List Apps Script projects',
286
+ openWorldHint: false,
287
+ destructiveHint: true,
288
+ idempotentHint: false,
289
+ readOnlyHint: false,
290
+ }, async () => {
291
+ const clasp = await initClaspInstance({
292
+ credentials: auth.credentials,
293
+ });
294
+ try {
295
+ const scripts = await clasp.project.listScripts();
296
+ const scriptList = scripts.results.map(script => ({
297
+ type: 'text',
298
+ text: `${script.name} (${script.id})`,
299
+ }));
300
+ return {
301
+ content: [
302
+ {
303
+ type: 'text',
304
+ text: `Found ${scripts.results.length} Apps Script projects (script ID in parentheses):`,
305
+ },
306
+ ...scriptList,
307
+ ],
308
+ structuredContent: {
309
+ scripts: scripts.results.map(script => ({
310
+ scriptId: script.id,
311
+ name: script.name,
312
+ })),
313
+ },
314
+ };
315
+ }
316
+ catch (err) {
317
+ return {
318
+ isError: true,
319
+ content: [
320
+ {
321
+ type: 'text',
322
+ text: `Error listing projects: ${err.message}`,
323
+ },
324
+ ],
325
+ };
326
+ }
327
+ });
328
+ return server;
329
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@google/clasp",
3
- "version": "3.0.3-alpha",
3
+ "version": "3.0.5-alpha",
4
4
  "description": "Develop Apps Script Projects locally",
5
5
  "type": "module",
6
6
  "exports": "./build/src/index.js",
@@ -55,81 +55,61 @@
55
55
  "author": "Grant Timmerman",
56
56
  "license": "Apache-2.0",
57
57
  "dependencies": {
58
- "@formatjs/intl": "^3.1.4",
59
- "@messageformat/core": "^3.4.0",
60
- "@sindresorhus/is": "^7.0.1",
61
- "@types/debug": "^4.1.12",
58
+ "@formatjs/intl": "^3.1.6",
62
59
  "chalk": "^5.4.1",
63
60
  "chokidar": "^4.0.3",
64
61
  "cli-truncate": "^4.0.0",
65
- "commander": "^13.0.0",
66
- "debounce": "^2.2.0",
62
+ "commander": "^13.1.0",
67
63
  "debug": "^4.4.0",
68
- "dotf": "^2.0.2",
69
- "fdir": "^6.4.3",
64
+ "fdir": "^6.4.4",
70
65
  "find-up": "^7.0.0",
71
66
  "fuzzy": "^0.1.3",
72
- "gaxios": "^6.7.1",
73
- "google-auth-library": "^9.15.0",
74
- "googleapis": "^144.0.0",
67
+ "google-auth-library": "^9.15.1",
68
+ "googleapis": "^148.0.0",
75
69
  "googleapis-common": "7.2.0",
76
70
  "inflection": "^3.0.2",
77
- "inquirer": "^12.3.2",
71
+ "inquirer": "^12.6.0",
78
72
  "inquirer-autocomplete-standalone": "^0.8.1",
79
- "log-symbols": "^7.0.0",
80
73
  "loud-rejection": "^2.2.0",
81
- "make-dir": "^5.0.0",
82
74
  "micromatch": "^4.0.8",
83
- "multimatch": "^7.0.0",
84
- "normalize-newline": "^4.1.0",
75
+ "@modelcontextprotocol/sdk": "^1.12.1",
85
76
  "normalize-path": "^3.0.0",
86
- "open": "^10.1.0",
77
+ "open": "^10.1.2",
87
78
  "ora": "^8.1.1",
88
79
  "p-map": "^7.0.3",
89
80
  "picomatch": "^4.0.2",
90
- "read-pkg-up": "^11.0.0",
81
+ "read-package-up": "^11.0.0",
91
82
  "server-destroy": "^1.0.1",
92
83
  "split-lines": "^3.0.0",
93
84
  "strip-bom": "^5.0.0",
94
- "typescript": "^5.7.3"
85
+ "zod": "^3.25.36"
95
86
  },
96
87
  "devDependencies": {
97
88
  "@biomejs/biome": "^1.9.4",
98
- "@commander-js/extra-typings": "^13.0.0",
99
- "@formatjs/ts-transformer": "^3.13.32",
100
- "@istanbuljs/nyc-config-typescript": "^1.0.2",
101
- "@messageformat/cli": "^4.0.1",
102
- "@types/chai": "^5.0.1",
103
- "@types/chai-as-promised": "^8.0.1",
104
- "@types/chai-fs": "^2.0.5",
105
- "@types/chai-subset": "^1.3.5",
106
- "@types/debounce": "^1.2.4",
107
- "@types/fs-extra": "^11.0.4",
89
+ "@commander-js/extra-typings": "^13.1.0",
90
+ "@formatjs/ts-transformer": "^3.13.34",
91
+ "@types/chai": "^5.2.2",
92
+ "@types/chai-as-promised": "^8.0.2",
93
+ "@types/debug": "^4.1.12",
108
94
  "@types/micromatch": "^4.0.9",
109
95
  "@types/mocha": "^10.0.10",
110
96
  "@types/mock-fs": "^4.13.4",
111
- "@types/node": "^22.10.10",
97
+ "@types/node": "^22.15.17",
112
98
  "@types/normalize-path": "^3.0.2",
113
- "@types/picomatch": "^3.0.2",
99
+ "@types/picomatch": "^4.0.0",
114
100
  "@types/server-destroy": "^1.0.4",
115
101
  "@types/sinon": "^17.0.4",
116
- "@types/tmp": "^0.2.6",
117
- "@types/wtfnode": "^0.7.3",
118
102
  "c8": "^10.1.3",
119
- "chai": "^5.1.2",
103
+ "chai": "^5.2.0",
120
104
  "chai-as-promised": "^8.0.1",
121
- "chai-subset": "^1.6.0",
122
- "mocha": "^11.1.0",
123
- "mock-fs": "^5.4.1",
124
- "nock": "^14.0.0",
125
- "nyc": "^17.1.0",
126
- "sinon": "^19.0.2",
105
+ "mocha": "^11.2.2",
106
+ "mock-fs": "^5.5.0",
107
+ "nock": "^14.0.4",
108
+ "sinon": "^20.0.0",
127
109
  "source-map-support": "^0.5.21",
128
- "tmp": "^0.2.3",
129
110
  "ts-node": "^10.9.2",
130
111
  "ts-patch": "^3.3.0",
131
- "type-fest": "^4.33.0",
132
- "why-is-node-running": "^3.2.2",
133
- "wtfnode": "^0.10.0"
112
+ "type-fest": "^4.41.0",
113
+ "typescript": "^5.8.3"
134
114
  }
135
115
  }