@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,
|
|
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(
|
|
593
|
+
console.info(` Or set up manually:`);
|
|
526
594
|
for (const instruction of server.setup_instructions) {
|
|
527
|
-
console.info(`
|
|
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
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
|
|
204
|
-
|
|
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: ${
|
|
207
|
-
|
|
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
|
-
|
|
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
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
BatchSpanProcessor,
|
|
24
24
|
SimpleSpanProcessor,
|
|
25
25
|
} from "@opentelemetry/sdk-trace-base";
|
|
26
|
-
import {
|
|
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:
|
|
43
|
+
resource: resourceFromAttributes({
|
|
44
44
|
[ATTR_SERVICE_NAME]:
|
|
45
45
|
process.env.OTEL_SERVICE_NAME ?? "unknown-service",
|
|
46
46
|
}),
|