@quantakrypto/mcp 0.1.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/HOSTING.md +184 -0
- package/README.md +227 -0
- package/dist/http.d.ts +120 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/http.js +339 -0
- package/dist/http.js.map +1 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +109 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +68 -0
- package/dist/protocol.js.map +1 -0
- package/dist/rules.d.ts +48 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +143 -0
- package/dist/rules.js.map +1 -0
- package/dist/server.d.ts +56 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +132 -0
- package/dist/server.js.map +1 -0
- package/dist/stdio.d.ts +23 -0
- package/dist/stdio.d.ts.map +1 -0
- package/dist/stdio.js +89 -0
- package/dist/stdio.js.map +1 -0
- package/dist/tools.d.ts +38 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +421 -0
- package/dist/tools.js.map +1 -0
- package/examples/README.md +36 -0
- package/examples/transcript.jsonl +13 -0
- package/package.json +47 -0
package/dist/rules.d.ts
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule resolution for {@link explain_finding}.
|
|
3
|
+
*
|
|
4
|
+
* Core's detectors are coarse-grained: a single {@link Detector} (e.g. the
|
|
5
|
+
* `crypto-libs` detector) emits many distinct `ruleId`s (`forge-rsa-keygen`,
|
|
6
|
+
* `elliptic-ec`, `node-rsa`, …) that do NOT share the detector's `id` as a
|
|
7
|
+
* prefix. The old MCP `explain_finding` matched `ruleId` against `detector.id`
|
|
8
|
+
* by prefix and therefore returned "no matching detector" for every real
|
|
9
|
+
* library finding (P0-5).
|
|
10
|
+
*
|
|
11
|
+
* This module resolves a finding's `ruleId` against core's *actual* detector
|
|
12
|
+
* set: a curated index maps each canonical core ruleId to the detector that
|
|
13
|
+
* emits it plus the classical algorithm family it concerns, and the detector's
|
|
14
|
+
* human description is looked up live from {@link defaultRegistry}/{@link
|
|
15
|
+
* detectors}. Unknown rules fall back to a prefix match against the detector id
|
|
16
|
+
* space, and finally to the algorithm remediation. Pure and synchronous — no
|
|
17
|
+
* I/O — so it is directly unit-testable.
|
|
18
|
+
*/
|
|
19
|
+
import type { AlgorithmFamily } from "@quantakrypto/core";
|
|
20
|
+
/** A resolved rule: the detector it belongs to (if any) and its algorithm. */
|
|
21
|
+
export interface ResolvedRule {
|
|
22
|
+
/** The rule id that was looked up (echoed for convenience). */
|
|
23
|
+
ruleId: string;
|
|
24
|
+
/** The detector that emits this rule, when one could be resolved. */
|
|
25
|
+
detector?: {
|
|
26
|
+
id: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
/** The classical algorithm family the rule concerns, when known. */
|
|
30
|
+
algorithm?: AlgorithmFamily;
|
|
31
|
+
/** How the match was made — useful for tests and diagnostics. */
|
|
32
|
+
via: "index" | "detector-id" | "prefix" | "unresolved";
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Resolve a finding's `ruleId` to its detector and algorithm.
|
|
36
|
+
*
|
|
37
|
+
* Resolution order:
|
|
38
|
+
* 1. Curated {@link RULE_INDEX} — the canonical mapping for every core rule.
|
|
39
|
+
* 2. Exact detector id (a rule that IS a detector id, e.g. a future 1:1 rule).
|
|
40
|
+
* 3. Prefix against the detector id space (`node-crypto-*`, `pem-*`, …).
|
|
41
|
+
* 4. Unresolved — caller falls back to the algorithm remediation.
|
|
42
|
+
*
|
|
43
|
+
* Pure: depends only on its argument and the static core detector set.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveRule(ruleId: string): ResolvedRule;
|
|
46
|
+
/** Exposed for tests: the set of canonical rule ids the index knows. */
|
|
47
|
+
export declare const KNOWN_RULE_IDS: readonly string[];
|
|
48
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAY,MAAM,oBAAoB,CAAC;AAEpE,8EAA8E;AAC9E,MAAM,WAAW,YAAY;IAC3B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,oEAAoE;IACpE,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,iEAAiE;IACjE,GAAG,EAAE,OAAO,GAAG,aAAa,GAAG,QAAQ,GAAG,YAAY,CAAC;CACxD;AA8ED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CA2CxD;AAED,wEAAwE;AACxE,eAAO,MAAM,cAAc,EAAE,SAAS,MAAM,EAA4B,CAAC"}
|
package/dist/rules.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule resolution for {@link explain_finding}.
|
|
3
|
+
*
|
|
4
|
+
* Core's detectors are coarse-grained: a single {@link Detector} (e.g. the
|
|
5
|
+
* `crypto-libs` detector) emits many distinct `ruleId`s (`forge-rsa-keygen`,
|
|
6
|
+
* `elliptic-ec`, `node-rsa`, …) that do NOT share the detector's `id` as a
|
|
7
|
+
* prefix. The old MCP `explain_finding` matched `ruleId` against `detector.id`
|
|
8
|
+
* by prefix and therefore returned "no matching detector" for every real
|
|
9
|
+
* library finding (P0-5).
|
|
10
|
+
*
|
|
11
|
+
* This module resolves a finding's `ruleId` against core's *actual* detector
|
|
12
|
+
* set: a curated index maps each canonical core ruleId to the detector that
|
|
13
|
+
* emits it plus the classical algorithm family it concerns, and the detector's
|
|
14
|
+
* human description is looked up live from {@link defaultRegistry}/{@link
|
|
15
|
+
* detectors}. Unknown rules fall back to a prefix match against the detector id
|
|
16
|
+
* space, and finally to the algorithm remediation. Pure and synchronous — no
|
|
17
|
+
* I/O — so it is directly unit-testable.
|
|
18
|
+
*/
|
|
19
|
+
import { defaultRegistry, detectors } from "@quantakrypto/core";
|
|
20
|
+
/**
|
|
21
|
+
* Canonical core ruleId → { detector id, algorithm } index.
|
|
22
|
+
*
|
|
23
|
+
* Mirrors the `ruleId`s emitted by the built-in detectors in
|
|
24
|
+
* `@quantakrypto/core` (see `packages/core/src/detectors/*` and `dependencies.ts`).
|
|
25
|
+
* The `detectorId` values reference real detector ids in {@link
|
|
26
|
+
* defaultRegistry}; the descriptions are not duplicated here — they are read
|
|
27
|
+
* live from the registry so they never drift. Algorithms that a single rule
|
|
28
|
+
* can span (e.g. `jsrsasign-*` covers RSA *and* EC) are recorded as
|
|
29
|
+
* `"unknown"`, matching core's own classification.
|
|
30
|
+
*/
|
|
31
|
+
const RULE_INDEX = {
|
|
32
|
+
// node-crypto detector
|
|
33
|
+
"node-crypto-keygen": { detectorId: "node-crypto", algorithm: "unknown" },
|
|
34
|
+
"node-crypto-sign": { detectorId: "node-crypto", algorithm: "unknown" },
|
|
35
|
+
"node-crypto-sign-oneshot": { detectorId: "node-crypto", algorithm: "unknown" },
|
|
36
|
+
"node-crypto-dh": { detectorId: "node-crypto", algorithm: "DH" },
|
|
37
|
+
"node-crypto-dh-modp": { detectorId: "node-crypto", algorithm: "DH" },
|
|
38
|
+
"node-crypto-ecdh": { detectorId: "node-crypto", algorithm: "ECDH" },
|
|
39
|
+
"node-crypto-rsa-encrypt": { detectorId: "node-crypto", algorithm: "RSA" },
|
|
40
|
+
"node-crypto-dh-keyobject": { detectorId: "node-crypto", algorithm: "DH" },
|
|
41
|
+
// webcrypto detector
|
|
42
|
+
"webcrypto-classical": { detectorId: "webcrypto", algorithm: "unknown" },
|
|
43
|
+
// crypto-libs detector (the library findings P0-5 was about)
|
|
44
|
+
"forge-rsa-keygen": { detectorId: "crypto-libs", algorithm: "RSA" },
|
|
45
|
+
"forge-ed25519": { detectorId: "crypto-libs", algorithm: "EdDSA" },
|
|
46
|
+
"elliptic-ec": { detectorId: "crypto-libs", algorithm: "ECDSA" },
|
|
47
|
+
"secp256k1-usage": { detectorId: "crypto-libs", algorithm: "ECDSA" },
|
|
48
|
+
"jsrsasign-keygen": { detectorId: "crypto-libs", algorithm: "unknown" },
|
|
49
|
+
"jsrsasign-sign": { detectorId: "crypto-libs", algorithm: "unknown" },
|
|
50
|
+
"node-rsa": { detectorId: "crypto-libs", algorithm: "RSA" },
|
|
51
|
+
// jwt-jose detector
|
|
52
|
+
"jwt-classical-alg": { detectorId: "jwt-jose", algorithm: "unknown" },
|
|
53
|
+
"jose-ecdh-es": { detectorId: "jwt-jose", algorithm: "ECDH" },
|
|
54
|
+
// tls-config detector
|
|
55
|
+
"tls-legacy-version": { detectorId: "tls-config", algorithm: "unknown" },
|
|
56
|
+
"tls-reject-unauthorized": { detectorId: "tls-config", algorithm: "unknown" },
|
|
57
|
+
"tls-weak-cipher": { detectorId: "tls-config", algorithm: "unknown" },
|
|
58
|
+
// ssh-cert detector
|
|
59
|
+
"ssh-public-key": { detectorId: "ssh-cert", algorithm: "unknown" },
|
|
60
|
+
"cert-signature-algorithm": { detectorId: "ssh-cert", algorithm: "unknown" },
|
|
61
|
+
// pem-material detector
|
|
62
|
+
"pem-rsa-private-key": { detectorId: "pem-material", algorithm: "RSA" },
|
|
63
|
+
"pem-ec-private-key": { detectorId: "pem-material", algorithm: "ECDSA" },
|
|
64
|
+
"pem-dsa-private-key": { detectorId: "pem-material", algorithm: "DSA" },
|
|
65
|
+
"pem-openssh-private-key": { detectorId: "pem-material", algorithm: "unknown" },
|
|
66
|
+
"pem-pgp-private-key": { detectorId: "pem-material", algorithm: "unknown" },
|
|
67
|
+
"pem-pgp-message": { detectorId: "pem-material", algorithm: "unknown" },
|
|
68
|
+
"pem-pkcs8-private-key": { detectorId: "pem-material", algorithm: "unknown" },
|
|
69
|
+
"pem-certificate": { detectorId: "pem-material", algorithm: "unknown" },
|
|
70
|
+
// dependency scanner (not a Detector, handled by scan()); no registry entry.
|
|
71
|
+
"dep-vulnerable": { detectorId: "dep-vulnerable", algorithm: "unknown" },
|
|
72
|
+
};
|
|
73
|
+
/** Build a fast id → Detector lookup over the active detector set. */
|
|
74
|
+
function detectorMap() {
|
|
75
|
+
const map = new Map();
|
|
76
|
+
// Prefer the registry; fall back to the exported `detectors` array.
|
|
77
|
+
const all = (() => {
|
|
78
|
+
try {
|
|
79
|
+
return defaultRegistry.all();
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return detectors;
|
|
83
|
+
}
|
|
84
|
+
})();
|
|
85
|
+
for (const d of all)
|
|
86
|
+
map.set(d.id, d);
|
|
87
|
+
return map;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolve a finding's `ruleId` to its detector and algorithm.
|
|
91
|
+
*
|
|
92
|
+
* Resolution order:
|
|
93
|
+
* 1. Curated {@link RULE_INDEX} — the canonical mapping for every core rule.
|
|
94
|
+
* 2. Exact detector id (a rule that IS a detector id, e.g. a future 1:1 rule).
|
|
95
|
+
* 3. Prefix against the detector id space (`node-crypto-*`, `pem-*`, …).
|
|
96
|
+
* 4. Unresolved — caller falls back to the algorithm remediation.
|
|
97
|
+
*
|
|
98
|
+
* Pure: depends only on its argument and the static core detector set.
|
|
99
|
+
*/
|
|
100
|
+
export function resolveRule(ruleId) {
|
|
101
|
+
const id = ruleId.trim();
|
|
102
|
+
const detectorsById = detectorMap();
|
|
103
|
+
// 1. Curated index — the authoritative path for known core rules.
|
|
104
|
+
const indexed = RULE_INDEX[id];
|
|
105
|
+
if (indexed) {
|
|
106
|
+
const det = detectorsById.get(indexed.detectorId);
|
|
107
|
+
return {
|
|
108
|
+
ruleId: id,
|
|
109
|
+
detector: det ? { id: det.id, description: det.description } : undefined,
|
|
110
|
+
algorithm: indexed.algorithm,
|
|
111
|
+
via: "index",
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// 2. Exact detector id.
|
|
115
|
+
const exact = detectorsById.get(id);
|
|
116
|
+
if (exact) {
|
|
117
|
+
return {
|
|
118
|
+
ruleId: id,
|
|
119
|
+
detector: { id: exact.id, description: exact.description },
|
|
120
|
+
via: "detector-id",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// 3. Longest-prefix detector id (e.g. `node-crypto-foo` → `node-crypto`).
|
|
124
|
+
let best;
|
|
125
|
+
for (const det of detectorsById.values()) {
|
|
126
|
+
if (id === det.id || id.startsWith(`${det.id}-`)) {
|
|
127
|
+
if (!best || det.id.length > best.id.length)
|
|
128
|
+
best = det;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
if (best) {
|
|
132
|
+
return {
|
|
133
|
+
ruleId: id,
|
|
134
|
+
detector: { id: best.id, description: best.description },
|
|
135
|
+
via: "prefix",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// 4. Unresolved.
|
|
139
|
+
return { ruleId: id, via: "unresolved" };
|
|
140
|
+
}
|
|
141
|
+
/** Exposed for tests: the set of canonical rule ids the index knows. */
|
|
142
|
+
export const KNOWN_RULE_IDS = Object.keys(RULE_INDEX);
|
|
143
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../src/rules.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport-agnostic MCP server core.
|
|
3
|
+
*
|
|
4
|
+
* {@link McpServer} owns a tool registry and a single pure entry point,
|
|
5
|
+
* {@link McpServer.handle}, that turns one JSON-RPC message into one JSON-RPC
|
|
6
|
+
* response (or `null` for notifications, which get no reply). It performs no
|
|
7
|
+
* I/O whatsoever — the stdio and http transports feed it parsed messages and
|
|
8
|
+
* serialize whatever it returns. This makes the whole protocol surface
|
|
9
|
+
* unit-testable by calling `handle` directly.
|
|
10
|
+
*/
|
|
11
|
+
import type { JsonRpcResponse, ToolDefinition, ToolDescriptor } from "./protocol.js";
|
|
12
|
+
/** Identifying info advertised to clients during `initialize`. */
|
|
13
|
+
export interface ServerInfo {
|
|
14
|
+
name: string;
|
|
15
|
+
version: string;
|
|
16
|
+
}
|
|
17
|
+
export interface McpServerOptions {
|
|
18
|
+
/** Server identity returned in the `initialize` handshake. */
|
|
19
|
+
info: ServerInfo;
|
|
20
|
+
/** Optional human-readable instructions surfaced to the client. */
|
|
21
|
+
instructions?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A minimal, spec-faithful MCP server. Register tools with {@link registerTool}
|
|
25
|
+
* and drive it one message at a time with {@link handle}.
|
|
26
|
+
*/
|
|
27
|
+
export declare class McpServer {
|
|
28
|
+
private readonly tools;
|
|
29
|
+
private readonly info;
|
|
30
|
+
private readonly instructions?;
|
|
31
|
+
/** Set once `initialize` has been received; informational only. */
|
|
32
|
+
private initialized;
|
|
33
|
+
constructor(options: McpServerOptions);
|
|
34
|
+
/** Register a tool. Throws if a tool with the same name already exists. */
|
|
35
|
+
registerTool(tool: ToolDefinition): this;
|
|
36
|
+
/** The public descriptors of all registered tools (sorted by name). */
|
|
37
|
+
listTools(): ToolDescriptor[];
|
|
38
|
+
/** Whether the `initialize` handshake has completed. */
|
|
39
|
+
get isInitialized(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Process a single JSON-RPC message.
|
|
42
|
+
*
|
|
43
|
+
* @param message An already-parsed JSON value (the transport handles framing
|
|
44
|
+
* and `JSON.parse`). May be malformed; this method validates it.
|
|
45
|
+
* @returns The response to send back, or `null` when no reply is due
|
|
46
|
+
* (notifications, or an unparseable notification-shaped message).
|
|
47
|
+
*/
|
|
48
|
+
handle(message: unknown): Promise<JsonRpcResponse | null>;
|
|
49
|
+
/** Route a request to the right handler. Throws {@link RpcError} on failure. */
|
|
50
|
+
private dispatch;
|
|
51
|
+
/** Build the `initialize` result. */
|
|
52
|
+
private onInitialize;
|
|
53
|
+
/** Validate params and execute a tool for `tools/call`. */
|
|
54
|
+
private onToolsCall;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAWH,OAAO,KAAK,EAEV,eAAe,EACf,cAAc,EACd,cAAc,EAEf,MAAM,eAAe,CAAC;AAEvB,kEAAkE;AAClE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,8DAA8D;IAC9D,IAAI,EAAE,UAAU,CAAC;IACjB,mEAAmE;IACnE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;GAGG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAqC;IAC3D,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAa;IAClC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAS;IACvC,mEAAmE;IACnE,OAAO,CAAC,WAAW,CAAS;gBAEhB,OAAO,EAAE,gBAAgB;IAKrC,2EAA2E;IAC3E,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,IAAI;IAQxC,uEAAuE;IACvE,SAAS,IAAI,cAAc,EAAE;IAM7B,wDAAwD;IACxD,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED;;;;;;;OAOG;IACG,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAyB/D,gFAAgF;YAClE,QAAQ;IAkBtB,qCAAqC;IACrC,OAAO,CAAC,YAAY;IAcpB,2DAA2D;YAC7C,WAAW;CAuB1B"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport-agnostic MCP server core.
|
|
3
|
+
*
|
|
4
|
+
* {@link McpServer} owns a tool registry and a single pure entry point,
|
|
5
|
+
* {@link McpServer.handle}, that turns one JSON-RPC message into one JSON-RPC
|
|
6
|
+
* response (or `null` for notifications, which get no reply). It performs no
|
|
7
|
+
* I/O whatsoever — the stdio and http transports feed it parsed messages and
|
|
8
|
+
* serialize whatever it returns. This makes the whole protocol surface
|
|
9
|
+
* unit-testable by calling `handle` directly.
|
|
10
|
+
*/
|
|
11
|
+
import { ErrorCode, MCP_PROTOCOL_VERSION, RpcError, isJsonRpcRequestLike, isNotification, makeFailure, makeSuccess, } from "./protocol.js";
|
|
12
|
+
/**
|
|
13
|
+
* A minimal, spec-faithful MCP server. Register tools with {@link registerTool}
|
|
14
|
+
* and drive it one message at a time with {@link handle}.
|
|
15
|
+
*/
|
|
16
|
+
export class McpServer {
|
|
17
|
+
tools = new Map();
|
|
18
|
+
info;
|
|
19
|
+
instructions;
|
|
20
|
+
/** Set once `initialize` has been received; informational only. */
|
|
21
|
+
initialized = false;
|
|
22
|
+
constructor(options) {
|
|
23
|
+
this.info = options.info;
|
|
24
|
+
this.instructions = options.instructions;
|
|
25
|
+
}
|
|
26
|
+
/** Register a tool. Throws if a tool with the same name already exists. */
|
|
27
|
+
registerTool(tool) {
|
|
28
|
+
if (this.tools.has(tool.name)) {
|
|
29
|
+
throw new Error(`duplicate tool registration: ${tool.name}`);
|
|
30
|
+
}
|
|
31
|
+
this.tools.set(tool.name, tool);
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
/** The public descriptors of all registered tools (sorted by name). */
|
|
35
|
+
listTools() {
|
|
36
|
+
return [...this.tools.values()]
|
|
37
|
+
.map(({ name, description, inputSchema }) => ({ name, description, inputSchema }))
|
|
38
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
39
|
+
}
|
|
40
|
+
/** Whether the `initialize` handshake has completed. */
|
|
41
|
+
get isInitialized() {
|
|
42
|
+
return this.initialized;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Process a single JSON-RPC message.
|
|
46
|
+
*
|
|
47
|
+
* @param message An already-parsed JSON value (the transport handles framing
|
|
48
|
+
* and `JSON.parse`). May be malformed; this method validates it.
|
|
49
|
+
* @returns The response to send back, or `null` when no reply is due
|
|
50
|
+
* (notifications, or an unparseable notification-shaped message).
|
|
51
|
+
*/
|
|
52
|
+
async handle(message) {
|
|
53
|
+
if (!isJsonRpcRequestLike(message)) {
|
|
54
|
+
// Not a valid request object. We can't know its id, so reply with null id.
|
|
55
|
+
return makeFailure(null, ErrorCode.InvalidRequest, "invalid JSON-RPC request");
|
|
56
|
+
}
|
|
57
|
+
const req = message;
|
|
58
|
+
const notification = isNotification(req);
|
|
59
|
+
const id = notification ? null : (req.id ?? null);
|
|
60
|
+
try {
|
|
61
|
+
const result = await this.dispatch(req);
|
|
62
|
+
// Notifications never receive a response, even on success.
|
|
63
|
+
if (notification)
|
|
64
|
+
return null;
|
|
65
|
+
return makeSuccess(id, result);
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
if (notification)
|
|
69
|
+
return null; // swallow errors from notifications
|
|
70
|
+
if (err instanceof RpcError) {
|
|
71
|
+
return makeFailure(id, err.code, err.message, err.data);
|
|
72
|
+
}
|
|
73
|
+
const messageText = err instanceof Error ? err.message : String(err);
|
|
74
|
+
return makeFailure(id, ErrorCode.InternalError, messageText);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Route a request to the right handler. Throws {@link RpcError} on failure. */
|
|
78
|
+
async dispatch(req) {
|
|
79
|
+
switch (req.method) {
|
|
80
|
+
case "initialize":
|
|
81
|
+
return this.onInitialize();
|
|
82
|
+
case "notifications/initialized":
|
|
83
|
+
this.initialized = true;
|
|
84
|
+
return {}; // notification → result ignored by handle()
|
|
85
|
+
case "ping":
|
|
86
|
+
return {};
|
|
87
|
+
case "tools/list":
|
|
88
|
+
return { tools: this.listTools() };
|
|
89
|
+
case "tools/call":
|
|
90
|
+
return this.onToolsCall(req.params);
|
|
91
|
+
default:
|
|
92
|
+
throw new RpcError(ErrorCode.MethodNotFound, `method not found: ${req.method}`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** Build the `initialize` result. */
|
|
96
|
+
onInitialize() {
|
|
97
|
+
this.initialized = true;
|
|
98
|
+
const result = {
|
|
99
|
+
protocolVersion: MCP_PROTOCOL_VERSION,
|
|
100
|
+
capabilities: {
|
|
101
|
+
// We expose tools; our list is static, so listChanged is false.
|
|
102
|
+
tools: { listChanged: false },
|
|
103
|
+
},
|
|
104
|
+
serverInfo: { name: this.info.name, version: this.info.version },
|
|
105
|
+
};
|
|
106
|
+
if (this.instructions)
|
|
107
|
+
result.instructions = this.instructions;
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
/** Validate params and execute a tool for `tools/call`. */
|
|
111
|
+
async onToolsCall(params) {
|
|
112
|
+
if (typeof params !== "object" || params === null || Array.isArray(params)) {
|
|
113
|
+
throw new RpcError(ErrorCode.InvalidParams, "tools/call requires an object params");
|
|
114
|
+
}
|
|
115
|
+
const { name, arguments: args } = params;
|
|
116
|
+
if (typeof name !== "string" || name.length === 0) {
|
|
117
|
+
throw new RpcError(ErrorCode.InvalidParams, "tools/call requires a 'name' string");
|
|
118
|
+
}
|
|
119
|
+
const tool = this.tools.get(name);
|
|
120
|
+
if (!tool) {
|
|
121
|
+
throw new RpcError(ErrorCode.InvalidParams, `unknown tool: ${name}`);
|
|
122
|
+
}
|
|
123
|
+
if (args !== undefined && (typeof args !== "object" || args === null || Array.isArray(args))) {
|
|
124
|
+
throw new RpcError(ErrorCode.InvalidParams, "tool 'arguments' must be an object");
|
|
125
|
+
}
|
|
126
|
+
const toolArgs = (args ?? {});
|
|
127
|
+
// Tool-level failures are reported as isError results, not protocol errors,
|
|
128
|
+
// so the model can read and react to them. Only unexpected throws bubble up.
|
|
129
|
+
return tool.handler(toolArgs);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/stdio.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* quantakrypto-mcp — stdio transport.
|
|
4
|
+
*
|
|
5
|
+
* MCP's stdio transport is newline-delimited JSON: exactly one JSON-RPC 2.0
|
|
6
|
+
* message per line on stdin, one per line on stdout. (This is NOT HTTP-style
|
|
7
|
+
* Content-Length framing.) This module reads stdin line-by-line with
|
|
8
|
+
* {@link node:readline}, hands each parsed message to {@link McpServer.handle},
|
|
9
|
+
* and writes any response back as a single line on stdout. Diagnostics go to
|
|
10
|
+
* stderr so they never corrupt the protocol stream.
|
|
11
|
+
*
|
|
12
|
+
* Run as the `quantakrypto-mcp` bin, or `node dist/stdio.js`.
|
|
13
|
+
*/
|
|
14
|
+
import type { McpServer } from "./server.js";
|
|
15
|
+
/** Serialize a value as one newline-terminated JSON line to stdout. */
|
|
16
|
+
declare function writeLine(value: unknown): void;
|
|
17
|
+
/**
|
|
18
|
+
* Attach a line-delimited JSON-RPC loop to the given streams. Exposed (rather
|
|
19
|
+
* than only run on import) so it can be reused or tested with custom streams.
|
|
20
|
+
*/
|
|
21
|
+
export declare function runStdioServer(server: McpServer, input?: NodeJS.ReadableStream, output?: NodeJS.WritableStream): void;
|
|
22
|
+
export { writeLine };
|
|
23
|
+
//# sourceMappingURL=stdio.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;GAWG;AASH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,uEAAuE;AACvE,iBAAS,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAEvC;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,EACjB,KAAK,GAAE,MAAM,CAAC,cAA8B,EAC5C,MAAM,GAAE,MAAM,CAAC,cAA+B,GAC7C,IAAI,CAkCN;AA6BD,OAAO,EAAE,SAAS,EAAE,CAAC"}
|
package/dist/stdio.js
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* quantakrypto-mcp — stdio transport.
|
|
4
|
+
*
|
|
5
|
+
* MCP's stdio transport is newline-delimited JSON: exactly one JSON-RPC 2.0
|
|
6
|
+
* message per line on stdin, one per line on stdout. (This is NOT HTTP-style
|
|
7
|
+
* Content-Length framing.) This module reads stdin line-by-line with
|
|
8
|
+
* {@link node:readline}, hands each parsed message to {@link McpServer.handle},
|
|
9
|
+
* and writes any response back as a single line on stdout. Diagnostics go to
|
|
10
|
+
* stderr so they never corrupt the protocol stream.
|
|
11
|
+
*
|
|
12
|
+
* Run as the `quantakrypto-mcp` bin, or `node dist/stdio.js`.
|
|
13
|
+
*/
|
|
14
|
+
import { createInterface } from "node:readline";
|
|
15
|
+
import process from "node:process";
|
|
16
|
+
import { realpathSync } from "node:fs";
|
|
17
|
+
import { fileURLToPath } from "node:url";
|
|
18
|
+
import { createQuantakryptoServer } from "./index.js";
|
|
19
|
+
import { ErrorCode, makeFailure } from "./protocol.js";
|
|
20
|
+
/** Serialize a value as one newline-terminated JSON line to stdout. */
|
|
21
|
+
function writeLine(value) {
|
|
22
|
+
process.stdout.write(JSON.stringify(value) + "\n");
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Attach a line-delimited JSON-RPC loop to the given streams. Exposed (rather
|
|
26
|
+
* than only run on import) so it can be reused or tested with custom streams.
|
|
27
|
+
*/
|
|
28
|
+
export function runStdioServer(server, input = process.stdin, output = process.stdout) {
|
|
29
|
+
const rl = createInterface({ input, crlfDelay: Infinity });
|
|
30
|
+
rl.on("line", (raw) => {
|
|
31
|
+
const line = raw.trim();
|
|
32
|
+
if (line.length === 0)
|
|
33
|
+
return; // tolerate blank lines / keepalives
|
|
34
|
+
let parsed;
|
|
35
|
+
try {
|
|
36
|
+
parsed = JSON.parse(line);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Parse error: we have no id, so reply with a null-id error per JSON-RPC.
|
|
40
|
+
output.write(JSON.stringify(makeFailure(null, ErrorCode.ParseError, "parse error")) + "\n");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// Process asynchronously; responses are written as they resolve. Ordering
|
|
44
|
+
// within a single client is preserved because handlers here are fast and
|
|
45
|
+
// the event loop drains line events in order.
|
|
46
|
+
void server
|
|
47
|
+
.handle(parsed)
|
|
48
|
+
.then((response) => {
|
|
49
|
+
if (response !== null)
|
|
50
|
+
output.write(JSON.stringify(response) + "\n");
|
|
51
|
+
})
|
|
52
|
+
.catch((err) => {
|
|
53
|
+
// Defensive: handle() already catches, but never let a rejection escape.
|
|
54
|
+
const messageText = err instanceof Error ? err.message : String(err);
|
|
55
|
+
process.stderr.write(`quantakrypto-mcp internal error: ${messageText}\n`);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
rl.on("close", () => {
|
|
59
|
+
process.exitCode = 0;
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
/** Entry point when executed directly (the bin / `node dist/stdio.js`). */
|
|
63
|
+
function main() {
|
|
64
|
+
const server = createQuantakryptoServer();
|
|
65
|
+
process.stderr.write(`quantakrypto MCP server (stdio) ready\n`);
|
|
66
|
+
runStdioServer(server);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* True when this module is the program's entry point. Resolves symlinks so the
|
|
70
|
+
* check also holds when launched via the `quantakrypto-mcp` bin shim in node_modules/.bin.
|
|
71
|
+
*/
|
|
72
|
+
function isMainModule() {
|
|
73
|
+
const argv1 = process.argv[1];
|
|
74
|
+
if (!argv1)
|
|
75
|
+
return false;
|
|
76
|
+
const thisPath = fileURLToPath(import.meta.url);
|
|
77
|
+
try {
|
|
78
|
+
return realpathSync(argv1) === realpathSync(thisPath);
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return argv1 === thisPath;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Only auto-run when invoked as a script, not when imported by tests.
|
|
85
|
+
if (isMainModule()) {
|
|
86
|
+
main();
|
|
87
|
+
}
|
|
88
|
+
export { writeLine };
|
|
89
|
+
//# sourceMappingURL=stdio.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdio.js","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"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"}
|
package/dist/tools.d.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* quantakrypto MCP tools, backed by {@link @quantakrypto/core}.
|
|
3
|
+
*
|
|
4
|
+
* Every tool returns an MCP {@link ToolResult} ({ content, isError? }). Because
|
|
5
|
+
* `@quantakrypto/core` is still partly stubbed (several functions throw "not
|
|
6
|
+
* implemented"), each handler runs core calls through {@link safe} so a missing
|
|
7
|
+
* implementation surfaces as a readable `isError` tool result instead of a
|
|
8
|
+
* protocol-level crash. When core lands, the tools work unchanged.
|
|
9
|
+
*/
|
|
10
|
+
import type { AlgorithmFamily, ScanResult } from "@quantakrypto/core";
|
|
11
|
+
import type { JsonSchema, ToolDefinition } from "./protocol.js";
|
|
12
|
+
/** Map a free-text algorithm string onto a known {@link AlgorithmFamily}. */
|
|
13
|
+
declare function normalizeAlgorithm(input: string): AlgorithmFamily;
|
|
14
|
+
/** Render a scan result as a compact human-readable summary. */
|
|
15
|
+
declare function summarizeScan(result: ScanResult): string;
|
|
16
|
+
/** Built-in PQC guidance used when core's remediation table is unavailable. */
|
|
17
|
+
declare function staticHybridAdvice(algorithm: AlgorithmFamily): string[];
|
|
18
|
+
/**
|
|
19
|
+
* Tools that read arbitrary filesystem paths. Disabled by default on the HTTP
|
|
20
|
+
* transport (see {@link ./http.ts}) because a hosted endpoint must not be an
|
|
21
|
+
* arbitrary-file-read oracle (security audit Q-01). The stdio transport, which
|
|
22
|
+
* trusts the local user, always exposes them.
|
|
23
|
+
*/
|
|
24
|
+
export declare const FS_TOOL_NAMES: readonly string[];
|
|
25
|
+
/** All quantakrypto MCP tools, in a stable order. */
|
|
26
|
+
export declare const quantakryptoTools: ToolDefinition[];
|
|
27
|
+
/** The core version these tools are built against (re-exported for diagnostics). */
|
|
28
|
+
export declare const CORE_VERSION = "0.1.0";
|
|
29
|
+
/** Exposed for tests and advanced callers. */
|
|
30
|
+
export declare const __test: {
|
|
31
|
+
normalizeAlgorithm: typeof normalizeAlgorithm;
|
|
32
|
+
summarizeScan: typeof summarizeScan;
|
|
33
|
+
staticHybridAdvice: typeof staticHybridAdvice;
|
|
34
|
+
};
|
|
35
|
+
/** Keep the schema type imported and referenced (documentation aid). */
|
|
36
|
+
export type ToolInputSchema = JsonSchema;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAUH,OAAO,KAAK,EACV,eAAe,EAGf,UAAU,EAEX,MAAM,oBAAoB,CAAC;AAG5B,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAc,MAAM,eAAe,CAAC;AAmC5E,6EAA6E;AAC7E,iBAAS,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAkB1D;AAED,gEAAgE;AAChE,iBAAS,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAmCjD;AAyOD,+EAA+E;AAC/E,iBAAS,kBAAkB,CAAC,SAAS,EAAE,eAAe,GAAG,MAAM,EAAE,CA4BhE;AA8DD;;;;;GAKG;AACH,eAAO,MAAM,aAAa,EAAE,SAAS,MAAM,EAAuD,CAAC;AAEnG,qDAAqD;AACrD,eAAO,MAAM,iBAAiB,EAAE,cAAc,EAO7C,CAAC;AAEF,oFAAoF;AACpF,eAAO,MAAM,YAAY,UAAU,CAAC;AAEpC,8CAA8C;AAC9C,eAAO,MAAM,MAAM;;;;CAA4D,CAAC;AAEhF,wEAAwE;AACxE,MAAM,MAAM,eAAe,GAAG,UAAU,CAAC"}
|