@jieunmarslim/server-editable-slides 0.2.1 → 0.2.3
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/index.js +160 -16
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -71,43 +71,125 @@ async function runDoctor() {
|
|
|
71
71
|
|
|
72
72
|
// 1. Check Auth
|
|
73
73
|
if (account && token) {
|
|
74
|
-
console.error(` [1/
|
|
74
|
+
console.error(` [1/4] Google Cloud Authentication:`);
|
|
75
75
|
console.error(` ✓ Authenticated: ${account}`);
|
|
76
76
|
} else {
|
|
77
77
|
hasError = true;
|
|
78
|
-
console.error(` [1/
|
|
78
|
+
console.error(` [1/4] Google Cloud Authentication:`);
|
|
79
79
|
console.error(` ✗ FAILED: Not authenticated or token expired.`);
|
|
80
80
|
console.error(` 👉 Please run: gcloud auth login`);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
// 2. Check Project
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
console.error(` ✓ Target Project ID: ${project}`);
|
|
87
|
-
} else {
|
|
83
|
+
// 2. Check Project Configuration & Account Access Permissions
|
|
84
|
+
let projectAccessOk = false;
|
|
85
|
+
if (!project) {
|
|
88
86
|
hasError = true;
|
|
89
|
-
console.error(`\n [2/
|
|
90
|
-
console.error(` ✗ FAILED: No GCP project configured
|
|
87
|
+
console.error(`\n [2/4] GEAP Billing & Quota Project:`);
|
|
88
|
+
console.error(` ✗ FAILED: No GCP project configured.`);
|
|
91
89
|
console.error(` 👉 Please run: gcloud config set project YOUR_GCP_PROJECT_ID`);
|
|
92
90
|
console.error(` 👉 Or export: export EDITABLE_SLIDES_PROJECT=YOUR_GCP_PROJECT_ID`);
|
|
91
|
+
} else {
|
|
92
|
+
try {
|
|
93
|
+
// Actually verify that the logged-in account has access to this project
|
|
94
|
+
const check = execSync(
|
|
95
|
+
`gcloud projects describe ${project} --format="value(projectId)"`,
|
|
96
|
+
{ stdio: ['ignore', 'pipe', 'pipe'] },
|
|
97
|
+
)
|
|
98
|
+
.toString()
|
|
99
|
+
.trim();
|
|
100
|
+
if (check === project) {
|
|
101
|
+
projectAccessOk = true;
|
|
102
|
+
console.error(`\n [2/4] Target GCP Project Accessibility:`);
|
|
103
|
+
console.error(
|
|
104
|
+
` ✓ Verified: Account [${account}] has active access to project [${project}].`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
hasError = true;
|
|
109
|
+
const stderr = err.stderr ? err.stderr.toString() : err.message;
|
|
110
|
+
console.error(`\n [2/4] Target GCP Project Accessibility:`);
|
|
111
|
+
if (
|
|
112
|
+
stderr.includes('does not have permission') ||
|
|
113
|
+
stderr.includes('caller does not have permission')
|
|
114
|
+
) {
|
|
115
|
+
console.error(
|
|
116
|
+
` ✗ ACCESS DENIED: Account [${account}] does NOT have permission to access project [${project}].`,
|
|
117
|
+
);
|
|
118
|
+
console.error(
|
|
119
|
+
` 👉 Solution 1: Switch to an account that has access to [${project}]:`,
|
|
120
|
+
);
|
|
121
|
+
console.error(` gcloud config set account <ACCOUNT_WITH_ACCESS>`);
|
|
122
|
+
console.error(
|
|
123
|
+
` 👉 Solution 2: Switch to a project that [${account}] can access:`,
|
|
124
|
+
);
|
|
125
|
+
console.error(
|
|
126
|
+
` gcloud config set project <ACCESSIBLE_PROJECT_ID>`,
|
|
127
|
+
);
|
|
128
|
+
} else if (stderr.includes('not found') || stderr.includes('404')) {
|
|
129
|
+
console.error(
|
|
130
|
+
` ✗ NOT FOUND: Project [${project}] does not exist or was deleted.`,
|
|
131
|
+
);
|
|
132
|
+
console.error(
|
|
133
|
+
` 👉 Please run: gcloud config set project <VALID_PROJECT_ID>`,
|
|
134
|
+
);
|
|
135
|
+
} else {
|
|
136
|
+
console.error(
|
|
137
|
+
` ✗ FAILED: Unable to verify project access (${stderr.trim()}).`,
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// 3. Verify One-Time IAM Delegation to EditNBLM Service Account
|
|
144
|
+
if (projectAccessOk) {
|
|
145
|
+
try {
|
|
146
|
+
const bindings = execSync(
|
|
147
|
+
`gcloud projects get-iam-policy ${project} --flatten="bindings[].members" --filter="bindings.role:roles/aiplatform.user" --format="value(bindings.members)"`,
|
|
148
|
+
{ stdio: ['ignore', 'pipe', 'ignore'] },
|
|
149
|
+
).toString();
|
|
150
|
+
const hasAiPlatform = bindings.includes(
|
|
151
|
+
'serviceAccount:editnblm-mcp-runtime@editnblm-in-ge-2036.iam.gserviceaccount.com',
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
console.error(`\n [3/4] GEAP Quota & IAM Delegation:`);
|
|
155
|
+
if (hasAiPlatform) {
|
|
156
|
+
console.error(
|
|
157
|
+
` ✓ GRANTED: Service account has roles/aiplatform.user on [${project}].`,
|
|
158
|
+
);
|
|
159
|
+
} else {
|
|
160
|
+
hasError = true;
|
|
161
|
+
console.error(
|
|
162
|
+
` ✗ MISSING: editnblm-mcp-runtime has NOT been granted roles/aiplatform.user on [${project}].`,
|
|
163
|
+
);
|
|
164
|
+
console.error(` 👉 Please run the delegation commands below.`);
|
|
165
|
+
}
|
|
166
|
+
} catch {
|
|
167
|
+
console.error(`\n [3/4] GEAP Quota & IAM Delegation:`);
|
|
168
|
+
console.error(
|
|
169
|
+
` ⚠️ Could not read IAM policy directly. Ensure the delegation commands below were executed.`,
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
console.error(`\n [3/4] GEAP Quota & IAM Delegation:`);
|
|
174
|
+
console.error(` ⏸️ Skipped until project access is resolved.`);
|
|
93
175
|
}
|
|
94
176
|
|
|
95
|
-
//
|
|
177
|
+
// 4. Check Backend Connectivity
|
|
96
178
|
try {
|
|
97
179
|
const res = await fetch(`${BASE_URL}/health`);
|
|
98
180
|
if (res.ok) {
|
|
99
|
-
console.error(`\n [
|
|
181
|
+
console.error(`\n [4/4] EditNBLM Cloud Backend:`);
|
|
100
182
|
console.error(` ✓ Status: Connected (${BASE_URL})`);
|
|
101
183
|
} else {
|
|
102
|
-
console.error(`\n [
|
|
184
|
+
console.error(`\n [4/4] EditNBLM Cloud Backend:`);
|
|
103
185
|
console.error(` ⚠️ Status: HTTP ${res.status}`);
|
|
104
186
|
}
|
|
105
187
|
} catch (err) {
|
|
106
|
-
console.error(`\n [
|
|
188
|
+
console.error(`\n [4/4] EditNBLM Cloud Backend:`);
|
|
107
189
|
console.error(` ✗ Status: Unreachable (${err.message})`);
|
|
108
190
|
}
|
|
109
191
|
|
|
110
|
-
//
|
|
192
|
+
// 5. One-Time IAM Delegation command helper
|
|
111
193
|
console.error('\n------------------------------------------------------------------------');
|
|
112
194
|
console.error(' 📋 One-Time IAM Delegation Commands for your Project:');
|
|
113
195
|
console.error('------------------------------------------------------------------------');
|
|
@@ -121,7 +203,7 @@ async function runDoctor() {
|
|
|
121
203
|
console.error('========================================================================\n');
|
|
122
204
|
|
|
123
205
|
if (!hasError) {
|
|
124
|
-
console.error('🎉
|
|
206
|
+
console.error('🎉 All checks PASSED! Your environment is ready to use EditNBLM MCP.\n');
|
|
125
207
|
process.exit(0);
|
|
126
208
|
} else {
|
|
127
209
|
console.error('⚠️ Setup incomplete. Please fix the items marked with ✗ above.\n');
|
|
@@ -189,7 +271,18 @@ async function callRemoteMcp(method, params, apiKey) {
|
|
|
189
271
|
const data = JSON.parse(jsonStr);
|
|
190
272
|
|
|
191
273
|
if (data.error) {
|
|
192
|
-
|
|
274
|
+
const msg = data.error.message || JSON.stringify(data.error);
|
|
275
|
+
if (
|
|
276
|
+
msg.includes('aiplatform.endpoints.predict') ||
|
|
277
|
+
(msg.includes('Permission') && msg.includes('denied'))
|
|
278
|
+
) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`[EditNBLM IAM Delegation Required] Project (${project}) has not delegated roles/aiplatform.user to the EditNBLM service account.\n` +
|
|
281
|
+
`Run in terminal:\n` +
|
|
282
|
+
`gcloud projects add-iam-policy-binding ${project} --member="serviceAccount:editnblm-mcp-runtime@editnblm-in-ge-2036.iam.gserviceaccount.com" --role="roles/aiplatform.user"`,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
throw new Error(msg);
|
|
193
286
|
}
|
|
194
287
|
return data.result;
|
|
195
288
|
}
|
|
@@ -251,7 +344,58 @@ async function downloadPptxHelper(downloadUrl, targetPath) {
|
|
|
251
344
|
return null;
|
|
252
345
|
}
|
|
253
346
|
|
|
347
|
+
function assertEnvironmentOrExit() {
|
|
348
|
+
const account = getAccount();
|
|
349
|
+
const token = getAuthToken();
|
|
350
|
+
const project = getProject();
|
|
351
|
+
|
|
352
|
+
if (!account || !token) {
|
|
353
|
+
console.error('\n[EditNBLM] ❌ Activation Failed: Google Cloud authentication required.');
|
|
354
|
+
console.error('[EditNBLM] Please run `gcloud auth login` before activating this MCP server.');
|
|
355
|
+
console.error('[EditNBLM] Run `npx -y @jieunmarslim/server-editable-slides doctor` to diagnose.\n');
|
|
356
|
+
process.exit(1);
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (!project) {
|
|
360
|
+
console.error('\n[EditNBLM] ❌ Activation Failed: No GCP Project configured for GEAP billing.');
|
|
361
|
+
console.error('[EditNBLM] Please run `gcloud config set project <PROJECT_ID>` or export EDITABLE_SLIDES_PROJECT.');
|
|
362
|
+
console.error('[EditNBLM] Run `npx -y @jieunmarslim/server-editable-slides doctor` to diagnose.\n');
|
|
363
|
+
process.exit(1);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Verify that account actually has permission to access the project
|
|
367
|
+
try {
|
|
368
|
+
const check = execSync(
|
|
369
|
+
`gcloud projects describe ${project} --format="value(projectId)"`,
|
|
370
|
+
{ stdio: ['ignore', 'pipe', 'pipe'] },
|
|
371
|
+
)
|
|
372
|
+
.toString()
|
|
373
|
+
.trim();
|
|
374
|
+
if (check !== project) {
|
|
375
|
+
throw new Error(`Project mismatch`);
|
|
376
|
+
}
|
|
377
|
+
} catch (err) {
|
|
378
|
+
const stderr = err.stderr ? err.stderr.toString() : err.message;
|
|
379
|
+
console.error(`\n[EditNBLM] ❌ Activation Failed: Account [${account}] does NOT have access to project [${project}].`);
|
|
380
|
+
if (
|
|
381
|
+
stderr.includes('does not have permission') ||
|
|
382
|
+
stderr.includes('caller does not have permission')
|
|
383
|
+
) {
|
|
384
|
+
console.error(`[EditNBLM] 👉 Solution 1: Switch to an account with access: gcloud config set account <ACCOUNT>`);
|
|
385
|
+
console.error(`[EditNBLM] 👉 Solution 2: Switch to a project that [${account}] can access: gcloud config set project <PROJECT>`);
|
|
386
|
+
} else if (stderr.includes('not found') || stderr.includes('404')) {
|
|
387
|
+
console.error(`[EditNBLM] 👉 Project [${project}] does not exist. Run: gcloud config set project <VALID_PROJECT_ID>`);
|
|
388
|
+
} else {
|
|
389
|
+
console.error(`[EditNBLM] 👉 Details: ${stderr.trim()}`);
|
|
390
|
+
}
|
|
391
|
+
console.error('[EditNBLM] Run `npx -y @jieunmarslim/server-editable-slides doctor` to diagnose.\n');
|
|
392
|
+
process.exit(1);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
254
396
|
function startMcpServer() {
|
|
397
|
+
assertEnvironmentOrExit();
|
|
398
|
+
|
|
255
399
|
const server = new McpServer({
|
|
256
400
|
name: 'editable-slides',
|
|
257
401
|
version: '1.0.0',
|