@infinitedusky/indusk-mcp 1.5.10 → 1.5.12

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.
@@ -501,6 +501,34 @@ function printMcpSetup(projectRoot, name) {
501
501
  return;
502
502
  printMcpInstructions(name, manifest);
503
503
  }
504
+ function readExtensionEnv(name) {
505
+ const cwd = process.cwd();
506
+ // Try profile-specific env files first, then plain .env
507
+ const candidates = [
508
+ join(cwd, ".indusk/extensions", name, ".env.local"),
509
+ join(cwd, ".indusk/extensions", name, ".env"),
510
+ join(cwd, ".indusk/disabled", name, ".env.local"),
511
+ join(cwd, ".indusk/disabled", name, ".env"),
512
+ ];
513
+ for (const envPath of candidates) {
514
+ if (!existsSync(envPath))
515
+ continue;
516
+ const vars = {};
517
+ const content = readFileSync(envPath, "utf-8");
518
+ for (const line of content.split("\n")) {
519
+ const trimmed = line.trim();
520
+ if (!trimmed || trimmed.startsWith("#"))
521
+ continue;
522
+ const eqIdx = trimmed.indexOf("=");
523
+ if (eqIdx > 0) {
524
+ vars[trimmed.slice(0, eqIdx)] = trimmed.slice(eqIdx + 1);
525
+ }
526
+ }
527
+ if (Object.keys(vars).length > 0)
528
+ return vars;
529
+ }
530
+ return {};
531
+ }
504
532
  function printMcpInstructions(name, manifest) {
505
533
  const server = manifest.mcp_server;
506
534
  if (!server)
@@ -520,17 +548,53 @@ function printMcpInstructions(name, manifest) {
520
548
  }
521
549
  return;
522
550
  }
523
- // For servers needing auth, print setup instructions
551
+ // For servers needing auth, try to read credentials from extension .env
552
+ if (needsAuth && server.type === "http" && server.url && server.headers) {
553
+ const envVars = readExtensionEnv(name);
554
+ if (Object.keys(envVars).length > 0) {
555
+ // Substitute placeholders in url and headers
556
+ let url = server.url;
557
+ for (const [envKey, envVal] of Object.entries(envVars)) {
558
+ url = url.replaceAll(envKey, envVal);
559
+ }
560
+ const headerArgs = [];
561
+ let allResolved = true;
562
+ for (const [key, template] of Object.entries(server.headers)) {
563
+ let resolved = template;
564
+ for (const [envKey, envVal] of Object.entries(envVars)) {
565
+ resolved = resolved.replaceAll(envKey, envVal);
566
+ }
567
+ if (resolved === template) {
568
+ allResolved = false;
569
+ break;
570
+ }
571
+ headerArgs.push(`--header "${key}: ${resolved}"`);
572
+ }
573
+ if (allResolved) {
574
+ const cmd = `claude mcp add -t http ${headerArgs.join(" ")} -s project -- ${name} "${url}"`;
575
+ console.info(`\n ${name}: adding MCP server with credentials from .env...`);
576
+ try {
577
+ execSync(cmd, { cwd: process.cwd(), timeout: 15000, stdio: ["ignore", "pipe", "pipe"] });
578
+ console.info(` ${name}: MCP server added (restart Claude Code to load)`);
579
+ return;
580
+ }
581
+ catch {
582
+ console.info(` ${name}: auto-add failed.`);
583
+ }
584
+ }
585
+ }
586
+ }
587
+ // Fallback: no .env found or credentials incomplete
588
+ console.info(`\n ${name}: no credentials found at .indusk/extensions/${name}/.env`);
589
+ console.info(` To set up automatically:`);
590
+ console.info(` 1. Create .indusk/extensions/${name}/.env with the required credentials`);
591
+ console.info(` 2. Run 'extensions enable ${name}' again`);
524
592
  if (server.setup_instructions?.length) {
525
- console.info(`\n ${name} MCP setup:`);
593
+ console.info(` Or set up manually:`);
526
594
  for (const instruction of server.setup_instructions) {
527
- console.info(` ${instruction}`);
595
+ console.info(` ${instruction}`);
528
596
  }
529
597
  }
