@google-cloud/cloud-run-mcp 1.7.0 → 1.9.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/.env.example +13 -0
- package/.env.gcloud-sdk-oauth +13 -0
- package/README.md +38 -0
- package/constants.js +8 -0
- package/lib/clients.js +292 -0
- package/lib/cloud-api/auth.js +1 -0
- package/lib/cloud-api/billing.js +21 -14
- package/lib/cloud-api/build.js +98 -49
- package/lib/cloud-api/helpers.js +10 -17
- package/lib/cloud-api/projects.js +16 -13
- package/lib/cloud-api/registry.js +9 -5
- package/lib/cloud-api/run.js +20 -38
- package/lib/cloud-api/storage.js +7 -7
- package/lib/deployment/deployer.js +33 -61
- package/lib/deployment/helpers.js +1 -1
- package/lib/middleware/oauth.js +92 -0
- package/lib/util/helpers.js +12 -0
- package/mcp-server.js +51 -7
- package/package.json +3 -3
- package/test/local/clients.test.js +176 -0
- package/test/local/cloud-api/build.test.js +293 -309
- package/test/local/mcp-server-stdio.test.js +0 -1
- package/test/local/mcp-server-streamable-http.test.js +69 -0
- package/test/local/mcp-server.test.js +1 -0
- package/test/local/middleware/oauth.test.js +156 -0
- package/test/local/mock-clients.js +35 -0
- package/test/local/tools.test.js +2 -2
- package/test/need-gcp/test-helpers.js +1 -1
- package/tools/register-tools.js +18 -5
- package/.kokoro/kokoro_build.sh +0 -22
- package/.kokoro/presubmit.cfg +0 -21
- package/.kokoro/run_tests.sh +0 -31
package/lib/cloud-api/helpers.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 { getServiceUsageClient } from '../clients.js';
|
|
17
18
|
import { ensureBillingEnabled } from './billing.js';
|
|
18
19
|
|
|
19
20
|
const PREREQUISITE_APIS = [
|
|
@@ -93,7 +94,6 @@ async function checkAndEnableApi(
|
|
|
93
94
|
|
|
94
95
|
/**
|
|
95
96
|
* Enables a single Google Cloud API with retry logic.
|
|
96
|
-
* @param {object} serviceUsageClient - The Service Usage client.
|
|
97
97
|
* @param {string} projectId - The Google Cloud project ID.
|
|
98
98
|
* @param {string} api - The API identifier (e.g., 'run.googleapis.com').
|
|
99
99
|
* @param {function(string, string=): void} progressCallback - A function to call with progress updates.
|
|
@@ -101,11 +101,15 @@ async function checkAndEnableApi(
|
|
|
101
101
|
* @throws {Error} If the API fails to enable or if there's an issue checking its status.
|
|
102
102
|
*/
|
|
103
103
|
export async function enableApiWithRetry(
|
|
104
|
-
serviceUsageClient,
|
|
105
104
|
projectId,
|
|
106
105
|
api,
|
|
106
|
+
accessToken,
|
|
107
107
|
progressCallback
|
|
108
108
|
) {
|
|
109
|
+
const serviceUsageClient = await getServiceUsageClient(
|
|
110
|
+
projectId,
|
|
111
|
+
accessToken
|
|
112
|
+
);
|
|
109
113
|
const serviceName = `projects/${projectId}/services/${api}`;
|
|
110
114
|
try {
|
|
111
115
|
await checkAndEnableApi(
|
|
@@ -145,7 +149,6 @@ export async function enableApiWithRetry(
|
|
|
145
149
|
* Throws an error if an API cannot be enabled.
|
|
146
150
|
*
|
|
147
151
|
* @async
|
|
148
|
-
* @param {object} context - The context object containing clients and other parameters.
|
|
149
152
|
* @param {string} projectId - The Google Cloud project ID.
|
|
150
153
|
* @param {string[]} apis - An array of API identifiers to check and enable (e.g., 'run.googleapis.com').
|
|
151
154
|
* @param {function(string, string=): void} progressCallback - A function to call with progress updates.
|
|
@@ -154,34 +157,24 @@ export async function enableApiWithRetry(
|
|
|
154
157
|
* @returns {Promise<void>} A promise that resolves when all specified APIs are enabled.
|
|
155
158
|
*/
|
|
156
159
|
export async function ensureApisEnabled(
|
|
157
|
-
context,
|
|
158
160
|
projectId,
|
|
159
161
|
apis,
|
|
162
|
+
accessToken,
|
|
160
163
|
progressCallback
|
|
161
164
|
) {
|
|
162
165
|
for (const api of PREREQUISITE_APIS) {
|
|
163
|
-
await enableApiWithRetry(
|
|
164
|
-
context.serviceUsageClient,
|
|
165
|
-
projectId,
|
|
166
|
-
api,
|
|
167
|
-
progressCallback
|
|
168
|
-
);
|
|
166
|
+
await enableApiWithRetry(projectId, api, accessToken, progressCallback);
|
|
169
167
|
}
|
|
170
168
|
|
|
171
169
|
// Before enabling other APIs, ensure billing is enabled.
|
|
172
|
-
await ensureBillingEnabled(
|
|
170
|
+
await ensureBillingEnabled(projectId, accessToken, progressCallback);
|
|
173
171
|
|
|
174
172
|
const message = 'Checking and enabling required APIs...';
|
|
175
173
|
console.log(message);
|
|
176
174
|
if (progressCallback) progressCallback({ level: 'info', data: message });
|
|
177
175
|
|
|
178
176
|
for (const api of apis) {
|
|
179
|
-
await enableApiWithRetry(
|
|
180
|
-
context.serviceUsageClient,
|
|
181
|
-
projectId,
|
|
182
|
-
api,
|
|
183
|
-
progressCallback
|
|
184
|
-
);
|
|
177
|
+
await enableApiWithRetry(projectId, api, accessToken, progressCallback);
|
|
185
178
|
}
|
|
186
179
|
const successMsg = 'All required APIs are enabled.';
|
|
187
180
|
console.log(successMsg);
|
|
@@ -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.
|
|
@@ -25,9 +26,8 @@ import {
|
|
|
25
26
|
* @function listProjects
|
|
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
|
-
export async function listProjects() {
|
|
29
|
-
const
|
|
30
|
-
const client = new ProjectsClient();
|
|
29
|
+
export async function listProjects(accessToken) {
|
|
30
|
+
const client = await getProjectsClient(accessToken);
|
|
31
31
|
try {
|
|
32
32
|
const [projects] = await client.searchProjects();
|
|
33
33
|
return projects.map((project) => ({
|
|
@@ -71,9 +71,8 @@ export function generateProjectId() {
|
|
|
71
71
|
* @param {string} [parent] - Optional. The resource name of the parent under which the project is to be created. e.g., "organizations/123" or "folders/456".
|
|
72
72
|
* @returns {Promise<{projectId: string}|null>} A promise that resolves to an object containing the new project's ID.
|
|
73
73
|
*/
|
|
74
|
-
export async function createProject(projectId, parent) {
|
|
75
|
-
const
|
|
76
|
-
const client = new ProjectsClient();
|
|
74
|
+
export async function createProject(projectId, parent, accessToken) {
|
|
75
|
+
const client = await getProjectsClient(accessToken);
|
|
77
76
|
let projectIdToUse = projectId;
|
|
78
77
|
|
|
79
78
|
if (!projectIdToUse) {
|
|
@@ -113,10 +112,14 @@ export async function createProject(projectId, parent) {
|
|
|
113
112
|
* @param {string} [parent] - Optional. The resource name of the parent under which the project is to be created. e.g., "organizations/123" or "folders/456".
|
|
114
113
|
* @returns {Promise<{projectId: string, billingMessage: string}>} A promise that resolves to an object containing the project ID and a billing status message.
|
|
115
114
|
*/
|
|
116
|
-
export async function createProjectAndAttachBilling(
|
|
115
|
+
export async function createProjectAndAttachBilling(
|
|
116
|
+
projectIdParam,
|
|
117
|
+
parent,
|
|
118
|
+
accessToken
|
|
119
|
+
) {
|
|
117
120
|
let newProject;
|
|
118
121
|
try {
|
|
119
|
-
newProject = await createProject(projectIdParam, parent);
|
|
122
|
+
newProject = await createProject(projectIdParam, parent, accessToken);
|
|
120
123
|
} catch (error) {
|
|
121
124
|
throw new Error(`Failed to create project: ${error.message}`);
|
|
122
125
|
}
|
|
@@ -129,7 +132,7 @@ export async function createProjectAndAttachBilling(projectIdParam, parent) {
|
|
|
129
132
|
let billingMessage = `Project ${projectId} created successfully.`;
|
|
130
133
|
|
|
131
134
|
try {
|
|
132
|
-
const billingAccounts = await listBillingAccounts();
|
|
135
|
+
const billingAccounts = await listBillingAccounts(accessToken);
|
|
133
136
|
if (billingAccounts && billingAccounts.length > 0) {
|
|
134
137
|
const firstBillingAccount = billingAccounts.find((acc) => acc.open); // Prefer an open account
|
|
135
138
|
if (firstBillingAccount) {
|
|
@@ -138,7 +141,8 @@ export async function createProjectAndAttachBilling(projectIdParam, parent) {
|
|
|
138
141
|
);
|
|
139
142
|
const billingInfo = await attachProjectToBillingAccount(
|
|
140
143
|
projectId,
|
|
141
|
-
firstBillingAccount.name
|
|
144
|
+
firstBillingAccount.name,
|
|
145
|
+
accessToken
|
|
142
146
|
);
|
|
143
147
|
if (billingInfo && billingInfo.billingEnabled) {
|
|
144
148
|
billingMessage += ` It has been attached to billing account ${firstBillingAccount.displayName}.`;
|
|
@@ -172,9 +176,8 @@ export async function createProjectAndAttachBilling(projectIdParam, parent) {
|
|
|
172
176
|
* @param {string} projectId - The ID of the project to delete.
|
|
173
177
|
* @returns {Promise<void>} A promise that resolves when the delete operation is initiated.
|
|
174
178
|
*/
|
|
175
|
-
export async function deleteProject(projectId) {
|
|
176
|
-
const
|
|
177
|
-
const client = new ProjectsClient();
|
|
179
|
+
export async function deleteProject(projectId, accessToken) {
|
|
180
|
+
const client = await getProjectsClient(accessToken);
|
|
178
181
|
try {
|
|
179
182
|
console.log(`Attempting to delete project with ID: ${projectId}`);
|
|
180
183
|
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,19 @@ 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,
|
|
36
|
+
accessToken,
|
|
37
37
|
location,
|
|
38
38
|
repositoryId,
|
|
39
39
|
format = 'DOCKER',
|
|
40
40
|
progressCallback
|
|
41
41
|
) {
|
|
42
|
+
const artifactRegistryClient = await getArtifactRegistryClient(
|
|
43
|
+
projectId,
|
|
44
|
+
accessToken
|
|
45
|
+
);
|
|
42
46
|
const parent = `projects/${projectId}/locations/${location}`;
|
|
43
|
-
const repoPath =
|
|
47
|
+
const repoPath = artifactRegistryClient.repositoryPath(
|
|
44
48
|
projectId,
|
|
45
49
|
location,
|
|
46
50
|
repositoryId
|
|
@@ -48,7 +52,7 @@ export async function ensureArtifactRegistryRepoExists(
|
|
|
48
52
|
|
|
49
53
|
try {
|
|
50
54
|
const [repository] = await callWithRetry(
|
|
51
|
-
() =>
|
|
55
|
+
() => artifactRegistryClient.getRepository({ name: repoPath }),
|
|
52
56
|
`artifactRegistry.getRepository ${repositoryId}`
|
|
53
57
|
);
|
|
54
58
|
await logAndProgress(
|
|
@@ -68,7 +72,7 @@ export async function ensureArtifactRegistryRepoExists(
|
|
|
68
72
|
|
|
69
73
|
const [operation] = await callWithRetry(
|
|
70
74
|
() =>
|
|
71
|
-
|
|
75
|
+
artifactRegistryClient.createRepository({
|
|
72
76
|
parent: parent,
|
|
73
77
|
repository: repositoryToCreate,
|
|
74
78
|
repositoryId: repositoryId,
|
package/lib/cloud-api/run.js
CHANGED
|
@@ -15,20 +15,22 @@ 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
|
-
|
|
22
|
-
let serviceUsageClient;
|
|
23
|
-
let loggingClient;
|
|
24
|
-
|
|
25
|
-
async function listCloudRunLocations(projectId) {
|
|
26
|
+
async function listCloudRunLocations(projectId, accessToken) {
|
|
26
27
|
const listLocationsRequest = {
|
|
27
28
|
name: `projects/${projectId}`,
|
|
28
29
|
};
|
|
29
30
|
|
|
30
31
|
const availableLocations = [];
|
|
31
32
|
try {
|
|
33
|
+
const runClient = await getRunClient(projectId, accessToken);
|
|
32
34
|
console.log('Listing Cloud Run supported locations:');
|
|
33
35
|
const iterable = runClient.listLocationsAsync(listLocationsRequest);
|
|
34
36
|
for await (const location of iterable) {
|
|
@@ -49,20 +51,11 @@ async function listCloudRunLocations(projectId) {
|
|
|
49
51
|
* @param {string} projectId - The Google Cloud project ID.
|
|
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
|
-
export async function listServices(projectId) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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']);
|
|
65
|
-
const locations = await listCloudRunLocations(projectId);
|
|
54
|
+
export async function listServices(projectId, accessToken) {
|
|
55
|
+
const runClient = await getRunClient(projectId, accessToken);
|
|
56
|
+
|
|
57
|
+
await ensureApisEnabled(projectId, ['run.googleapis.com'], accessToken);
|
|
58
|
+
const locations = await listCloudRunLocations(projectId, accessToken);
|
|
66
59
|
|
|
67
60
|
const allServices = {};
|
|
68
61
|
for (const location of locations) {
|
|
@@ -92,12 +85,8 @@ export async function listServices(projectId) {
|
|
|
92
85
|
* @param {string} serviceId - The ID of the Cloud Run service.
|
|
93
86
|
* @returns {Promise<object>} - A promise that resolves to the service object.
|
|
94
87
|
*/
|
|
95
|
-
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
|
-
}
|
|
88
|
+
export async function getService(projectId, location, serviceId, accessToken) {
|
|
89
|
+
const runClient = await getRunClient(projectId, accessToken);
|
|
101
90
|
|
|
102
91
|
const servicePath = runClient.servicePath(projectId, location, serviceId);
|
|
103
92
|
|
|
@@ -136,13 +125,10 @@ export async function getServiceLogs(
|
|
|
136
125
|
projectId,
|
|
137
126
|
location,
|
|
138
127
|
serviceId,
|
|
128
|
+
accessToken,
|
|
139
129
|
requestOptions
|
|
140
130
|
) {
|
|
141
|
-
|
|
142
|
-
const { Logging } = await import('@google-cloud/logging');
|
|
143
|
-
loggingClient = new Logging({ projectId });
|
|
144
|
-
}
|
|
145
|
-
|
|
131
|
+
const loggingClient = await getLoggingClient(projectId, accessToken);
|
|
146
132
|
try {
|
|
147
133
|
const LOG_SEVERITY = 'DEFAULT'; // e.g., 'DEFAULT', 'INFO', 'WARNING', 'ERROR'
|
|
148
134
|
const PAGE_SIZE = 100; // Number of log entries to retrieve per page
|
|
@@ -237,21 +223,17 @@ 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,
|
|
229
|
+
accessToken,
|
|
244
230
|
progressCallback
|
|
245
231
|
) {
|
|
246
|
-
const
|
|
247
|
-
const servicePath =
|
|
248
|
-
projectId,
|
|
249
|
-
location,
|
|
250
|
-
serviceId
|
|
251
|
-
);
|
|
232
|
+
const runClient = await getRunClient(projectId, accessToken);
|
|
233
|
+
const servicePath = runClient.servicePath(projectId, location, serviceId);
|
|
252
234
|
try {
|
|
253
235
|
await callWithRetry(
|
|
254
|
-
() =>
|
|
236
|
+
() => runClient.getService({ name: servicePath }),
|
|
255
237
|
`getService ${serviceId}`
|
|
256
238
|
);
|
|
257
239
|
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,14 @@ 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',
|
|
37
|
+
accessToken,
|
|
36
38
|
progressCallback
|
|
37
39
|
) {
|
|
38
|
-
const
|
|
40
|
+
const storage = await getStorageClient(projectId, accessToken);
|
|
41
|
+
const bucket = storage.bucket(bucketName);
|
|
39
42
|
try {
|
|
40
43
|
const [exists] = await callWithRetry(
|
|
41
44
|
() => bucket.exists(),
|
|
@@ -54,8 +57,7 @@ export async function ensureStorageBucketExists(
|
|
|
54
57
|
);
|
|
55
58
|
try {
|
|
56
59
|
const [createdBucket] = await callWithRetry(
|
|
57
|
-
() =>
|
|
58
|
-
context.storage.createBucket(bucketName, { location: location }),
|
|
60
|
+
() => storage.createBucket(bucketName, { location: location }),
|
|
59
61
|
`storage.createBucket ${bucketName}`
|
|
60
62
|
);
|
|
61
63
|
await logAndProgress(
|
|
@@ -85,7 +87,6 @@ export async function ensureStorageBucketExists(
|
|
|
85
87
|
* Uploads a buffer to a specified Google Cloud Storage bucket and blob name.
|
|
86
88
|
*
|
|
87
89
|
* @async
|
|
88
|
-
* @param {object} context - The context object containing clients and other parameters.
|
|
89
90
|
* @param {import('@google-cloud/storage').Bucket} bucket - The Google Cloud Storage bucket object.
|
|
90
91
|
* @param {Buffer} buffer - The buffer containing the data to upload.
|
|
91
92
|
* @param {string} destinationBlobName - The name for the blob in the bucket.
|
|
@@ -94,7 +95,6 @@ export async function ensureStorageBucketExists(
|
|
|
94
95
|
* @throws {Error} If the upload fails.
|
|
95
96
|
*/
|
|
96
97
|
export async function uploadToStorageBucket(
|
|
97
|
-
context,
|
|
98
98
|
bucket,
|
|
99
99
|
buffer,
|
|
100
100
|
destinationBlobName,
|