@descryy/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/LICENSE +21 -0
- package/dist/action-handshake.d.ts +86 -0
- package/dist/action-handshake.d.ts.map +1 -0
- package/dist/action-handshake.js +144 -0
- package/dist/action-handshake.js.map +1 -0
- package/dist/bin/descry-mcp.d.ts +18 -0
- package/dist/bin/descry-mcp.d.ts.map +1 -0
- package/dist/bin/descry-mcp.js +54 -0
- package/dist/bin/descry-mcp.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/language-guess.d.ts +17 -0
- package/dist/language-guess.d.ts.map +1 -0
- package/dist/language-guess.js +43 -0
- package/dist/language-guess.js.map +1 -0
- package/dist/protocol.d.ts +91 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +127 -0
- package/dist/protocol.js.map +1 -0
- package/dist/registry.d.ts +72 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +136 -0
- package/dist/registry.js.map +1 -0
- package/dist/render.d.ts +275 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +333 -0
- package/dist/render.js.map +1 -0
- package/dist/server.d.ts +42 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +263 -0
- package/dist/server.js.map +1 -0
- package/dist/session.d.ts +164 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +302 -0
- package/dist/session.js.map +1 -0
- package/dist/tools/analyze.d.ts +124 -0
- package/dist/tools/analyze.d.ts.map +1 -0
- package/dist/tools/analyze.js +411 -0
- package/dist/tools/analyze.js.map +1 -0
- package/dist/tools/contracts.d.ts +105 -0
- package/dist/tools/contracts.d.ts.map +1 -0
- package/dist/tools/contracts.js +141 -0
- package/dist/tools/contracts.js.map +1 -0
- package/dist/tools/cross-pr.d.ts +137 -0
- package/dist/tools/cross-pr.d.ts.map +1 -0
- package/dist/tools/cross-pr.js +216 -0
- package/dist/tools/cross-pr.js.map +1 -0
- package/dist/tools/git-diff.d.ts +29 -0
- package/dist/tools/git-diff.d.ts.map +1 -0
- package/dist/tools/git-diff.js +117 -0
- package/dist/tools/git-diff.js.map +1 -0
- package/dist/tools/git-history.d.ts +27 -0
- package/dist/tools/git-history.d.ts.map +1 -0
- package/dist/tools/git-history.js +91 -0
- package/dist/tools/git-history.js.map +1 -0
- package/dist/tools/history.d.ts +63 -0
- package/dist/tools/history.d.ts.map +1 -0
- package/dist/tools/history.js +114 -0
- package/dist/tools/history.js.map +1 -0
- package/dist/tools/impact.d.ts +81 -0
- package/dist/tools/impact.d.ts.map +1 -0
- package/dist/tools/impact.js +229 -0
- package/dist/tools/impact.js.map +1 -0
- package/dist/tools/index.d.ts +33 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +47 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/kit.d.ts +183 -0
- package/dist/tools/kit.d.ts.map +1 -0
- package/dist/tools/kit.js +117 -0
- package/dist/tools/kit.js.map +1 -0
- package/dist/tools/link-workspace.d.ts +62 -0
- package/dist/tools/link-workspace.d.ts.map +1 -0
- package/dist/tools/link-workspace.js +156 -0
- package/dist/tools/link-workspace.js.map +1 -0
- package/dist/tools/lookup.d.ts +29 -0
- package/dist/tools/lookup.d.ts.map +1 -0
- package/dist/tools/lookup.js +73 -0
- package/dist/tools/lookup.js.map +1 -0
- package/dist/tools/pr-analysis.d.ts +89 -0
- package/dist/tools/pr-analysis.d.ts.map +1 -0
- package/dist/tools/pr-analysis.js +169 -0
- package/dist/tools/pr-analysis.js.map +1 -0
- package/dist/tools/propagation.d.ts +74 -0
- package/dist/tools/propagation.d.ts.map +1 -0
- package/dist/tools/propagation.js +194 -0
- package/dist/tools/propagation.js.map +1 -0
- package/dist/tools/questions.d.ts +80 -0
- package/dist/tools/questions.d.ts.map +1 -0
- package/dist/tools/questions.js +213 -0
- package/dist/tools/questions.js.map +1 -0
- package/dist/tools/refusal-fetch.d.ts +71 -0
- package/dist/tools/refusal-fetch.d.ts.map +1 -0
- package/dist/tools/refusal-fetch.js +137 -0
- package/dist/tools/refusal-fetch.js.map +1 -0
- package/dist/tools/scope.d.ts +60 -0
- package/dist/tools/scope.d.ts.map +1 -0
- package/dist/tools/scope.js +160 -0
- package/dist/tools/scope.js.map +1 -0
- package/dist/tools/similar-incidents.d.ts +105 -0
- package/dist/tools/similar-incidents.d.ts.map +1 -0
- package/dist/tools/similar-incidents.js +228 -0
- package/dist/tools/similar-incidents.js.map +1 -0
- package/dist/tools/validate.d.ts +103 -0
- package/dist/tools/validate.d.ts.map +1 -0
- package/dist/tools/validate.js +176 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/tools/verification-status.d.ts +68 -0
- package/dist/tools/verification-status.d.ts.map +1 -0
- package/dist/tools/verification-status.js +179 -0
- package/dist/tools/verification-status.js.map +1 -0
- package/dist/transport.d.ts +58 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +115 -0
- package/dist/transport.js.map +1 -0
- package/package.json +33 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Descry
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two-call action-tool handshake — `ai-tool-contract.md` §7, DEC-278's
|
|
3
|
+
* `action` class.
|
|
4
|
+
*
|
|
5
|
+
* G5, the action gate, does not run in MCP mode: no pipeline sits between an
|
|
6
|
+
* external model and this server, so there is no control flow to hold the
|
|
7
|
+
* gate. The handshake is what replaces it. An unconfirmed call to an
|
|
8
|
+
* `action`-class tool performs nothing and returns a token plus a
|
|
9
|
+
* plain-English description of what confirming would do; only a second call
|
|
10
|
+
* presenting that exact token performs anything, and it performs the
|
|
11
|
+
* arguments frozen when the token was minted — never whatever the second
|
|
12
|
+
* call happens to supply. **The gate holds without the client's
|
|
13
|
+
* cooperation, because the server owns both the decision and the state.**
|
|
14
|
+
*
|
|
15
|
+
* ## Generic by construction
|
|
16
|
+
*
|
|
17
|
+
* A tool takes part by declaring `class: "action"` and providing
|
|
18
|
+
* `describeAction`, nothing else — the mint/redeem/expiry/single-use
|
|
19
|
+
* machinery below is shared by every present and future action tool rather
|
|
20
|
+
* than reimplemented per tool. `describeAction` returning `undefined` means
|
|
21
|
+
* *this particular call* has nothing to confirm — `questions` called with no
|
|
22
|
+
* `answer` is a pure read of the open-question queue, and gating that behind
|
|
23
|
+
* a token would make the tool unable to answer the first question asked of
|
|
24
|
+
* it (the same failure §4 names for over-gating `stateful-read`).
|
|
25
|
+
*
|
|
26
|
+
* ## The seam with the server's dispatch loop
|
|
27
|
+
*
|
|
28
|
+
* `resolveActionDispatch` decides *before* the caller's budget timer starts:
|
|
29
|
+
* a mint has no real work to time out on (no I/O, no `tool.run`), so it must
|
|
30
|
+
* not be raced against `tools.md` §6's budget the way a real call is. The
|
|
31
|
+
* caller (`server.ts`) checks `decision.kind` — `"handshake"` renders via
|
|
32
|
+
* `render.ts`'s `renderHandshake` immediately; `"run"` proceeds exactly as
|
|
33
|
+
* an ungated call would, using `decision.args` (the original arguments for
|
|
34
|
+
* a `read`/`stateful-read` tool or a pass-through `action` call, or the
|
|
35
|
+
* arguments captured at mint time for a redeemed one).
|
|
36
|
+
*
|
|
37
|
+
* ## Token design, decided here because nothing upstream decides it
|
|
38
|
+
*
|
|
39
|
+
* - **Binds to call 1's exact arguments.** The token is not a bare
|
|
40
|
+
* capability — it is minted together with a stored copy of the arguments
|
|
41
|
+
* that produced its `willDo`. Redeeming it runs *that* stored copy,
|
|
42
|
+
* ignoring whatever the redeeming call supplies alongside the token. A
|
|
43
|
+
* client cannot describe one action and, on confirmation, have a
|
|
44
|
+
* different one performed.
|
|
45
|
+
* - **Single-use.** A token is deleted from the store the moment it is
|
|
46
|
+
* looked up for redemption, whether or not that redemption goes on to
|
|
47
|
+
* succeed — replay is not a retry, and a caller that wants to do the same
|
|
48
|
+
* thing twice must be told about it twice.
|
|
49
|
+
* - **Five-minute expiry.** A starting value, not a measurement (no
|
|
50
|
+
* confirmation flow has run yet to measure a real gap between mint and
|
|
51
|
+
* confirm) — long enough for a human or a model to read `willDo` and
|
|
52
|
+
* decide, short enough that a token is not a standing credential.
|
|
53
|
+
* - **Unknown, expired or wrong-tool token → `ToolInputError`, never a
|
|
54
|
+
* silent no-op.** This is an argument the caller can fix (ask again,
|
|
55
|
+
* without `confirmToken`, for a fresh one) so it takes the same path as
|
|
56
|
+
* any other bad argument — a tool result the caller can read and act on,
|
|
57
|
+
* not a protocol error and not a quiet success that performed nothing.
|
|
58
|
+
* - **Session-scoped.** One MCP server process serves exactly one `Session`
|
|
59
|
+
* (`McpServerOptions` takes a single session, not a pool), so the store is
|
|
60
|
+
* keyed on the `Session` instance via a `WeakMap` rather than kept as
|
|
61
|
+
* module-global mutable state — the scoping a second session would need is
|
|
62
|
+
* already correct without anything having to change if one is ever added.
|
|
63
|
+
*/
|
|
64
|
+
import { type ToolContext, type ToolDefinition } from "./tools/kit.ts";
|
|
65
|
+
export type ActionDispatch = {
|
|
66
|
+
readonly kind: "run";
|
|
67
|
+
readonly args: Record<string, unknown>;
|
|
68
|
+
} | {
|
|
69
|
+
readonly kind: "handshake";
|
|
70
|
+
readonly token: string;
|
|
71
|
+
readonly willDo: string;
|
|
72
|
+
};
|
|
73
|
+
/**
|
|
74
|
+
* Decides what a call to `tool` should do, before any budget timer starts.
|
|
75
|
+
*
|
|
76
|
+
* `read` and `stateful-read` tools, and an `action` tool whose
|
|
77
|
+
* `describeAction` says this call has nothing to confirm, resolve to
|
|
78
|
+
* `{ kind: "run", args }` with `args` unchanged. An unconfirmed call to a
|
|
79
|
+
* gated `action` tool resolves to `{ kind: "handshake", token, willDo }` —
|
|
80
|
+
* the caller must render this directly and must not call `tool.run`. A
|
|
81
|
+
* confirmed call (an `args.confirmToken` that redeems) resolves to `{ kind:
|
|
82
|
+
* "run", args }` with `args` replaced by the arguments captured at mint
|
|
83
|
+
* time.
|
|
84
|
+
*/
|
|
85
|
+
export declare function resolveActionDispatch(tool: ToolDefinition, args: Record<string, unknown>, ctx: ToolContext): Promise<ActionDispatch>;
|
|
86
|
+
//# sourceMappingURL=action-handshake.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-handshake.d.ts","sourceRoot":"","sources":["../src/action-handshake.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8DG;AAIH,OAAO,EAAkB,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAiCvF,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAChE;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpF;;;;;;;;;;;GAWG;AACH,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,GAAG,EAAE,WAAW,GACf,OAAO,CAAC,cAAc,CAAC,CAazB"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The two-call action-tool handshake — `ai-tool-contract.md` §7, DEC-278's
|
|
3
|
+
* `action` class.
|
|
4
|
+
*
|
|
5
|
+
* G5, the action gate, does not run in MCP mode: no pipeline sits between an
|
|
6
|
+
* external model and this server, so there is no control flow to hold the
|
|
7
|
+
* gate. The handshake is what replaces it. An unconfirmed call to an
|
|
8
|
+
* `action`-class tool performs nothing and returns a token plus a
|
|
9
|
+
* plain-English description of what confirming would do; only a second call
|
|
10
|
+
* presenting that exact token performs anything, and it performs the
|
|
11
|
+
* arguments frozen when the token was minted — never whatever the second
|
|
12
|
+
* call happens to supply. **The gate holds without the client's
|
|
13
|
+
* cooperation, because the server owns both the decision and the state.**
|
|
14
|
+
*
|
|
15
|
+
* ## Generic by construction
|
|
16
|
+
*
|
|
17
|
+
* A tool takes part by declaring `class: "action"` and providing
|
|
18
|
+
* `describeAction`, nothing else — the mint/redeem/expiry/single-use
|
|
19
|
+
* machinery below is shared by every present and future action tool rather
|
|
20
|
+
* than reimplemented per tool. `describeAction` returning `undefined` means
|
|
21
|
+
* *this particular call* has nothing to confirm — `questions` called with no
|
|
22
|
+
* `answer` is a pure read of the open-question queue, and gating that behind
|
|
23
|
+
* a token would make the tool unable to answer the first question asked of
|
|
24
|
+
* it (the same failure §4 names for over-gating `stateful-read`).
|
|
25
|
+
*
|
|
26
|
+
* ## The seam with the server's dispatch loop
|
|
27
|
+
*
|
|
28
|
+
* `resolveActionDispatch` decides *before* the caller's budget timer starts:
|
|
29
|
+
* a mint has no real work to time out on (no I/O, no `tool.run`), so it must
|
|
30
|
+
* not be raced against `tools.md` §6's budget the way a real call is. The
|
|
31
|
+
* caller (`server.ts`) checks `decision.kind` — `"handshake"` renders via
|
|
32
|
+
* `render.ts`'s `renderHandshake` immediately; `"run"` proceeds exactly as
|
|
33
|
+
* an ungated call would, using `decision.args` (the original arguments for
|
|
34
|
+
* a `read`/`stateful-read` tool or a pass-through `action` call, or the
|
|
35
|
+
* arguments captured at mint time for a redeemed one).
|
|
36
|
+
*
|
|
37
|
+
* ## Token design, decided here because nothing upstream decides it
|
|
38
|
+
*
|
|
39
|
+
* - **Binds to call 1's exact arguments.** The token is not a bare
|
|
40
|
+
* capability — it is minted together with a stored copy of the arguments
|
|
41
|
+
* that produced its `willDo`. Redeeming it runs *that* stored copy,
|
|
42
|
+
* ignoring whatever the redeeming call supplies alongside the token. A
|
|
43
|
+
* client cannot describe one action and, on confirmation, have a
|
|
44
|
+
* different one performed.
|
|
45
|
+
* - **Single-use.** A token is deleted from the store the moment it is
|
|
46
|
+
* looked up for redemption, whether or not that redemption goes on to
|
|
47
|
+
* succeed — replay is not a retry, and a caller that wants to do the same
|
|
48
|
+
* thing twice must be told about it twice.
|
|
49
|
+
* - **Five-minute expiry.** A starting value, not a measurement (no
|
|
50
|
+
* confirmation flow has run yet to measure a real gap between mint and
|
|
51
|
+
* confirm) — long enough for a human or a model to read `willDo` and
|
|
52
|
+
* decide, short enough that a token is not a standing credential.
|
|
53
|
+
* - **Unknown, expired or wrong-tool token → `ToolInputError`, never a
|
|
54
|
+
* silent no-op.** This is an argument the caller can fix (ask again,
|
|
55
|
+
* without `confirmToken`, for a fresh one) so it takes the same path as
|
|
56
|
+
* any other bad argument — a tool result the caller can read and act on,
|
|
57
|
+
* not a protocol error and not a quiet success that performed nothing.
|
|
58
|
+
* - **Session-scoped.** One MCP server process serves exactly one `Session`
|
|
59
|
+
* (`McpServerOptions` takes a single session, not a pool), so the store is
|
|
60
|
+
* keyed on the `Session` instance via a `WeakMap` rather than kept as
|
|
61
|
+
* module-global mutable state — the scoping a second session would need is
|
|
62
|
+
* already correct without anything having to change if one is ever added.
|
|
63
|
+
*/
|
|
64
|
+
import { randomBytes } from "node:crypto";
|
|
65
|
+
import { ToolInputError } from "./tools/kit.js";
|
|
66
|
+
const TOKEN_TTL_MS = 5 * 60 * 1000;
|
|
67
|
+
const storesBySession = new WeakMap();
|
|
68
|
+
function storeFor(session) {
|
|
69
|
+
let store = storesBySession.get(session);
|
|
70
|
+
if (store === undefined) {
|
|
71
|
+
store = new Map();
|
|
72
|
+
storesBySession.set(session, store);
|
|
73
|
+
}
|
|
74
|
+
return store;
|
|
75
|
+
}
|
|
76
|
+
function prune(store) {
|
|
77
|
+
const now = Date.now();
|
|
78
|
+
for (const [token, pending] of store) {
|
|
79
|
+
if (pending.expiresAt < now)
|
|
80
|
+
store.delete(token);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function mintToken() {
|
|
84
|
+
return `act_${randomBytes(9).toString("base64url")}`;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Decides what a call to `tool` should do, before any budget timer starts.
|
|
88
|
+
*
|
|
89
|
+
* `read` and `stateful-read` tools, and an `action` tool whose
|
|
90
|
+
* `describeAction` says this call has nothing to confirm, resolve to
|
|
91
|
+
* `{ kind: "run", args }` with `args` unchanged. An unconfirmed call to a
|
|
92
|
+
* gated `action` tool resolves to `{ kind: "handshake", token, willDo }` —
|
|
93
|
+
* the caller must render this directly and must not call `tool.run`. A
|
|
94
|
+
* confirmed call (an `args.confirmToken` that redeems) resolves to `{ kind:
|
|
95
|
+
* "run", args }` with `args` replaced by the arguments captured at mint
|
|
96
|
+
* time.
|
|
97
|
+
*/
|
|
98
|
+
export async function resolveActionDispatch(tool, args, ctx) {
|
|
99
|
+
if (tool.class !== "action")
|
|
100
|
+
return { kind: "run", args };
|
|
101
|
+
const confirmToken = args["confirmToken"];
|
|
102
|
+
if (confirmToken !== undefined && typeof confirmToken !== "string") {
|
|
103
|
+
throw new ToolInputError('"confirmToken" must be a string');
|
|
104
|
+
}
|
|
105
|
+
if (confirmToken === undefined) {
|
|
106
|
+
return mintOrPassThrough(tool, args, ctx);
|
|
107
|
+
}
|
|
108
|
+
return redeem(tool, confirmToken, ctx);
|
|
109
|
+
}
|
|
110
|
+
async function mintOrPassThrough(tool, args, ctx) {
|
|
111
|
+
if (tool.describeAction === undefined) {
|
|
112
|
+
// A bug in the tool registry, not a caller mistake: an author declared
|
|
113
|
+
// `class: "action"` without saying how to describe it. Thrown, not
|
|
114
|
+
// returned as a refusal — there is no argument the caller could change
|
|
115
|
+
// to fix this.
|
|
116
|
+
throw new Error(`${tool.name} declares class "action" but has no describeAction`);
|
|
117
|
+
}
|
|
118
|
+
const willDo = await tool.describeAction(args, ctx);
|
|
119
|
+
if (willDo === undefined)
|
|
120
|
+
return { kind: "run", args };
|
|
121
|
+
const store = storeFor(ctx.session);
|
|
122
|
+
prune(store);
|
|
123
|
+
const token = mintToken();
|
|
124
|
+
store.set(token, { toolName: tool.name, args, expiresAt: Date.now() + TOKEN_TTL_MS });
|
|
125
|
+
return { kind: "handshake", token, willDo };
|
|
126
|
+
}
|
|
127
|
+
async function redeem(tool, confirmToken, ctx) {
|
|
128
|
+
const store = storeFor(ctx.session);
|
|
129
|
+
const pending = store.get(confirmToken);
|
|
130
|
+
// Deleted on lookup, not on success: a replayed token is a used token
|
|
131
|
+
// whether or not what follows here succeeds.
|
|
132
|
+
store.delete(confirmToken);
|
|
133
|
+
if (pending === undefined) {
|
|
134
|
+
throw new ToolInputError(`"confirmToken" is unknown or was already used. Call ${tool.name} again without confirmToken for a fresh one.`);
|
|
135
|
+
}
|
|
136
|
+
if (pending.expiresAt < Date.now()) {
|
|
137
|
+
throw new ToolInputError(`"confirmToken" expired. Call ${tool.name} again without confirmToken for a fresh one.`);
|
|
138
|
+
}
|
|
139
|
+
if (pending.toolName !== tool.name) {
|
|
140
|
+
throw new ToolInputError(`"confirmToken" was not issued for ${tool.name}.`);
|
|
141
|
+
}
|
|
142
|
+
return { kind: "run", args: pending.args };
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=action-handshake.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-handshake.js","sourceRoot":"","sources":["../src/action-handshake.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The executable an MCP host launches.
|
|
4
|
+
*
|
|
5
|
+
* Takes the repository path as its only argument, defaulting to the working
|
|
6
|
+
* directory — which is what a host passes when it starts the server inside a
|
|
7
|
+
* project. Everything else comes from `.descry/config.json`.
|
|
8
|
+
*
|
|
9
|
+
* ## Nothing may print before the transport is installed
|
|
10
|
+
*
|
|
11
|
+
* `serveStdio` redirects every console channel to stderr on its first line. Any
|
|
12
|
+
* output *before* that call goes to real stdout and corrupts the very first
|
|
13
|
+
* frame — the handshake — which is the worst place to fail, because the host
|
|
14
|
+
* reports it as "server did not start" with no further detail. So this file
|
|
15
|
+
* writes nothing itself and does its argument handling before opening anything.
|
|
16
|
+
*/
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=descry-mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descry-mcp.d.ts","sourceRoot":"","sources":["../../src/bin/descry-mcp.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* The executable an MCP host launches.
|
|
4
|
+
*
|
|
5
|
+
* Takes the repository path as its only argument, defaulting to the working
|
|
6
|
+
* directory — which is what a host passes when it starts the server inside a
|
|
7
|
+
* project. Everything else comes from `.descry/config.json`.
|
|
8
|
+
*
|
|
9
|
+
* ## Nothing may print before the transport is installed
|
|
10
|
+
*
|
|
11
|
+
* `serveStdio` redirects every console channel to stderr on its first line. Any
|
|
12
|
+
* output *before* that call goes to real stdout and corrupts the very first
|
|
13
|
+
* frame — the handshake — which is the worst place to fail, because the host
|
|
14
|
+
* reports it as "server did not start" with no further detail. So this file
|
|
15
|
+
* writes nothing itself and does its argument handling before opening anything.
|
|
16
|
+
*/
|
|
17
|
+
import process from "node:process";
|
|
18
|
+
import { resolve } from "node:path";
|
|
19
|
+
import { createMcpServer } from "../server.js";
|
|
20
|
+
import { Session } from "../session.js";
|
|
21
|
+
import { serveStdio } from "../transport.js";
|
|
22
|
+
const USAGE = `descry-mcp — Descry's MCP server (stdio)
|
|
23
|
+
|
|
24
|
+
descry-mcp [repository-path]
|
|
25
|
+
|
|
26
|
+
Reads .descry/config.json from the repository for the graph location and the
|
|
27
|
+
list of adapter packages to load. Runs until stdin closes.
|
|
28
|
+
`;
|
|
29
|
+
async function main() {
|
|
30
|
+
const args = process.argv.slice(2);
|
|
31
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
32
|
+
process.stderr.write(USAGE);
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
const repoPath = resolve(args[0] ?? process.cwd());
|
|
36
|
+
const session = await Session.open({ repoPath });
|
|
37
|
+
const server = createMcpServer({ session, name: "descry", version: "0.0.0" });
|
|
38
|
+
try {
|
|
39
|
+
await serveStdio({ stdin: process.stdin, stdout: process.stdout, stderr: process.stderr }, (line, transport) => server.dispatch(line, transport));
|
|
40
|
+
return 0;
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
// The SQLite handle and anything else with a lifetime. Held open past the
|
|
44
|
+
// end of stdin, a WAL journal outlives the process that owned it.
|
|
45
|
+
session.close();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
main().then((code) => {
|
|
49
|
+
process.exitCode = code;
|
|
50
|
+
}, (error) => {
|
|
51
|
+
process.stderr.write(`descry-mcp: ${error instanceof Error ? error.stack ?? error.message : String(error)}\n`);
|
|
52
|
+
process.exitCode = 1;
|
|
53
|
+
});
|
|
54
|
+
//# sourceMappingURL=descry-mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"descry-mcp.js","sourceRoot":"","sources":["../../src/bin/descry-mcp.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,OAAO,MAAM,cAAc,CAAC;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,MAAM,KAAK,GAAG;;;;;;CAMb,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACnD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,eAAe,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAE9E,IAAI,CAAC;QACH,MAAM,UAAU,CACd,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,EACxE,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CACtD,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC;YAAS,CAAC;QACT,0EAA0E;QAC1E,kEAAkE;QAClE,OAAO,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,IAAI,CACT,CAAC,IAAI,EAAE,EAAE;IACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC,EACD,CAAC,KAAc,EAAE,EAAE;IACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,eAAe,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CACzF,CAAC;IACF,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CACF,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@descryy/mcp` — the agent-facing front door.
|
|
3
|
+
*
|
|
4
|
+
* A thin wrapper over the query layer, as DEC-002 requires: every consumer goes
|
|
5
|
+
* through here and **nothing touches storage directly**. That is why the tools
|
|
6
|
+
* hold a `Session` and not a driver.
|
|
7
|
+
*/
|
|
8
|
+
export { createMcpServer } from "./server.ts";
|
|
9
|
+
export type { McpServer, McpServerOptions } from "./server.ts";
|
|
10
|
+
export { Session, loadConfig, writeConfig, writeSources, ConfigError, CONFIG_RELATIVE_PATH, DEFAULT_GRAPH_RELATIVE_PATH, } from "./session.ts";
|
|
11
|
+
export type { DescryConfig, SessionOptions, ConfigPatch } from "./session.ts";
|
|
12
|
+
export { loadSources, detectSources, normaliseSpec } from "./registry.ts";
|
|
13
|
+
export type { SourceSpec, LoadedSource, SourceLoadFailure, RegistryResult } from "./registry.ts";
|
|
14
|
+
export { renderAnswer, renderFailed, renderHandshake, renderTimedOut, renderToolError, DisclosureError, TOOL_TIERS, } from "./render.ts";
|
|
15
|
+
export type { AnswerEnvelope, CallToolResult, Provenance, ResultState, ToolAnswer, ToolTier, TruncationDetail, } from "./render.ts";
|
|
16
|
+
export { TOOLS, TOOLS_BY_NAME, ToolInputError, resolveOneNode } from "./tools/index.ts";
|
|
17
|
+
export type { BriefNode, NodeCandidates, Progress, ToolContext, ToolDefinition, } from "./tools/index.ts";
|
|
18
|
+
/**
|
|
19
|
+
* The shape each tool puts in `AnswerEnvelope.data`.
|
|
20
|
+
*
|
|
21
|
+
* `data` is `unknown` in the envelope by design — it differs per tool — but
|
|
22
|
+
* "read the handler" is not a contract, and an AI caller cannot rely on a
|
|
23
|
+
* shape nobody wrote down. Each of these describes what its tool returns
|
|
24
|
+
* **today**; where a tool's payload is genuinely conditional the type is a
|
|
25
|
+
* union that says so rather than a widening to `unknown`.
|
|
26
|
+
*/
|
|
27
|
+
export type { AffectedNode, AnalyzeData, BlockedCheck, CoChangedFile, ConfigDependencyReport, ContractPathFinding, ContractShapeMismatchReport, ContractShapeStatus, ContractsData, CrossPrData, CrossPrEmptyDiff, CrossPrGitFailure, CrossPrReport, EnvFindingReport, HiddenDependencyCheck, HiddenDependencyFinding, HistoryData, HistoryFile, HistoryFileResolved, HistoryFileUnresolved, HistoryReport, ImpactData, ImpactReport, ImpactedNode, LinkWorkspaceData, LinkedRepo, OverlapCheck, OverlapReportChecked, PrAnalysisData, PrAnalysisEmptyDiff, PrAnalysisGitFailure, PrAnalysisRange, PrAnalysisReport, PrAnalysisUnresolved, PropagationData, PropagationReport, QuestionsData, ReachedNode, RelatedNode, ScopeData, ScopeNotSeeded, ScopeReport, ScopeTierCount, ScopedNodeReport, SimilarIncidentReport, SimilarIncidentSignalReport, SimilarIncidentsData, SourceRun, SupersededCheck, TracedPath, ValidateData, } from "./tools/index.ts";
|
|
28
|
+
export { serveStdio, guardStdout, createLineReader } from "./transport.ts";
|
|
29
|
+
export type { Transport, StdioOptions } from "./transport.ts";
|
|
30
|
+
export { PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, negotiateVersion, parseMessage, ProtocolError, } from "./protocol.ts";
|
|
31
|
+
export type { RpcRequest, RpcNotification, RpcResponse, RpcMessage, RpcId } from "./protocol.ts";
|
|
32
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9C,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/D,OAAO,EACL,OAAO,EACP,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAE9E,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC1E,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEjG,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,cAAc,EACd,eAAe,EACf,eAAe,EACf,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,YAAY,EACV,cAAc,EACd,cAAc,EACd,UAAU,EACV,WAAW,EACX,UAAU,EACV,QAAQ,EACR,gBAAgB,GACjB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACxF,YAAY,EACV,SAAS,EACT,cAAc,EACd,QAAQ,EACR,WAAW,EACX,cAAc,GACf,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;GAQG;AACH,YAAY,EACV,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,aAAa,EACb,sBAAsB,EACtB,mBAAmB,EACnB,2BAA2B,EAC3B,mBAAmB,EACnB,aAAa,EACb,WAAW,EACX,gBAAgB,EAChB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,qBAAqB,EACrB,uBAAuB,EACvB,WAAW,EACX,WAAW,EACX,mBAAmB,EACnB,qBAAqB,EACrB,aAAa,EACb,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,WAAW,EACX,WAAW,EACX,SAAS,EACT,cAAc,EACd,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,qBAAqB,EACrB,2BAA2B,EAC3B,oBAAoB,EACpB,SAAS,EACT,eAAe,EACf,UAAU,EACV,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAC3E,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,gBAAgB,EAChB,YAAY,EACZ,aAAa,GACd,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@descryy/mcp` — the agent-facing front door.
|
|
3
|
+
*
|
|
4
|
+
* A thin wrapper over the query layer, as DEC-002 requires: every consumer goes
|
|
5
|
+
* through here and **nothing touches storage directly**. That is why the tools
|
|
6
|
+
* hold a `Session` and not a driver.
|
|
7
|
+
*/
|
|
8
|
+
export { createMcpServer } from "./server.js";
|
|
9
|
+
export { Session, loadConfig, writeConfig, writeSources, ConfigError, CONFIG_RELATIVE_PATH, DEFAULT_GRAPH_RELATIVE_PATH, } from "./session.js";
|
|
10
|
+
export { loadSources, detectSources, normaliseSpec } from "./registry.js";
|
|
11
|
+
export { renderAnswer, renderFailed, renderHandshake, renderTimedOut, renderToolError, DisclosureError, TOOL_TIERS, } from "./render.js";
|
|
12
|
+
export { TOOLS, TOOLS_BY_NAME, ToolInputError, resolveOneNode } from "./tools/index.js";
|
|
13
|
+
export { serveStdio, guardStdout, createLineReader } from "./transport.js";
|
|
14
|
+
export { PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, negotiateVersion, parseMessage, ProtocolError, } from "./protocol.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG9C,OAAO,EACL,OAAO,EACP,UAAU,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAG1E,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,cAAc,EACd,eAAe,EACf,eAAe,EACf,UAAU,GACX,MAAM,aAAa,CAAC;AAWrB,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAwExF,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAG3E,OAAO,EACL,gBAAgB,EAChB,2BAA2B,EAC3B,gBAAgB,EAChB,YAAY,EACZ,aAAa,GACd,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A guess, never an assertion — this module names its own limits so nothing
|
|
3
|
+
* downstream mistakes its output for a confirmed fact.
|
|
4
|
+
*
|
|
5
|
+
* `verification_status` needs a language name for a ref that resolved to
|
|
6
|
+
* nothing in the graph, to check whether a *configured, declared* source
|
|
7
|
+
* covers it (`registry.ts`'s `SourceSpec.language`). Extension alone cannot
|
|
8
|
+
* confirm a file's language — a `.h` file could be C or C++, a build script
|
|
9
|
+
* could be anything — so this table only maps extensions unambiguous enough
|
|
10
|
+
* that a wrong guess would be a surprise, and returns `null` rather than a
|
|
11
|
+
* plausible-but-unconfirmed answer for everything else. `null` here means
|
|
12
|
+
* exactly what it always meant one layer up: "this tool did not determine
|
|
13
|
+
* one," never "no language exists."
|
|
14
|
+
*/
|
|
15
|
+
/** `null` on anything not confidently namable — see the module header. */
|
|
16
|
+
export declare function guessLanguageFromPath(path: string): string | null;
|
|
17
|
+
//# sourceMappingURL=language-guess.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-guess.d.ts","sourceRoot":"","sources":["../src/language-guess.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAwBH,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIjE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A guess, never an assertion — this module names its own limits so nothing
|
|
3
|
+
* downstream mistakes its output for a confirmed fact.
|
|
4
|
+
*
|
|
5
|
+
* `verification_status` needs a language name for a ref that resolved to
|
|
6
|
+
* nothing in the graph, to check whether a *configured, declared* source
|
|
7
|
+
* covers it (`registry.ts`'s `SourceSpec.language`). Extension alone cannot
|
|
8
|
+
* confirm a file's language — a `.h` file could be C or C++, a build script
|
|
9
|
+
* could be anything — so this table only maps extensions unambiguous enough
|
|
10
|
+
* that a wrong guess would be a surprise, and returns `null` rather than a
|
|
11
|
+
* plausible-but-unconfirmed answer for everything else. `null` here means
|
|
12
|
+
* exactly what it always meant one layer up: "this tool did not determine
|
|
13
|
+
* one," never "no language exists."
|
|
14
|
+
*/
|
|
15
|
+
const EXTENSION_TO_LANGUAGE = {
|
|
16
|
+
ts: "typescript",
|
|
17
|
+
tsx: "typescript",
|
|
18
|
+
js: "javascript",
|
|
19
|
+
jsx: "javascript",
|
|
20
|
+
mjs: "javascript",
|
|
21
|
+
cjs: "javascript",
|
|
22
|
+
py: "python",
|
|
23
|
+
go: "go",
|
|
24
|
+
java: "java",
|
|
25
|
+
kt: "kotlin",
|
|
26
|
+
kts: "kotlin",
|
|
27
|
+
cs: "csharp",
|
|
28
|
+
php: "php",
|
|
29
|
+
rb: "ruby",
|
|
30
|
+
rs: "rust",
|
|
31
|
+
swift: "swift",
|
|
32
|
+
sql: "sql",
|
|
33
|
+
sh: "bash",
|
|
34
|
+
bash: "bash",
|
|
35
|
+
};
|
|
36
|
+
/** `null` on anything not confidently namable — see the module header. */
|
|
37
|
+
export function guessLanguageFromPath(path) {
|
|
38
|
+
const match = /\.([^./]+)$/.exec(path);
|
|
39
|
+
if (match === null)
|
|
40
|
+
return null;
|
|
41
|
+
return EXTENSION_TO_LANGUAGE[match[1].toLowerCase()] ?? null;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=language-guess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"language-guess.js","sourceRoot":"","sources":["../src/language-guess.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,qBAAqB,GAAqC;IAC9D,EAAE,EAAE,YAAY;IAChB,GAAG,EAAE,YAAY;IACjB,EAAE,EAAE,YAAY;IAChB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,YAAY;IACjB,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,IAAI;IACR,IAAI,EAAE,MAAM;IACZ,EAAE,EAAE,QAAQ;IACZ,GAAG,EAAE,QAAQ;IACb,EAAE,EAAE,QAAQ;IACZ,GAAG,EAAE,KAAK;IACV,EAAE,EAAE,MAAM;IACV,EAAE,EAAE,MAAM;IACV,KAAK,EAAE,OAAO;IACd,GAAG,EAAE,KAAK;IACV,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,MAAM;CACb,CAAC;AAEF,0EAA0E;AAC1E,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,MAAM,KAAK,GAAG,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAChC,OAAO,qBAAqB,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,WAAW,EAAE,CAAC,IAAI,IAAI,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON-RPC 2.0 and the slice of MCP this server speaks.
|
|
3
|
+
*
|
|
4
|
+
* ## Why this is hand-written
|
|
5
|
+
*
|
|
6
|
+
* `@modelcontextprotocol/sdk` is the obvious answer and the blueprint's
|
|
7
|
+
* dependency table names it. It was measured rather than assumed: version 1.30.0
|
|
8
|
+
* declares **seventeen direct dependencies** — `express`, `hono`,
|
|
9
|
+
* `@hono/node-server`, `cors`, `express-rate-limit`, `jose`, `pkce-challenge`,
|
|
10
|
+
* `eventsource`, `ajv`, `zod` — essentially all of which exist to serve the HTTP
|
|
11
|
+
* and OAuth transports. This server speaks stdio to a coding agent on the same
|
|
12
|
+
* machine. It will never open a socket.
|
|
13
|
+
*
|
|
14
|
+
* The rest of this project has made the same call four times — `node:sqlite`
|
|
15
|
+
* over `better-sqlite3`, `node --test` over a test runner, `tsc` over a bundler,
|
|
16
|
+
* `git` invoked directly over a git library — every time on the same grounds:
|
|
17
|
+
* this is a **locally installed developer tool**, and a dependency tree is a
|
|
18
|
+
* distribution problem before it is anything else. Both shipped packages have
|
|
19
|
+
* zero runtime dependencies today. Seventeen transitively-hundreds is a large
|
|
20
|
+
* price for newline-delimited JSON.
|
|
21
|
+
*
|
|
22
|
+
* **The cost is named, not waved away.** Hand-rolling means spec drift is
|
|
23
|
+
* silent: the wire format changes, the client stops working, and nothing in this
|
|
24
|
+
* repository fails. Two things pay it down. `PROTOCOL_VERSION` is pinned and
|
|
25
|
+
* `SUPPORTED_PROTOCOL_VERSIONS` is explicit, so an unknown version is negotiated
|
|
26
|
+
* rather than assumed compatible; and the handshake is covered by tests that
|
|
27
|
+
* assert the exact frames, so a change to the shape is a red test rather than a
|
|
28
|
+
* user-visible mystery. If the protocol moves past what is here, the fix is to
|
|
29
|
+
* add a version to the list — not to discover it from a bug report.
|
|
30
|
+
*/
|
|
31
|
+
/** JSON-RPC 2.0 ids: string or number, never null in a request. */
|
|
32
|
+
export type RpcId = string | number;
|
|
33
|
+
export interface RpcRequest {
|
|
34
|
+
readonly jsonrpc: "2.0";
|
|
35
|
+
readonly id: RpcId;
|
|
36
|
+
readonly method: string;
|
|
37
|
+
readonly params?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
export interface RpcNotification {
|
|
40
|
+
readonly jsonrpc: "2.0";
|
|
41
|
+
readonly method: string;
|
|
42
|
+
readonly params?: Record<string, unknown>;
|
|
43
|
+
}
|
|
44
|
+
export interface RpcError {
|
|
45
|
+
readonly code: number;
|
|
46
|
+
readonly message: string;
|
|
47
|
+
readonly data?: unknown;
|
|
48
|
+
}
|
|
49
|
+
export interface RpcResponse {
|
|
50
|
+
readonly jsonrpc: "2.0";
|
|
51
|
+
readonly id: RpcId;
|
|
52
|
+
readonly result?: unknown;
|
|
53
|
+
readonly error?: RpcError;
|
|
54
|
+
}
|
|
55
|
+
export type RpcMessage = RpcRequest | RpcNotification;
|
|
56
|
+
/** Standard JSON-RPC codes. MCP adds none of its own at this layer. */
|
|
57
|
+
export declare const RPC_PARSE_ERROR = -32700;
|
|
58
|
+
export declare const RPC_INVALID_REQUEST = -32600;
|
|
59
|
+
export declare const RPC_METHOD_NOT_FOUND = -32601;
|
|
60
|
+
export declare const RPC_INVALID_PARAMS = -32602;
|
|
61
|
+
export declare const RPC_INTERNAL_ERROR = -32603;
|
|
62
|
+
/**
|
|
63
|
+
* The version this server implements.
|
|
64
|
+
*
|
|
65
|
+
* Newest first. Negotiation echoes the client's version when it is on this list
|
|
66
|
+
* and otherwise answers with `PROTOCOL_VERSION`, which is what the spec asks for
|
|
67
|
+
* — the client then decides whether it can proceed. Answering with the client's
|
|
68
|
+
* unknown version instead would be a claim of compatibility nobody has checked.
|
|
69
|
+
*/
|
|
70
|
+
export declare const PROTOCOL_VERSION = "2025-06-18";
|
|
71
|
+
export declare const SUPPORTED_PROTOCOL_VERSIONS: readonly string[];
|
|
72
|
+
export declare function negotiateVersion(requested: unknown): string;
|
|
73
|
+
/**
|
|
74
|
+
* A frame that could not be understood well enough to answer.
|
|
75
|
+
*
|
|
76
|
+
* Distinguished from a request that is merely wrong, because JSON-RPC says a
|
|
77
|
+
* message with no usable id gets no response at all — replying to it with a
|
|
78
|
+
* fabricated id would put a frame on the wire the client cannot correlate.
|
|
79
|
+
*/
|
|
80
|
+
export declare class ProtocolError extends Error {
|
|
81
|
+
readonly code: number;
|
|
82
|
+
/** Null when the frame carried no usable id and must therefore go unanswered. */
|
|
83
|
+
readonly id: RpcId | null;
|
|
84
|
+
constructor(code: number, message: string, id: RpcId | null);
|
|
85
|
+
}
|
|
86
|
+
/** Parse one line into a request or notification, or throw `ProtocolError`. */
|
|
87
|
+
export declare function parseMessage(line: string): RpcMessage;
|
|
88
|
+
export declare function isRequest(message: RpcMessage): message is RpcRequest;
|
|
89
|
+
export declare function ok(id: RpcId, result: unknown): RpcResponse;
|
|
90
|
+
export declare function fail(id: RpcId, code: number, message: string, data?: unknown): RpcResponse;
|
|
91
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,mEAAmE;AACnE,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,MAAM,CAAC;AAEpC,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC3C;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,OAAO,EAAE,KAAK,CAAC;IACxB,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IACnB,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;CAC3B;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,GAAG,eAAe,CAAC;AAEtD,uEAAuE;AACvE,eAAO,MAAM,eAAe,SAAS,CAAC;AACtC,eAAO,MAAM,mBAAmB,SAAS,CAAC;AAC1C,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAC3C,eAAO,MAAM,kBAAkB,SAAS,CAAC;AACzC,eAAO,MAAM,kBAAkB,SAAS,CAAC;AAEzC;;;;;;;GAOG;AACH,eAAO,MAAM,gBAAgB,eAAe,CAAC;AAC7C,eAAO,MAAM,2BAA2B,EAAE,SAAS,MAAM,EAIxD,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAI3D;AAMD;;;;;;GAMG;AACH,qBAAa,aAAc,SAAQ,KAAK;IACtC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,EAAE,KAAK,GAAG,IAAI,CAAC;gBAEd,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,GAAG,IAAI;CAM5D;AAMD,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAsCrD;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,IAAI,UAAU,CAEpE;AAED,wBAAgB,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAG,WAAW,CAE1D;AAED,wBAAgB,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,WAAW,CAM1F"}
|