530
- else if (server.type === "http" && server.url) {
531
- console.info(`\n ${name} MCP setup:`);
532
- console.info(` claude mcp add -t http -- ${name} ${server.url}`);
533
- }
534
598
  console.info("");
535
599
  }
536
600
  function installSkill(projectRoot, name) {
@@ -176,65 +176,73 @@ export async function init(projectRoot, options = {}) {
176
176
  console.info(` ${existsSync(biomePath) ? "overwrite" : "create"}: biome.json`);
177
177
  }
178
178
  // 7. Scaffold OpenTelemetry instrumentation
179
- console.info("\n[OpenTelemetry]");
180
- const otelTemplates = ["instrumentation.ts", "filtering-exporter.ts", "logger.ts"];
181
- const isNextJs = existsSync(join(projectRoot, "next.config.js")) ||
182
- existsSync(join(projectRoot, "next.config.ts")) ||
183
- existsSync(join(projectRoot, "next.config.mjs"));
184
- const isPython = existsSync(join(projectRoot, "requirements.txt")) ||
185
- existsSync(join(projectRoot, "pyproject.toml"));
186
- if (isPython) {
187
- const pyTemplate = "instrumentation.py";
188
- const targetFile = join(projectRoot, pyTemplate);
189
- if (existsSync(targetFile) && !force) {
190
- console.info(` skip: ${pyTemplate} (already exists)`);
191
- }
192
- else {
193
- cpSync(join(packageRoot, "templates", pyTemplate), targetFile);
194
- console.info(` create: ${pyTemplate}`);
195
- console.info(" install: pip install opentelemetry-distro opentelemetry-instrumentation opentelemetry-exporter-otlp");
196
- console.info(" run: opentelemetry-instrument python your_app.py");
197
- }
179
+ // Skip if this is the indusk-mcp package itself (has templates/ directory with instrumentation.ts)
180
+ const isInduskMcp = existsSync(join(projectRoot, "templates/instrumentation.ts"));
181
+ if (isInduskMcp) {
182
+ console.info("\n[OpenTelemetry]");
183
+ console.info(" skip: this is the indusk-mcp package (templates are source, not scaffolded)");
198
184
  }
199
185
  else {
200
- // Node.js or Next.js — copy TypeScript templates
201
- const targetDir = isNextJs ? projectRoot : join(projectRoot, "src");
202
- const srcDir = existsSync(join(projectRoot, "src")) ? join(projectRoot, "src") : projectRoot;
203
- for (const template of otelTemplates) {
204
- const targetFile = join(srcDir, template);
186
+ console.info("\n[OpenTelemetry]");
187
+ const otelTemplates = ["instrumentation.ts", "filtering-exporter.ts", "logger.ts"];
188
+ const isNextJs = existsSync(join(projectRoot, "next.config.js")) ||
189
+ existsSync(join(projectRoot, "next.config.ts")) ||
190
+ existsSync(join(projectRoot, "next.config.mjs"));
191
+ const isPython = existsSync(join(projectRoot, "requirements.txt")) ||
192
+ existsSync(join(projectRoot, "pyproject.toml"));
193
+ if (isPython) {
194
+ const pyTemplate = "instrumentation.py";
195
+ const targetFile = join(projectRoot, pyTemplate);
205
196
  if (existsSync(targetFile) && !force) {
206
- console.info(` skip: ${template} (already exists)`);
207
- continue;
197
+ console.info(` skip: ${pyTemplate} (already exists)`);
198
+ }
199
+ else {
200
+ cpSync(join(packageRoot, "templates", pyTemplate), targetFile);
201
+ console.info(` create: ${pyTemplate}`);
202
+ console.info(" install: pip install opentelemetry-distro opentelemetry-instrumentation opentelemetry-exporter-otlp");
203
+ console.info(" run: opentelemetry-instrument python your_app.py");
208
204
  }
209
- cpSync(join(packageRoot, "templates", template), targetFile);
210
- console.info(` create: ${template}`);
211
- }
212
- // Set service name in instrumentation.ts
213
- const instrPath = join(srcDir, "instrumentation.ts");
214
- if (existsSync(instrPath)) {
215
- const content = readFileSync(instrPath, "utf-8");
216
- writeFileSync(instrPath, content.replace('"unknown-service"', `"${projectName}"`));
217
- }
218
- // Print instructions
219
- const packages = [
220
- "@opentelemetry/sdk-node",
221
- "@opentelemetry/auto-instrumentations-node",
222
- "@opentelemetry/exporter-trace-otlp-http",
223
- "@opentelemetry/sdk-trace-base",
224
- "@opentelemetry/resources",
225
- "@opentelemetry/semantic-conventions",
226
- "@opentelemetry/core",
227
- "pino",
228
- "pino-opentelemetry-transport",
229
- ];
230
- console.info(` install: pnpm add ${packages.join(" ")}`);
231
- if (isNextJs) {
232
- console.info(" wire: Next.js instrumentation hook (instrumentation.ts in app root)");
233
205
  }
234
206
  else {
235
- console.info(" wire: node -r ./src/instrumentation.ts src/index.ts");
207
+ // Node.js or Next.js — copy TypeScript templates
208
+ const targetDir = isNextJs ? projectRoot : join(projectRoot, "src");
209
+ const srcDir = existsSync(join(projectRoot, "src")) ? join(projectRoot, "src") : projectRoot;
210
+ for (const template of otelTemplates) {
211
+ const targetFile = join(srcDir, template);
212
+ if (existsSync(targetFile) && !force) {
213
+ console.info(` skip: ${template} (already exists)`);
214
+ continue;
215
+ }
216
+ cpSync(join(packageRoot, "templates", template), targetFile);
217
+ console.info(` create: ${template}`);
218
+ }
219
+ // Set service name in instrumentation.ts
220
+ const instrPath = join(srcDir, "instrumentation.ts");
221
+ if (existsSync(instrPath)) {
222
+ const content = readFileSync(instrPath, "utf-8");
223
+ writeFileSync(instrPath, content.replace('"unknown-service"', `"${projectName}"`));
224
+ }
225
+ // Print instructions
226
+ const packages = [
227
+ "@opentelemetry/sdk-node",
228
+ "@opentelemetry/auto-instrumentations-node",
229
+ "@opentelemetry/exporter-trace-otlp-http",
230
+ "@opentelemetry/sdk-trace-base",
231
+ "@opentelemetry/resources",
232
+ "@opentelemetry/semantic-conventions",
233
+ "@opentelemetry/core",
234
+ "pino",
235
+ "pino-opentelemetry-transport",
236
+ ];
237
+ console.info(` install: pnpm add ${packages.join(" ")}`);
238
+ if (isNextJs) {
239
+ console.info(" wire: Next.js instrumentation hook (instrumentation.ts in app root)");
240
+ }
241
+ else {
242
+ console.info(" wire: node -r ./src/instrumentation.ts src/index.ts");
243
+ }
236
244
  }
237
- }
245
+ } // end isInduskMcp else
238
246
  // 8. Install gate enforcement hooks
239
247
  console.info("\n[Hooks]");
240
248
  const hooksSource = join(packageRoot, "hooks");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infinitedusky/indusk-mcp",
3
- "version": "1.5.10",
3
+ "version": "1.5.12",
4
4
  "description": "InDusk development system — skills, MCP tools, and CLI for structured AI-assisted development",
5
5
  "type": "module",
6
6
  "files": [
@@ -23,7 +23,7 @@ import {
23
23
  BatchSpanProcessor,
24
24
  SimpleSpanProcessor,
25
25
  } from "@opentelemetry/sdk-trace-base";
26
- import { Resource } from "@opentelemetry/resources";
26
+ import { resourceFromAttributes } from "@opentelemetry/resources";
27
27
  import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
28
28
  import { FilteringExporter } from "./filtering-exporter";
29
29
 
@@ -40,7 +40,7 @@ function createSpanProcessor() {
40
40
  }
41
41
 
42
42
  const sdk = new NodeSDK({
43
- resource: new Resource({
43
+ resource: resourceFromAttributes({
44
44
  [ATTR_SERVICE_NAME]:
45
45
  process.env.OTEL_SERVICE_NAME ?? "unknown-service",
46
46
  }),