@google-cloud/cloud-run-mcp 1.6.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +1 -1
- package/CONTRIBUTING.md +1 -1
- package/lib/clients.js +222 -0
- package/lib/cloud-api/billing.js +90 -4
- package/lib/cloud-api/build.js +96 -49
- package/lib/cloud-api/helpers.js +60 -38
- package/lib/cloud-api/projects.js +4 -6
- package/lib/cloud-api/registry.js +5 -5
- package/lib/cloud-api/run.js +14 -33
- package/lib/cloud-api/storage.js +6 -7
- package/lib/deployment/constants.js +54 -0
- package/lib/deployment/deployer.js +251 -149
- package/lib/deployment/helpers.js +194 -0
- package/lib/deployment/source-processor.js +124 -0
- package/lib/util/archive.js +15 -6
- package/package.json +3 -3
- package/test/local/clients.test.js +146 -0
- package/test/local/cloud-api/build.test.js +293 -309
- package/test/local/deployment-helpers.test.js +231 -0
- package/test/local/mcp-server-stdio.test.js +59 -0
- package/test/local/mcp-server-streamable-http.test.js +25 -21
- package/test/local/mock-clients.js +35 -0
- package/test/local/source-processor.test.js +290 -0
- package/test/local/test-utils.js +51 -0
- package/test/need-gcp/test-helpers.js +1 -1
- package/test/need-gcp/workflows/deployment-workflows.test.js +3 -3
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
listBillingAccounts,
|
|
19
19
|
attachProjectToBillingAccount,
|
|
20
20
|
} from './billing.js';
|
|
21
|
+
import { getProjectsClient } from '../clients.js';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Lists all accessible Google Cloud Platform projects.
|
|
@@ -26,8 +27,7 @@ import {
|
|
|
26
27
|
* @returns {Promise<Array<{id: string}>>} A promise that resolves to an array of project objects, each with an 'id' property. Returns an empty array on error.
|
|
27
28
|
*/
|
|
28
29
|
export async function listProjects() {
|
|
29
|
-
const
|
|
30
|
-
const client = new ProjectsClient();
|
|
30
|
+
const client = await getProjectsClient();
|
|
31
31
|
try {
|
|
32
32
|
const [projects] = await client.searchProjects();
|
|
33
33
|
return projects.map((project) => ({
|
|
@@ -72,8 +72,7 @@ export function generateProjectId() {
|
|
|
72
72
|
* @returns {Promise<{projectId: string}|null>} A promise that resolves to an object containing the new project's ID.
|
|
73
73
|
*/
|
|
74
74
|
export async function createProject(projectId, parent) {
|
|
75
|
-
const
|
|
76
|
-
const client = new ProjectsClient();
|
|
75
|
+
const client = await getProjectsClient();
|
|
77
76
|
let projectIdToUse = projectId;
|
|
78
77
|
|
|
79
78
|
if (!projectIdToUse) {
|
|
@@ -173,8 +172,7 @@ export async function createProjectAndAttachBilling(projectIdParam, parent) {
|
|
|
173
172
|
* @returns {Promise<void>} A promise that resolves when the delete operation is initiated.
|
|
174
173
|
*/
|
|
175
174
|
export async function deleteProject(projectId) {
|
|
176
|
-
const
|
|
177
|
-
const client = new ProjectsClient();
|
|
175
|
+
const client = await getProjectsClient();
|
|
178
176
|
try {
|
|
179
177
|
console.log(`Attempting to delete project with ID: ${projectId}`);
|
|
180
178
|
await client.deleteProject({ name: `projects/${projectId}` });
|
|
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { getArtifactRegistryClient } from '../clients.js';
|
|
17
18
|
import { callWithRetry } from './helpers.js';
|
|
18
19
|
import { logAndProgress } from '../util/helpers.js';
|
|
19
20
|
|
|
@@ -22,7 +23,6 @@ import { logAndProgress } from '../util/helpers.js';
|
|
|
22
23
|
* If the repository does not exist, it attempts to create it with the specified format.
|
|
23
24
|
*
|
|
24
25
|
* @async
|
|
25
|
-
* @param {object} context - The context object containing clients and other parameters.
|
|
26
26
|
* @param {string} projectId - The Google Cloud project ID.
|
|
27
27
|
* @param {string} location - The Google Cloud region for the repository.
|
|
28
28
|
* @param {string} repositoryId - The ID for the Artifact Registry repository.
|
|
@@ -32,15 +32,15 @@ import { logAndProgress } from '../util/helpers.js';
|
|
|
32
32
|
* @throws {Error} If there's an error checking or creating the repository.
|
|
33
33
|
*/
|
|
34
34
|
export async function ensureArtifactRegistryRepoExists(
|
|
35
|
-
context,
|
|
36
35
|
projectId,
|
|
37
36
|
location,
|
|
38
37
|
repositoryId,
|
|
39
38
|
format = 'DOCKER',
|
|
40
39
|
progressCallback
|
|
41
40
|
) {
|
|
41
|
+
const artifactRegistryClient = await getArtifactRegistryClient(projectId);
|
|
42
42
|
const parent = `projects/${projectId}/locations/${location}`;
|
|
43
|
-
const repoPath =
|
|
43
|
+
const repoPath = artifactRegistryClient.repositoryPath(
|
|
44
44
|
projectId,
|
|
45
45
|
location,
|
|
46
46
|
repositoryId
|
|
@@ -48,7 +48,7 @@ export async function ensureArtifactRegistryRepoExists(
|
|
|
48
48
|
|
|
49
49
|
try {
|
|
50
50
|
const [repository] = await callWithRetry(
|
|
51
|
-
() =>
|
|
51
|
+
() => artifactRegistryClient.getRepository({ name: repoPath }),
|
|
52
52
|
`artifactRegistry.getRepository ${repositoryId}`
|
|
53
53
|
);
|
|
54
54
|
await logAndProgress(
|
|
@@ -68,7 +68,7 @@ export async function ensureArtifactRegistryRepoExists(
|
|
|
68
68
|
|
|
69
69
|
const [operation] = await callWithRetry(
|
|
70
70
|
() =>
|
|
71
|
-
|
|
71
|
+
artifactRegistryClient.createRepository({
|
|
72
72
|
parent: parent,
|
|
73
73
|
repository: repositoryToCreate,
|
|
74
74
|
repositoryId: repositoryId,
|
package/lib/cloud-api/run.js
CHANGED
|
@@ -15,13 +15,14 @@ limitations under the License.
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
import protofiles from 'google-proto-files';
|
|
18
|
+
import {
|
|
19
|
+
getRunClient,
|
|
20
|
+
getServiceUsageClient,
|
|
21
|
+
getLoggingClient,
|
|
22
|
+
} from '../clients.js';
|
|
18
23
|
import { callWithRetry, ensureApisEnabled } from './helpers.js';
|
|
19
24
|
import { logAndProgress } from '../util/helpers.js';
|
|
20
25
|
|
|
21
|
-
let runClient;
|
|
22
|
-
let serviceUsageClient;
|
|
23
|
-
let loggingClient;
|
|
24
|
-
|
|
25
26
|
async function listCloudRunLocations(projectId) {
|
|
26
27
|
const listLocationsRequest = {
|
|
27
28
|
name: `projects/${projectId}`,
|
|
@@ -29,6 +30,7 @@ async function listCloudRunLocations(projectId) {
|
|
|
29
30
|
|
|
30
31
|
const availableLocations = [];
|
|
31
32
|
try {
|
|
33
|
+
const runClient = await getRunClient(projectId);
|
|
32
34
|
console.log('Listing Cloud Run supported locations:');
|
|
33
35
|
const iterable = runClient.listLocationsAsync(listLocationsRequest);
|
|
34
36
|
for await (const location of iterable) {
|
|
@@ -50,18 +52,9 @@ async function listCloudRunLocations(projectId) {
|
|
|
50
52
|
* @returns {Promise<object>} - A promise that resolves to an object mapping region to list of service objects in that region.
|
|
51
53
|
*/
|
|
52
54
|
export async function listServices(projectId) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const { ServiceUsageClient } = await import('@google-cloud/service-usage');
|
|
57
|
-
runClient = new ServicesClient({ projectId });
|
|
58
|
-
serviceUsageClient = new ServiceUsageClient({ projectId });
|
|
59
|
-
}
|
|
60
|
-
const context = {
|
|
61
|
-
runClient: runClient,
|
|
62
|
-
serviceUsageClient: serviceUsageClient,
|
|
63
|
-
};
|
|
64
|
-
await ensureApisEnabled(context, projectId, ['run.googleapis.com']);
|
|
55
|
+
const runClient = await getRunClient(projectId);
|
|
56
|
+
|
|
57
|
+
await ensureApisEnabled(projectId, ['run.googleapis.com']);
|
|
65
58
|
const locations = await listCloudRunLocations(projectId);
|
|
66
59
|
|
|
67
60
|
const allServices = {};
|
|
@@ -93,11 +86,7 @@ export async function listServices(projectId) {
|
|
|
93
86
|
* @returns {Promise<object>} - A promise that resolves to the service object.
|
|
94
87
|
*/
|
|
95
88
|
export async function getService(projectId, location, serviceId) {
|
|
96
|
-
|
|
97
|
-
const { v2 } = await import('@google-cloud/run');
|
|
98
|
-
const { ServicesClient } = v2;
|
|
99
|
-
runClient = new ServicesClient({ projectId });
|
|
100
|
-
}
|
|
89
|
+
const runClient = await getRunClient(projectId);
|
|
101
90
|
|
|
102
91
|
const servicePath = runClient.servicePath(projectId, location, serviceId);
|
|
103
92
|
|
|
@@ -138,10 +127,7 @@ export async function getServiceLogs(
|
|
|
138
127
|
serviceId,
|
|
139
128
|
requestOptions
|
|
140
129
|
) {
|
|
141
|
-
|
|
142
|
-
const { Logging } = await import('@google-cloud/logging');
|
|
143
|
-
loggingClient = new Logging({ projectId });
|
|
144
|
-
}
|
|
130
|
+
const loggingClient = await getLoggingClient(projectId);
|
|
145
131
|
|
|
146
132
|
try {
|
|
147
133
|
const LOG_SEVERITY = 'DEFAULT'; // e.g., 'DEFAULT', 'INFO', 'WARNING', 'ERROR'
|
|
@@ -237,21 +223,16 @@ function formatLogEntry(entry) {
|
|
|
237
223
|
* @throws {Error} If there's an error checking the service (other than not found).
|
|
238
224
|
*/
|
|
239
225
|
export async function checkCloudRunServiceExists(
|
|
240
|
-
context,
|
|
241
226
|
projectId,
|
|
242
227
|
location,
|
|
243
228
|
serviceId,
|
|
244
229
|
progressCallback
|
|
245
230
|
) {
|
|
246
|
-
const
|
|
247
|
-
const servicePath =
|
|
248
|
-
projectId,
|
|
249
|
-
location,
|
|
250
|
-
serviceId
|
|
251
|
-
);
|
|
231
|
+
const runClient = await getRunClient(projectId);
|
|
232
|
+
const servicePath = runClient.servicePath(projectId, location, serviceId);
|
|
252
233
|
try {
|
|
253
234
|
await callWithRetry(
|
|
254
|
-
() =>
|
|
235
|
+
() => runClient.getService({ name: servicePath }),
|
|
255
236
|
`getService ${serviceId}`
|
|
256
237
|
);
|
|
257
238
|
await logAndProgress(
|
package/lib/cloud-api/storage.js
CHANGED
|
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
import { getStorageClient } from '../clients.js';
|
|
17
18
|
import { callWithRetry } from './helpers.js';
|
|
18
19
|
import { logAndProgress } from '../util/helpers.js';
|
|
19
20
|
|
|
@@ -22,7 +23,7 @@ import { logAndProgress } from '../util/helpers.js';
|
|
|
22
23
|
* If the bucket does not exist, it attempts to create it in the specified location.
|
|
23
24
|
*
|
|
24
25
|
* @async
|
|
25
|
-
* @param {
|
|
26
|
+
* @param {string} projectId - The Google Cloud project ID.
|
|
26
27
|
* @param {string} bucketName - The name of the storage bucket.
|
|
27
28
|
* @param {string} [location='us'] - The location to create the bucket in if it doesn't exist. Defaults to 'us'.
|
|
28
29
|
* @param {function(object): void} [progressCallback] - Optional callback for progress updates.
|
|
@@ -30,12 +31,13 @@ import { logAndProgress } from '../util/helpers.js';
|
|
|
30
31
|
* @throws {Error} If there's an error checking or creating the bucket.
|
|
31
32
|
*/
|
|
32
33
|
export async function ensureStorageBucketExists(
|
|
33
|
-
|
|
34
|
+
projectId,
|
|
34
35
|
bucketName,
|
|
35
36
|
location = 'us',
|
|
36
37
|
progressCallback
|
|
37
38
|
) {
|
|
38
|
-
const
|
|
39
|
+
const storage = await getStorageClient(projectId);
|
|
40
|
+
const bucket = storage.bucket(bucketName);
|
|
39
41
|
try {
|
|
40
42
|
const [exists] = await callWithRetry(
|
|
41
43
|
() => bucket.exists(),
|
|
@@ -54,8 +56,7 @@ export async function ensureStorageBucketExists(
|
|
|
54
56
|
);
|
|
55
57
|
try {
|
|
56
58
|
const [createdBucket] = await callWithRetry(
|
|
57
|
-
() =>
|
|
58
|
-
context.storage.createBucket(bucketName, { location: location }),
|
|
59
|
+
() => storage.createBucket(bucketName, { location: location }),
|
|
59
60
|
`storage.createBucket ${bucketName}`
|
|
60
61
|
);
|
|
61
62
|
await logAndProgress(
|
|
@@ -85,7 +86,6 @@ export async function ensureStorageBucketExists(
|
|
|
85
86
|
* Uploads a buffer to a specified Google Cloud Storage bucket and blob name.
|
|
86
87
|
*
|
|
87
88
|
* @async
|
|
88
|
-
* @param {object} context - The context object containing clients and other parameters.
|
|
89
89
|
* @param {import('@google-cloud/storage').Bucket} bucket - The Google Cloud Storage bucket object.
|
|
90
90
|
* @param {Buffer} buffer - The buffer containing the data to upload.
|
|
91
91
|
* @param {string} destinationBlobName - The name for the blob in the bucket.
|
|
@@ -94,7 +94,6 @@ export async function ensureStorageBucketExists(
|
|
|
94
94
|
* @throws {Error} If the upload fails.
|
|
95
95
|
*/
|
|
96
96
|
export async function uploadToStorageBucket(
|
|
97
|
-
context,
|
|
98
97
|
bucket,
|
|
99
98
|
buffer,
|
|
100
99
|
destinationBlobName,
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 Google LLC
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
https://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export const DEPLOYMENT_CONFIG = {
|
|
18
|
+
REPO_NAME: 'mcp-cloud-run-deployments',
|
|
19
|
+
ZIP_FILE_NAME: 'source.zip',
|
|
20
|
+
TARGZ_FILE_NAME: 'source.tar.gz',
|
|
21
|
+
IMAGE_TAG: 'latest',
|
|
22
|
+
LABEL_CREATED_BY: 'cloud-run-mcp',
|
|
23
|
+
DEFAULT_NODE_BASE_IMAGE: 'nodejs22',
|
|
24
|
+
NO_BUILD_IMAGE_TYPE: 'scratch',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const TEMP_PATHS = {
|
|
28
|
+
BASE: '.cloud-run-mcp',
|
|
29
|
+
SUBDIR: 'source',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const REQUIRED_APIS = {
|
|
33
|
+
SOURCE_DEPLOY: [
|
|
34
|
+
'serviceusage.googleapis.com',
|
|
35
|
+
'iam.googleapis.com',
|
|
36
|
+
'storage.googleapis.com',
|
|
37
|
+
'cloudbuild.googleapis.com',
|
|
38
|
+
'artifactregistry.googleapis.com',
|
|
39
|
+
'run.googleapis.com',
|
|
40
|
+
],
|
|
41
|
+
IMAGE_DEPLOY: ['serviceusage.googleapis.com', 'run.googleapis.com'],
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const DEPLOYMENT_TYPES = {
|
|
45
|
+
NO_BUILD: 'no-build',
|
|
46
|
+
IMAGE: 'image',
|
|
47
|
+
WITH_BUILD: 'with-build',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const RUNTIMES = {
|
|
51
|
+
NODEJS: 'nodejs',
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const MAX_ALLOWED_DIRECT_SOURCE_SIZE_BYTES = 250 * 1024 * 1024; // 250 MiB
|