@google-cloud/cloud-run-mcp 1.10.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/.github/workflows/npm-publish.yml +10 -6
  2. package/README.md +26 -0
  3. package/SECURITY.md +7 -0
  4. package/cloudbuild.yaml +37 -0
  5. package/example-sources-to-deploy/java/pom.xml +41 -0
  6. package/example-sources-to-deploy/java/src/main/java/com/example/helloworld/HelloworldApplication.java +21 -0
  7. package/example-sources-to-deploy/java/src/main/resources/application.properties +1 -0
  8. package/example-sources-to-deploy/nodejs/index.js +15 -0
  9. package/example-sources-to-deploy/nodejs/package.json +12 -0
  10. package/example-sources-to-deploy/python/pip-project/main.py +11 -0
  11. package/example-sources-to-deploy/python/pip-project/requirements.txt +2 -0
  12. package/example-sources-to-deploy/python/pyproject-project/main.py +11 -0
  13. package/example-sources-to-deploy/python/pyproject-project/pyproject.toml +10 -0
  14. package/lib/clients.js +142 -12
  15. package/lib/cloud-api/build.js +192 -28
  16. package/lib/cloud-api/run.js +62 -0
  17. package/lib/cloud-api/secrets.js +196 -0
  18. package/lib/cloud-api/storage.js +164 -16
  19. package/lib/deployment/compose.js +517 -0
  20. package/lib/deployment/constants.js +8 -0
  21. package/lib/deployment/deployer.js +283 -28
  22. package/lib/deployment/helpers.js +58 -1
  23. package/lib/deployment/universal-maker.js +21 -148
  24. package/lib/middleware/oauth.js +26 -15
  25. package/lib/util/archive.js +3 -3
  26. package/lib/util/artifacts.js +189 -0
  27. package/lib/util/helpers.js +109 -0
  28. package/mcp-server.js +83 -20
  29. package/package.json +7 -4
  30. package/skills/cloud-run/SKILL.md +109 -0
  31. package/test/local/archive.test.js +88 -0
  32. package/test/local/clients.test.js +307 -2
  33. package/test/local/cloud-api/build.test.js +330 -25
  34. package/test/local/cloud-api/run.test.js +129 -0
  35. package/test/local/cloud-api/secrets.test.js +291 -0
  36. package/test/local/cloud-api/storage.test.js +405 -0
  37. package/test/local/compose.test.js +618 -0
  38. package/test/local/deploy.test.js +408 -0
  39. package/test/local/deployment-helpers.test.js +100 -1
  40. package/test/local/mcp-server-streamable-http.test.js +132 -12
  41. package/test/local/middleware/oauth.test.js +106 -5
  42. package/test/local/test-utils.js +2 -18
  43. package/test/local/tools.test.js +43 -1
  44. package/test/local/universal-maker.test.js +23 -114
  45. package/test/local/util.test.js +361 -0
  46. package/test/need-gcp/deployer.test.js +1 -1
  47. package/test/need-gcp/test-helpers.js +5 -1
  48. package/test/need-gcp/workflows/deployment-workflows.test.js +172 -6
  49. package/tools/register-tools.js +98 -78
  50. /package/example-sources-to-deploy/{Dockerfile → golang/Dockerfile} +0 -0
  51. /package/example-sources-to-deploy/{go.mod → golang/go.mod} +0 -0
  52. /package/example-sources-to-deploy/{main.go → golang/main.go} +0 -0
@@ -49,6 +49,10 @@ function gcpTool(gcpCredentialsAvailable, fn) {
49
49
  return fn;
50
50
  }
51
51
 
52
+ function withDefault(schema, defaultValue) {
53
+ return defaultValue !== undefined ? schema.default(defaultValue) : schema;
54
+ }
55
+
52
56
  // Tool to list GCP projects
