@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.
Files changed (2) hide show
  1. package/index.js +160 -16
  2. 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/3] Google Cloud Authentication:`);
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/3] Google Cloud Authentication:`);
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
- if (project) {
85
- console.error(`\n [2/3] GEAP Billing & Quota Project:`);
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/3] GEAP Billing & Quota Project:`);
90
- console.error(` ✗ FAILED: No GCP project configured for billing.`);
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
- // 3. Check Backend Connectivity
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 [3/3] EditNBLM Cloud Backend:`);
181
+ console.error(`\n [4/4] EditNBLM Cloud Backend:`);
100
182
  console.error(` ✓ Status: Connected (${BASE_URL})`);
101
183
  } else {
102
- console.error(`\n [3/3] EditNBLM Cloud Backend:`);
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 [3/3] EditNBLM Cloud Backend:`);
188
+ console.error(`\n [4/4] EditNBLM Cloud Backend:`);
107
189
  console.error(` ✗ Status: Unreachable (${err.message})`);
108
190
  }
109
191
 
110
- // 4. One-Time IAM Delegation command helper
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('🎉 Pre-flight check PASSED! Your environment is ready to use EditNBLM MCP.\n');
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
- throw new Error(data.error.message || JSON.stringify(data.error));
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',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jieunmarslim/server-editable-slides",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Model Context Protocol (MCP) client for Editable Slides Cloud",
5
5
  "type": "module",
6
6
  "bin": {