qyl-mcp-server 1.0.0-beta.3 → 1.0.0
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/dist/ci.d.ts +8 -4
- package/dist/ci.js +3 -3
- package/dist/ci.js.map +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.js +1 -1
- package/dist/contract-handshake.d.ts +66 -0
- package/dist/contract-handshake.js +142 -0
- package/dist/contract-handshake.js.map +1 -0
- package/dist/contract-validation.d.ts +118 -101
- package/dist/contract-validation.js +106 -103
- package/dist/contract-validation.js.map +1 -1
- package/dist/data.js +5 -5
- package/dist/data.js.map +1 -1
- package/dist/demo.js +37 -30
- package/dist/demo.js.map +1 -1
- package/dist/execution-evidence.d.ts +3 -3
- package/dist/execution-evidence.js +5 -5
- package/dist/execution-evidence.js.map +1 -1
- package/dist/main.js +5 -0
- package/dist/main.js.map +1 -1
- package/dist/mcp-app.html +7 -7
- package/dist/mcp-dashboard.html +7 -7
- package/dist/mcp-home.html +1 -1
- package/dist/native-execution.d.ts +3 -11
- package/dist/native-execution.js +10 -13
- package/dist/native-execution.js.map +1 -1
- package/dist/oauth.d.ts +2 -0
- package/dist/oauth.js +11 -18
- package/dist/oauth.js.map +1 -1
- package/dist/stats.js +7 -5
- package/dist/stats.js.map +1 -1
- package/dist/stderr-log.d.ts +15 -0
- package/dist/stderr-log.js +20 -0
- package/dist/stderr-log.js.map +1 -0
- package/dist/summaries.d.ts +14 -2
- package/dist/summaries.js +33 -14
- package/dist/summaries.js.map +1 -1
- package/package.json +7 -5
package/dist/ci.d.ts
CHANGED
|
@@ -9,10 +9,14 @@
|
|
|
9
9
|
* - one span per phase, named after the phase, carrying a `ci.leg` string
|
|
10
10
|
* attribute (e.g. "macos-latest"); a failed phase sets span status error.
|
|
11
11
|
*
|
|
12
|
-
* The input/output
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
12
|
+
* The input/output shapes have graduated: they are authored in qyl-api-schema
|
|
13
|
+
* as Mcp.Tools.CiLogInput/CiRunSummary/CiPhase/CiLogOutput. The inline zod below
|
|
14
|
+
* is what remains until the @ancplua/qyl-api-schema pin moves past 3.0.0, which
|
|
15
|
+
* is the first release whose JSON Schema carries those definitions —
|
|
16
|
+
* publishedContractSchema throws at module load on a definition the installed
|
|
17
|
+
* package does not have. On that bump, delete these schemas, take the four
|
|
18
|
+
* validators from contract-validation.ts, and drop the ci.ts entries from
|
|
19
|
+
* verify-generated-shapes.mjs.
|
|
16
20
|
*/
|
|
17
21
|
import type { McpServer } from "@modelcontextprotocol/server";
|
|
18
22
|
import { z } from "zod";
|
package/dist/ci.js
CHANGED
|
@@ -47,7 +47,7 @@ function spanLeg(span) {
|
|
|
47
47
|
const attribute = span.attributes?.find((entry) => entry.key === "ci.leg");
|
|
48
48
|
return typeof attribute?.value === "string"
|
|
49
49
|
? attribute.value
|
|
50
|
-
: span.resource
|
|
50
|
+
: span.resource.service_name;
|
|
51
51
|
}
|
|
52
52
|
/** Flatten a run's traces into per-leg phases, failures first. */
|
|
53
53
|
export function collectCiPhases(traces) {
|
|
@@ -55,7 +55,7 @@ export function collectCiPhases(traces) {
|
|
|
55
55
|
leg: spanLeg(span),
|
|
56
56
|
phase: span.name,
|
|
57
57
|
status: span.status.code === 2 ? "error" : span.status.code === 1 ? "ok" : "unset",
|
|
58
|
-
duration_ms: Math.max(0, Math.round((span.end_time_unix_nano - span.start_time_unix_nano) / 1_000_000)),
|
|
58
|
+
duration_ms: Math.max(0, Math.round(Number(BigInt(span.end_time_unix_nano) - BigInt(span.start_time_unix_nano)) / 1_000_000)),
|
|
59
59
|
...(span.status.message ? { message: span.status.message } : {}),
|
|
60
60
|
})));
|
|
61
61
|
return phases.sort((a, b) => a.status === b.status
|
|
@@ -124,7 +124,7 @@ export function registerCiTools(server) {
|
|
|
124
124
|
const runs = filterCiSessions(sessions)
|
|
125
125
|
.slice(0, args.limit ?? 10)
|
|
126
126
|
.map((session) => ({
|
|
127
|
-
run_id: session["
|
|
127
|
+
run_id: session["session_id"],
|
|
128
128
|
state: session.state,
|
|
129
129
|
start_time: session.start_time,
|
|
130
130
|
...(session.duration_ms === undefined ? {} : { duration_ms: session.duration_ms }),
|
package/dist/ci.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ci.js","sourceRoot":"","sources":["../src/ci.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ci.js","sourceRoot":"","sources":["../src/ci.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared configuration: UI resource URIs and collector endpoint resolution.
|
|
3
3
|
*
|
|
4
4
|
* The collector URL is resolved lazily (per call, not at module load) so the
|
|
5
|
-
* embedding host — the qyl.mcp
|
|
5
|
+
* embedding host — the qyl.mcp workbench hosting this server in-process — controls
|
|
6
6
|
* it through its own environment.
|
|
7
7
|
*/
|
|
8
8
|
/** URI of the trace explorer UI resource. */
|
package/dist/config.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared configuration: UI resource URIs and collector endpoint resolution.
|
|
3
3
|
*
|
|
4
4
|
* The collector URL is resolved lazily (per call, not at module load) so the
|
|
5
|
-
* embedding host — the qyl.mcp
|
|
5
|
+
* embedding host — the qyl.mcp workbench hosting this server in-process — controls
|
|
6
6
|
* it through its own environment.
|
|
7
7
|
*/
|
|
8
8
|
import qylOpenApi from "@ancplua/qyl-api-schema/openapi" with { type: "json" };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Startup contract-revision handshake (architecture gate G10c).
|
|
3
|
+
*
|
|
4
|
+
* The collector advertises the revision of the API contract it serves on its
|
|
5
|
+
* health surface; this server compares it to the revision baked into the
|
|
6
|
+
* generated artifacts it was built from and refuses to run on a mismatch. It is
|
|
7
|
+
* the SelfExportGuard pattern applied to the contract axis: lockstep deploys are
|
|
8
|
+
* the honest cost of one contract, and for a solo-operated Railway pair that
|
|
9
|
+
* cost is near zero.
|
|
10
|
+
*
|
|
11
|
+
* What the gate proves is skew, and skew needs evidence: a revision the
|
|
12
|
+
* collector actually advertised. An unreachable collector proves nothing, so it
|
|
13
|
+
* is handled by transport rather than misreported as a mismatch:
|
|
14
|
+
*
|
|
15
|
+
* - hosted HTTP refuses to start after a bounded in-process retry (which covers
|
|
16
|
+
* private-network DNS warm-up) — a booted mcp.qyl.at must mean a completed
|
|
17
|
+
* handshake, and the platform restart loop is the outer retry;
|
|
18
|
+
* - stdio starts degraded with a structured warning — a local install without a
|
|
19
|
+
* running collector is a normal state, and every tool call already surfaces
|
|
20
|
+
* an actionable collector error until one appears.
|
|
21
|
+
*
|
|
22
|
+
* A reachable collector whose 200 health body carries no contract_revision
|
|
23
|
+
* serves a contract older than this server's: that is evidence of skew and
|
|
24
|
+
* stays fatal on both transports, as does a differing revision.
|
|
25
|
+
*/
|
|
26
|
+
/** The contract revision this build's generated artifacts were emitted from. */
|
|
27
|
+
export declare function generatedContractRevision(): string;
|
|
28
|
+
export type HealthProbeResult = {
|
|
29
|
+
kind: "advertised";
|
|
30
|
+
healthy: boolean;
|
|
31
|
+
revision: string;
|
|
32
|
+
} | {
|
|
33
|
+
kind: "pre-revision";
|
|
34
|
+
detail: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: "unreachable";
|
|
37
|
+
detail: string;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Read the one field the handshake needs from whatever the collector serves.
|
|
41
|
+
*
|
|
42
|
+
* Deliberately lenient where the rest of this server validates with the
|
|
43
|
+
* generated contract: the probe must read health bodies emitted by ANY contract
|
|
44
|
+
* revision — older and newer than this build's — because diagnosing skew is its
|
|
45
|
+
* whole purpose, and a validator generated from this build's revision cannot
|
|
46
|
+
* accept a body from the revision it is trying to detect. An unhealthy
|
|
47
|
+
* collector still advertises its revision (the health writer always includes
|
|
48
|
+
* it), so a 503 body with a revision is compared like a 200.
|
|
49
|
+
*/
|
|
50
|
+
export declare function probeCollectorHealth(): Promise<HealthProbeResult>;
|
|
51
|
+
export interface HandshakeOptions {
|
|
52
|
+
/** Revision this server was generated from; defaults to the build's own. */
|
|
53
|
+
expected?: string;
|
|
54
|
+
/** Defaults to the invocation mode main() derives from the same argv. */
|
|
55
|
+
transport?: "stdio" | "http";
|
|
56
|
+
probe?: () => Promise<HealthProbeResult>;
|
|
57
|
+
attempts?: number;
|
|
58
|
+
delayMs?: number;
|
|
59
|
+
sleep?: (ms: number) => Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Startup gate for both transports. Throws — and so aborts startup — on
|
|
63
|
+
* evidence of contract skew; an unreachable collector aborts the hosted HTTP
|
|
64
|
+
* transport after retries and degrades stdio with a structured warning.
|
|
65
|
+
*/
|
|
66
|
+
export declare function assertCollectorContractRevision(options?: HandshakeOptions): Promise<void>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Startup contract-revision handshake (architecture gate G10c).
|
|
3
|
+
*
|
|
4
|
+
* The collector advertises the revision of the API contract it serves on its
|
|
5
|
+
* health surface; this server compares it to the revision baked into the
|
|
6
|
+
* generated artifacts it was built from and refuses to run on a mismatch. It is
|
|
7
|
+
* the SelfExportGuard pattern applied to the contract axis: lockstep deploys are
|
|
8
|
+
* the honest cost of one contract, and for a solo-operated Railway pair that
|
|
9
|
+
* cost is near zero.
|
|
10
|
+
*
|
|
11
|
+
* What the gate proves is skew, and skew needs evidence: a revision the
|
|
12
|
+
* collector actually advertised. An unreachable collector proves nothing, so it
|
|
13
|
+
* is handled by transport rather than misreported as a mismatch:
|
|
14
|
+
*
|
|
15
|
+
* - hosted HTTP refuses to start after a bounded in-process retry (which covers
|
|
16
|
+
* private-network DNS warm-up) — a booted mcp.qyl.at must mean a completed
|
|
17
|
+
* handshake, and the platform restart loop is the outer retry;
|
|
18
|
+
* - stdio starts degraded with a structured warning — a local install without a
|
|
19
|
+
* running collector is a normal state, and every tool call already surfaces
|
|
20
|
+
* an actionable collector error until one appears.
|
|
21
|
+
*
|
|
22
|
+
* A reachable collector whose 200 health body carries no contract_revision
|
|
23
|
+
* serves a contract older than this server's: that is evidence of skew and
|
|
24
|
+
* stays fatal on both transports, as does a differing revision.
|
|
25
|
+
*/
|
|
26
|
+
import { CONTRACT_REVISION } from "@ancplua/qyl-api-schema/types";
|
|
27
|
+
import { resolveMode } from "./collector.js";
|
|
28
|
+
import { collectorHeaders, collectorUrl } from "./config.js";
|
|
29
|
+
import { logInfo, logWarning } from "./stderr-log.js";
|
|
30
|
+
/** The contract revision this build's generated artifacts were emitted from. */
|
|
31
|
+
export function generatedContractRevision() {
|
|
32
|
+
if (typeof CONTRACT_REVISION !== "string" || !/^sha256:[a-f0-9]{16}$/u.test(CONTRACT_REVISION)) {
|
|
33
|
+
throw new Error("this build carries a malformed CONTRACT_REVISION from @ancplua/qyl-api-schema; " +
|
|
34
|
+
"the generated artifacts are broken and the G10c gate cannot run");
|
|
35
|
+
}
|
|
36
|
+
return CONTRACT_REVISION;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Read the one field the handshake needs from whatever the collector serves.
|
|
40
|
+
*
|
|
41
|
+
* Deliberately lenient where the rest of this server validates with the
|
|
42
|
+
* generated contract: the probe must read health bodies emitted by ANY contract
|
|
43
|
+
* revision — older and newer than this build's — because diagnosing skew is its
|
|
44
|
+
* whole purpose, and a validator generated from this build's revision cannot
|
|
45
|
+
* accept a body from the revision it is trying to detect. An unhealthy
|
|
46
|
+
* collector still advertises its revision (the health writer always includes
|
|
47
|
+
* it), so a 503 body with a revision is compared like a 200.
|
|
48
|
+
*/
|
|
49
|
+
export async function probeCollectorHealth() {
|
|
50
|
+
const url = new URL("/health", collectorUrl());
|
|
51
|
+
let response;
|
|
52
|
+
try {
|
|
53
|
+
response = await fetch(url, {
|
|
54
|
+
headers: { accept: "application/json", ...collectorHeaders() },
|
|
55
|
+
signal: AbortSignal.timeout(5_000),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
return { kind: "unreachable", detail: describeNetworkFailure(error) };
|
|
60
|
+
}
|
|
61
|
+
let body;
|
|
62
|
+
try {
|
|
63
|
+
body = await response.json();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return response.ok
|
|
67
|
+
? { kind: "pre-revision", detail: "the 200 health body is not JSON" }
|
|
68
|
+
: { kind: "unreachable", detail: `health returned ${response.status} with a non-JSON body` };
|
|
69
|
+
}
|
|
70
|
+
const revision = typeof body === "object" && body !== null && !Array.isArray(body)
|
|
71
|
+
? body.contract_revision
|
|
72
|
+
: undefined;
|
|
73
|
+
if (typeof revision === "string" && revision.length > 0) {
|
|
74
|
+
return { kind: "advertised", healthy: response.ok, revision };
|
|
75
|
+
}
|
|
76
|
+
return response.ok
|
|
77
|
+
? { kind: "pre-revision", detail: `the ${response.status} health body carries no contract_revision` }
|
|
78
|
+
: { kind: "unreachable", detail: `health returned ${response.status} without a contract_revision` };
|
|
79
|
+
}
|
|
80
|
+
function describeNetworkFailure(error) {
|
|
81
|
+
if (!(error instanceof Error))
|
|
82
|
+
return "unknown network failure";
|
|
83
|
+
if (error.cause instanceof Error && error.cause.message)
|
|
84
|
+
return error.cause.message;
|
|
85
|
+
return error.message || error.name;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Startup gate for both transports. Throws — and so aborts startup — on
|
|
89
|
+
* evidence of contract skew; an unreachable collector aborts the hosted HTTP
|
|
90
|
+
* transport after retries and degrades stdio with a structured warning.
|
|
91
|
+
*/
|
|
92
|
+
export async function assertCollectorContractRevision(options = {}) {
|
|
93
|
+
// Demo mode never reaches a collector, so there is no peer to handshake with.
|
|
94
|
+
// This is the same condition that decides whether a collector is used at all,
|
|
95
|
+
// not a bypass for live deployments.
|
|
96
|
+
if (await resolveMode() === "demo")
|
|
97
|
+
return;
|
|
98
|
+
const expected = options.expected ?? generatedContractRevision();
|
|
99
|
+
const transport = options.transport ?? (process.argv.includes("--stdio") ? "stdio" : "http");
|
|
100
|
+
const probe = options.probe ?? probeCollectorHealth;
|
|
101
|
+
const attempts = options.attempts ?? (transport === "http" ? 4 : 1);
|
|
102
|
+
const delayMs = options.delayMs ?? 1_500;
|
|
103
|
+
const sleep = options.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
|
|
104
|
+
let lastDetail = "collector never probed";
|
|
105
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) {
|
|
106
|
+
const result = await probe();
|
|
107
|
+
if (result.kind === "advertised") {
|
|
108
|
+
if (result.revision !== expected) {
|
|
109
|
+
fail(`collector contract revision ${result.revision} does not match this server's ` +
|
|
110
|
+
`${expected}: deploy the collector and qyl.mcp from the same contract revision`);
|
|
111
|
+
}
|
|
112
|
+
if (!result.healthy) {
|
|
113
|
+
logWarning(`collector at ${collectorUrl()} is unhealthy but advertises the matching ` +
|
|
114
|
+
"contract revision; proceeding");
|
|
115
|
+
}
|
|
116
|
+
logInfo(`contract revision ${result.revision} matched at ${collectorUrl()}`);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if (result.kind === "pre-revision") {
|
|
120
|
+
fail(`collector health surface advertises no contract_revision (${result.detail}): it ` +
|
|
121
|
+
"serves a contract older than the one this server was generated from");
|
|
122
|
+
}
|
|
123
|
+
lastDetail = result.detail;
|
|
124
|
+
if (attempt < attempts)
|
|
125
|
+
await sleep(delayMs);
|
|
126
|
+
}
|
|
127
|
+
if (transport === "stdio") {
|
|
128
|
+
logWarning(`contract revision unverified: collector unreachable at ${collectorUrl()} ` +
|
|
129
|
+
`(${lastDetail}); tools will return actionable collector errors until it is reachable`);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
fail(`collector unreachable at ${collectorUrl()} after ${attempts} attempts (${lastDetail}): ` +
|
|
133
|
+
"refusing to serve the hosted transport without a completed contract-revision handshake");
|
|
134
|
+
}
|
|
135
|
+
// The handshake's diagnostics are its entire value — which side drifted, which
|
|
136
|
+
// revision each side carries — and contain no secrets by construction, so they
|
|
137
|
+
// go to stderr in full before the throw that main()'s sanitized reporter sees.
|
|
138
|
+
function fail(message) {
|
|
139
|
+
console.error(`contract-revision handshake failed: ${message}`);
|
|
140
|
+
throw new Error(message);
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=contract-handshake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract-handshake.js","sourceRoot":"","sources":["../src/contract-handshake.ts"],"names":[],"mappings":"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"}
|