53
57
  function registerListProjectsTool(server, options) {
54
58
  server.registerTool(
@@ -147,10 +151,10 @@ function registerListServicesTool(server, options) {
147
151
  {
148
152
  description: 'Lists all Cloud Run services in a given project.',
149
153
  inputSchema: {
150
- project: z
151
- .string()
152
- .describe('Google Cloud project ID')
153
- .default(options.defaultProjectId),
154
+ project: withDefault(
155
+ z.string().describe('Google Cloud project ID'),
156
+ options.defaultProjectId
157
+ ),
154
158
  },
155
159
  },
156
160
  gcpTool(options.gcpCredentialsAvailable, async ({ project }) => {
@@ -202,18 +206,18 @@ function registerGetServiceTool(server, options) {
202
206
  {
203
207
  description: 'Gets details for a specific Cloud Run service.',
204
208
  inputSchema: {
205
- project: z
206
- .string()
207
- .describe('Google Cloud project ID containing the service')
208
- .default(options.defaultProjectId),
209
- region: z
210
- .string()
211
- .describe('Region where the service is located')
212
- .default(options.defaultRegion),
213
- service: z
214
- .string()
215
- .describe('Name of the Cloud Run service')
216
- .default(options.defaultServiceName),
209
+ project: withDefault(
210
+ z.string().describe('Google Cloud project ID containing the service'),
211
+ options.defaultProjectId
212
+ ),
213
+ region: withDefault(
214
+ z.string().describe('Region where the service is located'),
215
+ options.defaultRegion
216
+ ),
217
+ service: withDefault(
218
+ z.string().describe('Name of the Cloud Run service'),
219
+ options.defaultServiceName
220
+ ),
217
221
  },
218
222
  },
219
223
  gcpTool(
@@ -241,11 +245,21 @@ function registerGetServiceTool(server, options) {
241
245
  options.accessToken
242
246
  );
243
247
  if (serviceDetails) {
248
+ // Format last deployment date, handling both Protobuf Timestamp (with
249
+ // seconds as string or number) and standard Date/ISO strings.
250
+ const lastDeployedDate = serviceDetails.updateTime
251
+ ? new Date(
252
+ serviceDetails.updateTime.seconds
253
+ ? Number(serviceDetails.updateTime.seconds) * 1000
254
+ : serviceDetails.updateTime
255
+ ).toUTCString()
256
+ : 'N/A';
257
+ const consoleUrl = `https://console.cloud.google.com/run/detail/${region}/${service}?project=${project}`;
244
258
  return {
245
259
  content: [
246
260
  {
247
261
  type: 'text',
248
- text: `Name: ${service}\nRegion: ${region}\nProject: ${project}\nURL: ${serviceDetails.uri}\nLast deployed by: ${serviceDetails.lastModifier}`,
262
+ text: `Name: ${service}\nRegion: ${region}\nProject: ${project}\nURL: ${serviceDetails.uri}\nConsole URL: ${consoleUrl}\nLast deployed by: ${serviceDetails.lastModifier}\nLast deployed date: ${lastDeployedDate}`,
249
263
  },
250
264
  ],
251
265
  };
@@ -282,18 +296,18 @@ function registerGetServiceLogTool(server, options) {
282
296
  description:
283
297
  'Gets Logs and Error Messages for a specific Cloud Run service.',
284
298
  inputSchema: {
285
- project: z
286
- .string()
287
- .describe('Google Cloud project ID containing the service')
288
- .default(options.defaultProjectId),
289
- region: z
290
- .string()
291
- .describe('Region where the service is located')
292
- .default(options.defaultRegion),
293
- service: z
294
- .string()
295
- .describe('Name of the Cloud Run service')
296
- .default(options.defaultServiceName),
299
+ project: withDefault(
300
+ z.string().describe('Google Cloud project ID containing the service'),
301
+ options.defaultProjectId
302
+ ),
303
+ region: withDefault(
304
+ z.string().describe('Region where the service is located'),
305
+ options.defaultRegion
306
+ ),
307
+ service: withDefault(
308
+ z.string().describe('Name of the Cloud Run service'),
309
+ options.defaultServiceName
310
+ ),
297
311
  },
298
312
  },
299
313
  gcpTool(
@@ -351,22 +365,24 @@ function registerDeployLocalFolderTool(server, options) {
351
365
  description:
352
366
  'Deploy a local folder to Cloud Run. Takes an absolute folder path from the local filesystem that will be deployed. Use this tool if the entire folder content needs to be deployed.',
353
367
  inputSchema: {
354
- project: z
355
- .string()
356
- .describe(
357
- 'Google Cloud project ID. Do not select it yourself, make sure the user provides or confirms the project ID.'
358
- )
359
- .default(options.defaultProjectId),
360
- region: z
361
- .string()
362
- .optional()
363
- .default(options.defaultRegion)
364
- .describe('Region to deploy the service to'),
365
- service: z
366
- .string()
367
- .optional()
368
- .default(options.defaultServiceName)
369
- .describe('Name of the Cloud Run service to deploy to'),
368
+ project: withDefault(
369
+ z
370
+ .string()
371
+ .describe(
372
+ 'Google Cloud project ID. Do not select it yourself, make sure the user provides or confirms the project ID.'
373
+ ),
374
+ options.defaultProjectId
375
+ ),
376
+ region: withDefault(
377
+ z.string().optional(),
378
+ options.defaultRegion
379
+ ).describe('Region to deploy the service to'),
380
+ service: withDefault(
381
+ z.string().optional(),
382
+ options.defaultServiceName
383
+ ).describe(
384
+ 'Name of the Cloud Run service to deploy to. If not provided, it will be inferred.'
385
+ ),
370
386
  folderPath: z
371
387
  .string()
372
388
  .describe(
@@ -405,13 +421,15 @@ function registerDeployLocalFolderTool(server, options) {
405
421
  files: [folderPath],
406
422
  skipIamCheck: options.skipIamCheck, // Pass the new flag
407
423
  accessToken: options.accessToken,
424
+ ingress: options.ingress,
408
425
  progressCallback,
409
426
  });
427
+ const serviceName = service || response.service;
410
428
  return {
411
429
  content: [
412
430
  {
413
431
  type: 'text',
414
- text: `Cloud Run service ${service} deployed from folder ${folderPath} in project ${project}\nCloud Console: https://console.cloud.google.com/run/detail/${region}/${service}?project=${project}\nService URL: ${response.uri}`,
432
+ text: `Cloud Run service ${serviceName} deployed from folder ${folderPath} in project ${project}\nCloud Console: https://console.cloud.google.com/run/detail/${region}/${serviceName}?project=${project}\nService URL: ${response.uri}`,
415
433
  },
416
434
  ],
417
435
  };
@@ -438,22 +456,22 @@ function registerDeployFileContentsTool(server, options) {
438
456
  description:
439
457
  'Deploy files to Cloud Run by providing their contents directly. Takes an array of file objects containing filename and content. Use this tool if the files only exist in the current chat context.',
440
458
  inputSchema: {
441
- project: z
442
- .string()
443
- .describe(
444
- 'Google Cloud project ID. Leave unset for the app to be deployed in a new project. If provided, make sure the user confirms the project ID they want to deploy to.'
445
- )
446
- .default(options.defaultProjectId),
447
- region: z
448
- .string()
449
- .optional()
450
- .default(options.defaultRegion)
451
- .describe('Region to deploy the service to'),
452
- service: z
453
- .string()
454
- .optional()
455
- .default(options.defaultServiceName)
456
- .describe('Name of the Cloud Run service to deploy to'),
459
+ project: withDefault(
460
+ z
461
+ .string()
462
+ .describe(
463
+ 'Google Cloud project ID. If provided, make sure the user confirms the project ID they want to deploy to.'
464
+ ),
465
+ options.defaultProjectId
466
+ ),
467
+ region: withDefault(
468
+ z.string().optional(),
469
+ options.defaultRegion
470
+ ).describe('Region to deploy the service to'),
471
+ service: withDefault(
472
+ z.string().optional(),
473
+ options.defaultServiceName
474
+ ).describe('Name of the Cloud Run service to deploy to'),
457
475
  files: z
458
476
  .array(
459
477
  z.object({
@@ -507,6 +525,7 @@ function registerDeployFileContentsTool(server, options) {
507
525
  files: files,
508
526
  skipIamCheck: options.skipIamCheck, // Pass the new flag
509
527
  accessToken: options.accessToken,
528
+ ingress: options.ingress,
510
529
  progressCallback,
511
530
  });
512
531
  return {
@@ -540,22 +559,22 @@ function registerDeployContainerImageTool(server, options) {
540
559
  description:
541
560
  'Deploys a container image to Cloud Run. Use this tool if the user provides a container image URL.',
542
561
  inputSchema: {
543
- project: z
544
- .string()
545
- .describe(
546
- 'Google Cloud project ID. Do not select it yourself, make sure the user provides or confirms the project ID.'
547
- )
548
- .default(options.defaultProjectId),
549
- region: z
550
- .string()
551
- .optional()
552
- .default(options.defaultRegion)
553
- .describe('Region to deploy the service to'),
554
- service: z
555
- .string()
556
- .optional()
557
- .default(options.defaultServiceName)
558
- .describe('Name of the Cloud Run service to deploy to'),
562
+ project: withDefault(
563
+ z
564
+ .string()
565
+ .describe(
566
+ 'Google Cloud project ID. Do not select it yourself, make sure the user provides or confirms the project ID.'
567
+ ),
568
+ options.defaultProjectId
569
+ ),
570
+ region: withDefault(
571
+ z.string().optional(),
572
+ options.defaultRegion
573
+ ).describe('Region to deploy the service to'),
574
+ service: withDefault(
575
+ z.string().optional(),
576
+ options.defaultServiceName
577
+ ).describe('Name of the Cloud Run service to deploy to'),
559
578
  imageUrl: z
560
579
  .string()
561
580
  .describe(
@@ -588,6 +607,7 @@ function registerDeployContainerImageTool(server, options) {
588
607
  imageUrl: imageUrl,
589
608
  skipIamCheck: options.skipIamCheck,
590
609
  accessToken: options.accessToken,
610
+ ingress: options.ingress,
591
611
  progressCallback,
592
612
  });
593
613
  return {