@pipeshub-ai/mcp 2.3.0 → 2.3.2
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/README.md +1 -1
- package/bin/mcp-server.js +56 -15
- package/bin/mcp-server.js.map +7 -7
- package/bin/pipeshub.js +185 -24
- package/bin/pipeshub.js.map +7 -7
- package/esm/cli/client.d.ts.map +1 -1
- package/esm/cli/client.js +6 -1
- package/esm/cli/client.js.map +1 -1
- package/esm/cli/commands.d.ts.map +1 -1
- package/esm/cli/commands.js +22 -16
- package/esm/cli/commands.js.map +1 -1
- package/esm/cli/config.d.ts +2 -2
- package/esm/cli/config.js +2 -2
- package/esm/cli/init-qm.d.ts +51 -1
- package/esm/cli/init-qm.d.ts.map +1 -1
- package/esm/cli/init-qm.js +209 -16
- package/esm/cli/init-qm.js.map +1 -1
- package/esm/cli/pipeshub.js +4 -3
- package/esm/cli/pipeshub.js.map +1 -1
- package/esm/mcp-server/tools/_helpers.d.ts +20 -3
- package/esm/mcp-server/tools/_helpers.d.ts.map +1 -1
- package/esm/mcp-server/tools/_helpers.js +41 -4
- package/esm/mcp-server/tools/_helpers.js.map +1 -1
- package/esm/mcp-server/tools/pipeshubDirectory.d.ts.map +1 -1
- package/esm/mcp-server/tools/pipeshubDirectory.js +62 -8
- package/esm/mcp-server/tools/pipeshubDirectory.js.map +1 -1
- package/esm/mcp-server/tools/pipeshubGetRecordContent.d.ts +1 -1
- package/esm/mcp-server/tools/pipeshubSearch.js +1 -1
- package/esm/mcp-server/tools/pipeshubSearch.js.map +1 -1
- package/esm/mcp-server/tools/pipeshubSources.js +2 -2
- package/esm/mcp-server/tools/pipeshubSources.js.map +1 -1
- package/esm/models/availablemodelsresponse.d.ts +1 -1
- package/esm/models/conversation.d.ts +1 -1
- package/esm/models/userteamsresponse.d.ts +1 -1
- package/esm/tool-names.js +1 -1
- package/esm/tool-names.js.map +1 -1
- package/package.json +1 -1
- package/qm/README.md +84 -71
- package/qm/SECURITY.md +7 -5
- package/qm/TROUBLESHOOTING.md +34 -29
- package/qm/qm.config.fragment.jsonc +12 -33
- package/qm/sandbox/Dockerfile +1 -1
- package/qm/sandbox/skills/pipeshub/SKILL.md +27 -7
- package/qm/sandbox/tools/pipeshub/tool.json +1 -1
- package/src/cli/client.ts +8 -1
- package/src/cli/commands.ts +22 -16
- package/src/cli/config.ts +2 -2
- package/src/cli/init-qm.ts +206 -17
- package/src/cli/pipeshub.ts +4 -3
- package/src/mcp-server/tools/_helpers.ts +42 -2
- package/src/mcp-server/tools/pipeshubDirectory.ts +65 -7
- package/src/mcp-server/tools/pipeshubSearch.ts +1 -1
- package/src/mcp-server/tools/pipeshubSources.ts +2 -2
- package/src/tool-names.ts +1 -1
package/bin/pipeshub.js
CHANGED
|
@@ -166,7 +166,7 @@ function toolErrorToExit(message) {
|
|
|
166
166
|
const m = message.match(/\(HTTP\s+(\d{3})/i);
|
|
167
167
|
if (m && m[1] !== undefined)
|
|
168
168
|
return statusToExit(Number(m[1]));
|
|
169
|
-
if (/\b401\b|unautheni?ticated|no token provided/i.test(message)) {
|
|
169
|
+
if (/\b401\b|unautheni?ticated|no token provided|token expired|been revoked/i.test(message)) {
|
|
170
170
|
return EXIT.UNAUTHENTICATED;
|
|
171
171
|
}
|
|
172
172
|
if (/\b403\b|not have permission|forbidden/i.test(message)) {
|
|
@@ -366,6 +366,116 @@ RUN npm install -g "@pipeshub-ai/mcp@${version}" \\
|
|
|
366
366
|
&& pipeshub --help >/dev/null
|
|
367
367
|
# ----------------------------------------------------------------------------
|
|
368
368
|
`;
|
|
369
|
+
function stripJsonComments(src) {
|
|
370
|
+
let out = "";
|
|
371
|
+
let inString = false;
|
|
372
|
+
let inLine = false;
|
|
373
|
+
let inBlock = false;
|
|
374
|
+
for (let i = 0;i < src.length; i++) {
|
|
375
|
+
const c = src[i];
|
|
376
|
+
const next = src[i + 1];
|
|
377
|
+
if (inLine) {
|
|
378
|
+
if (c === `
|
|
379
|
+
`) {
|
|
380
|
+
inLine = false;
|
|
381
|
+
out += c;
|
|
382
|
+
}
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (inBlock) {
|
|
386
|
+
if (c === "*" && next === "/") {
|
|
387
|
+
inBlock = false;
|
|
388
|
+
i++;
|
|
389
|
+
}
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
if (inString) {
|
|
393
|
+
out += c;
|
|
394
|
+
if (c === "\\") {
|
|
395
|
+
out += next ?? "";
|
|
396
|
+
i++;
|
|
397
|
+
continue;
|
|
398
|
+
}
|
|
399
|
+
if (c === '"')
|
|
400
|
+
inString = false;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (c === '"') {
|
|
404
|
+
inString = true;
|
|
405
|
+
out += c;
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
if (c === "/" && next === "/") {
|
|
409
|
+
inLine = true;
|
|
410
|
+
i++;
|
|
411
|
+
continue;
|
|
412
|
+
}
|
|
413
|
+
if (c === "/" && next === "*") {
|
|
414
|
+
inBlock = true;
|
|
415
|
+
i++;
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
out += c;
|
|
419
|
+
}
|
|
420
|
+
return out;
|
|
421
|
+
}
|
|
422
|
+
function dropTrailingCommas(src) {
|
|
423
|
+
let out = "";
|
|
424
|
+
let inString = false;
|
|
425
|
+
for (let i = 0;i < src.length; i++) {
|
|
426
|
+
const c = src[i];
|
|
427
|
+
if (inString) {
|
|
428
|
+
out += c;
|
|
429
|
+
if (c === "\\") {
|
|
430
|
+
out += src[i + 1] ?? "";
|
|
431
|
+
i++;
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
if (c === '"')
|
|
435
|
+
inString = false;
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
if (c === '"') {
|
|
439
|
+
inString = true;
|
|
440
|
+
out += c;
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (c === ",") {
|
|
444
|
+
let j = i + 1;
|
|
445
|
+
while (j < src.length && /\s/.test(src[j]))
|
|
446
|
+
j++;
|
|
447
|
+
if (src[j] === "}" || src[j] === "]")
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
out += c;
|
|
451
|
+
}
|
|
452
|
+
return out;
|
|
453
|
+
}
|
|
454
|
+
async function readDeploymentShape(dest) {
|
|
455
|
+
try {
|
|
456
|
+
const raw = await readFile(join(dest, "qm.config.jsonc"), "utf8");
|
|
457
|
+
const cfg = JSON.parse(dropTrailingCommas(stripJsonComments(raw)));
|
|
458
|
+
const target = typeof cfg.target === "string" ? cfg.target : undefined;
|
|
459
|
+
const backend = typeof cfg.sandbox?.backend === "string" ? cfg.sandbox.backend : undefined;
|
|
460
|
+
if (!target && !backend)
|
|
461
|
+
return null;
|
|
462
|
+
return { target, backend };
|
|
463
|
+
} catch {
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function imageSkipReason(shape) {
|
|
468
|
+
if (!shape)
|
|
469
|
+
return null;
|
|
470
|
+
if (shape.backend === "aws")
|
|
471
|
+
return "aws-microvm";
|
|
472
|
+
if (shape.backend === "sprites" || shape.target === "fly") {
|
|
473
|
+
return "sprites-ignores-image";
|
|
474
|
+
}
|
|
475
|
+
if (shape.target === "aws")
|
|
476
|
+
return "sprites-ignores-image";
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
369
479
|
async function initQm(targetDir, force) {
|
|
370
480
|
const root = await packageRoot();
|
|
371
481
|
const bundle = join(root, "qm");
|
|
@@ -374,6 +484,7 @@ async function initQm(targetDir, force) {
|
|
|
374
484
|
}
|
|
375
485
|
const version = await packageVersion(root);
|
|
376
486
|
const dest = resolve(targetDir);
|
|
487
|
+
const shape = await readDeploymentShape(dest);
|
|
377
488
|
const written = [];
|
|
378
489
|
const skipped = [];
|
|
379
490
|
for (const sub of ["tools/pipeshub", "skills/pipeshub"]) {
|
|
@@ -381,7 +492,12 @@ async function initQm(targetDir, force) {
|
|
|
381
492
|
}
|
|
382
493
|
const dockerfile = join(dest, "sandbox", "Dockerfile");
|
|
383
494
|
let dockerfileAction;
|
|
384
|
-
|
|
495
|
+
const skipReason = imageSkipReason(shape);
|
|
496
|
+
let staleDockerfile = false;
|
|
497
|
+
if (skipReason !== null) {
|
|
498
|
+
staleDockerfile = await exists(dockerfile);
|
|
499
|
+
dockerfileAction = "skipped-unusable";
|
|
500
|
+
} else if (!await exists(dockerfile)) {
|
|
385
501
|
await copyFile(join(bundle, "sandbox", "Dockerfile"), dockerfile);
|
|
386
502
|
const body = await readFile(dockerfile, "utf8");
|
|
387
503
|
await writeFile(dockerfile, body.replace(/ARG PIPESHUB_CLI_VERSION=.*/, `ARG PIPESHUB_CLI_VERSION=${version}`), "utf8");
|
|
@@ -404,7 +520,16 @@ async function initQm(targetDir, force) {
|
|
|
404
520
|
} catch (e) {
|
|
405
521
|
throw new CliError(`the QM bundle is incomplete — could not read ${fragmentPath} ` + `(${e.message}). Reinstall @pipeshub-ai/mcp.`);
|
|
406
522
|
}
|
|
407
|
-
return {
|
|
523
|
+
return {
|
|
524
|
+
written,
|
|
525
|
+
skipped,
|
|
526
|
+
dockerfileAction,
|
|
527
|
+
version,
|
|
528
|
+
configFragment,
|
|
529
|
+
shape,
|
|
530
|
+
skipReason,
|
|
531
|
+
staleDockerfile
|
|
532
|
+
};
|
|
408
533
|
}
|
|
409
534
|
function renderInitReport(dest, r) {
|
|
410
535
|
const lines = [];
|
|
@@ -416,6 +541,34 @@ function renderInitReport(dest, r) {
|
|
|
416
541
|
for (const f of r.skipped)
|
|
417
542
|
lines.push(` kept ${f} (already existed — use --force to replace)`);
|
|
418
543
|
lines.push("");
|
|
544
|
+
if (r.dockerfileAction === "skipped-unusable") {
|
|
545
|
+
if (r.skipReason === "aws-microvm") {
|
|
546
|
+
lines.push("No sandbox/Dockerfile was written: AWS Lambda MicroVM sandboxes");
|
|
547
|
+
lines.push("have no way to install a binary, so the file could never run.");
|
|
548
|
+
} else {
|
|
549
|
+
lines.push("No sandbox/Dockerfile was written: Fly Sprites boot the stock");
|
|
550
|
+
lines.push("image and ignore a published one, so the file would look like the");
|
|
551
|
+
lines.push("install path while never running.");
|
|
552
|
+
}
|
|
553
|
+
lines.push("The skill installs the CLI on first use instead — that is the line");
|
|
554
|
+
lines.push("that actually executes, and it needs nothing from you.");
|
|
555
|
+
lines.push("");
|
|
556
|
+
}
|
|
557
|
+
if (r.staleDockerfile) {
|
|
558
|
+
lines.push("ACTION NEEDED: sandbox/Dockerfile already exists here, written by an");
|
|
559
|
+
lines.push("earlier version. This deployment cannot use it, and upcoming QM");
|
|
560
|
+
lines.push("validation rejects it rather than ignoring it — `qm check` will fail");
|
|
561
|
+
lines.push("with an error naming that file. Delete it, or remove the PipesHub");
|
|
562
|
+
lines.push("install block if the rest of it is yours.");
|
|
563
|
+
lines.push("");
|
|
564
|
+
}
|
|
565
|
+
if (r.skipReason === "aws-microvm") {
|
|
566
|
+
lines.push("Heads up on AWS: with Lambda MicroVM sandboxes the CLI cannot be");
|
|
567
|
+
lines.push("installed at all (yc-software/qm#350). The tool's guidance, network");
|
|
568
|
+
lines.push("allowlist, and approval rules still apply, but the binary will be");
|
|
569
|
+
lines.push("missing. The sprites backend is what this bundle is tested against.");
|
|
570
|
+
lines.push("");
|
|
571
|
+
}
|
|
419
572
|
if (r.dockerfileAction === "appended") {
|
|
420
573
|
lines.push("Appended the install block to your existing sandbox/Dockerfile.");
|
|
421
574
|
} else if (r.dockerfileAction === "manual") {
|
|
@@ -424,20 +577,22 @@ function renderInitReport(dest, r) {
|
|
|
424
577
|
lines.push("");
|
|
425
578
|
lines.push("Two things left to do:");
|
|
426
579
|
lines.push("");
|
|
427
|
-
lines.push("1. Set
|
|
428
|
-
lines.push("
|
|
429
|
-
lines.push("
|
|
580
|
+
lines.push("1. Set `egress` in sandbox/tools/pipeshub/tool.json to your PipesHub");
|
|
581
|
+
lines.push(" hostname (no scheme, no path). It must be reachable over public HTTPS");
|
|
582
|
+
lines.push(" — QM sandboxes do not run on your machine, so localhost is unreachable.");
|
|
430
583
|
lines.push("");
|
|
431
|
-
lines.push(
|
|
432
|
-
lines.push(
|
|
433
|
-
lines.push("
|
|
584
|
+
lines.push("2. Each person adds two personal keychain entries (service: pipeshub):");
|
|
585
|
+
lines.push(" PIPESHUB_TOKEN → their PAT (never paste it into chat)");
|
|
586
|
+
lines.push(" PIPESHUB_BASE_URL → public HTTPS origin, no /mcp path");
|
|
587
|
+
lines.push(" Do NOT put a token in sandbox.secretEnv (org-wide). Do NOT rely on");
|
|
588
|
+
lines.push(" sandbox.env for the URL — it does not reach the sandbox.");
|
|
434
589
|
lines.push("");
|
|
435
|
-
lines.push("
|
|
436
|
-
lines.push(" would hand one person's credential to everybody. Each person adds");
|
|
437
|
-
lines.push(" their own to their own keychain (service: pipeshub, kind: env).");
|
|
590
|
+
lines.push("Then: qm check && qm up");
|
|
438
591
|
lines.push("");
|
|
439
|
-
|
|
440
|
-
|
|
592
|
+
if (r.dockerfileAction !== "skipped-unusable") {
|
|
593
|
+
lines.push("On Sprites, `qm sandbox publish` does not put pipeshub on PATH.");
|
|
594
|
+
lines.push("The skill installs the CLI on first use.");
|
|
595
|
+
}
|
|
441
596
|
return lines.join(`
|
|
442
597
|
`);
|
|
443
598
|
}
|
|
@@ -508,19 +663,25 @@ function connectHelp(ctx) {
|
|
|
508
663
|
"Add your Personal Access Token to your own QM keychain:",
|
|
509
664
|
"",
|
|
510
665
|
" 1. In PipesHub, open Developer Settings → Personal Access Tokens.",
|
|
511
|
-
" 2. Create a token.
|
|
512
|
-
"
|
|
513
|
-
" 3. In QM, add
|
|
666
|
+
" 2. Create a token. The panel defaults are fine — do not deselect scopes,",
|
|
667
|
+
" and do not add semantic:read if asked.",
|
|
668
|
+
" 3. In QM, add two personal keychain credentials (not a shared room):",
|
|
669
|
+
" service: pipeshub",
|
|
670
|
+
" environment variable: PIPESHUB_TOKEN",
|
|
671
|
+
" value: the token only — no URL, no KEY= prefix",
|
|
672
|
+
" and",
|
|
514
673
|
" service: pipeshub",
|
|
515
|
-
"
|
|
516
|
-
" value:
|
|
674
|
+
" environment variable: PIPESHUB_BASE_URL",
|
|
675
|
+
" value: public HTTPS origin, no /mcp path",
|
|
517
676
|
"",
|
|
518
|
-
"
|
|
677
|
+
"They arrive in your sandbox on the next turn.",
|
|
519
678
|
"",
|
|
520
679
|
"Never paste the token into a chat message: chat transcripts are durable",
|
|
521
680
|
"and pass through the model provider. The keychain exists to avoid that.",
|
|
522
681
|
"",
|
|
523
|
-
`The base URL
|
|
682
|
+
`The base URL currently visible here is: ${ctx.origin || "(unset)"}`,
|
|
683
|
+
"If it is unset, sandbox.env did not reach this sandbox — use the",
|
|
684
|
+
"PIPESHUB_BASE_URL keychain entry (or an org service credential)."
|
|
524
685
|
].join(`
|
|
525
686
|
`);
|
|
526
687
|
return { exit: EXIT.OK, payload: { requestId: ctx.requestId, help: text }, text };
|
|
@@ -613,7 +774,7 @@ async function ask(ctx, query, conversationId, chatMode) {
|
|
|
613
774
|
citationCount: citations.length,
|
|
614
775
|
citations,
|
|
615
776
|
confidence: obj["confidence"] ?? null,
|
|
616
|
-
warning: citations.length === 0 ? "No citations.
|
|
777
|
+
warning: citations.length === 0 ? "No citations — unsourced, so nothing in it can be verified. If " + "this is a refusal, relay it. If it asserts facts, do not repeat " + "them. Ignore confidence." : null
|
|
617
778
|
}
|
|
618
779
|
};
|
|
619
780
|
}
|
|
@@ -876,7 +1037,7 @@ async function run(argv) {
|
|
|
876
1037
|
}
|
|
877
1038
|
if (origin === null) {
|
|
878
1039
|
const alsoNoToken = token === null ? " Your PipesHub credential is also missing ($PIPESHUB_TOKEN is unset)." : "";
|
|
879
|
-
throw new CliError("PIPESHUB_BASE_URL is not set — an
|
|
1040
|
+
throw new CliError("PIPESHUB_BASE_URL is not set — it must reach the sandbox as an env var " + "(keychain or org service credential, not sandbox.env)." + alsoNoToken + " Run 'pipeshub auth connect-help' for the steps.", EXIT.USAGE);
|
|
880
1041
|
}
|
|
881
1042
|
if (token === null) {
|
|
882
1043
|
throw new CliError("No PipesHub credential found ($PIPESHUB_TOKEN is unset). " + "Run 'pipeshub auth connect-help' for setup steps.", EXIT.UNAUTHENTICATED);
|
|
@@ -942,5 +1103,5 @@ var code = await run(process.argv.slice(2)).catch(async (e) => {
|
|
|
942
1103
|
});
|
|
943
1104
|
process.exit(code);
|
|
944
1105
|
|
|
945
|
-
//# debugId=
|
|
1106
|
+
//# debugId=593C31ACCEC7553364756E2164756E21
|
|
946
1107
|
//# sourceMappingURL=pipeshub.js.map
|