@toolgrain/mcp 0.3.0-alpha.0 → 0.3.0-alpha.1
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/CHANGELOG.md +7 -0
- package/README.md +7 -3
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +9 -1
- package/dist/connection.js.map +1 -1
- package/dist/pending-requests.d.ts +2 -1
- package/dist/pending-requests.d.ts.map +1 -1
- package/dist/pending-requests.js +17 -0
- package/dist/pending-requests.js.map +1 -1
- package/dist/tools/context.d.ts +3 -2
- package/dist/tools/context.d.ts.map +1 -1
- package/dist/tools/context.js.map +1 -1
- package/dist/tools/progress.d.ts +4 -2
- package/dist/tools/progress.d.ts.map +1 -1
- package/dist/tools/progress.js +40 -6
- package/dist/tools/progress.js.map +1 -1
- package/package.json +2 -2
- package/src/connection.ts +15 -0
- package/src/pending-requests.ts +17 -1
- package/src/tools/context.ts +3 -2
- package/src/tools/progress.ts +46 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# @toolgrain/mcp
|
|
2
2
|
|
|
3
|
+
## 0.3.0-alpha.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Pace request-scoped progress notifications at 100 ms intervals while preserving ordered delivery, backpressure, and prompt cancellation. Reject duplicate active progress tokens on shared message connections, release tokens on completion or cancellation, and preserve isolation between independent HTTP response streams.
|
|
8
|
+
- @toolgrain/protocol@0.3.0-alpha.1
|
|
9
|
+
|
|
3
10
|
## 0.3.0-alpha.0
|
|
4
11
|
|
|
5
12
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -70,9 +70,13 @@ Handlers can accept `execute(input, ctx)` to use request information and
|
|
|
70
70
|
`ctx.signal` for cooperative cancellation. Report client-requested progress with
|
|
71
71
|
`await ctx.reportProgress({ progress: 1, total: 10, message: "Processing" })`.
|
|
72
72
|
The bound method is safe to pass to helpers and becomes a no-op without a
|
|
73
|
-
progress token or delivery channel.
|
|
74
|
-
|
|
75
|
-
|
|
73
|
+
progress token or delivery channel. The first update sends immediately; later
|
|
74
|
+
updates are spaced at least 100 ms apart per invocation, preserving order and
|
|
75
|
+
backpressure. Await each report. Duplicate active progress tokens are rejected
|
|
76
|
+
on shared message connections; independent HTTP response streams remain isolated.
|
|
77
|
+
Use `toolContext()` for direct handler calls, which bypass server validation and
|
|
78
|
+
result conversion. Unexpected server execution failures return generic errors
|
|
79
|
+
and reach the optional `onError`
|
|
76
80
|
callback. Request-cached dependencies use `createContext()` and inferred
|
|
77
81
|
accessors; see the
|
|
78
82
|
[context guide](https://toolgrain.mintlify.site/guides/context).
|
package/dist/connection.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,KAAK,YAAY,EAAe,MAAM,kBAAkB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAOlD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,KAAK,EACV,gBAAgB,EAChB,SAAS,EAEV,MAAM,gBAAgB,CAAC;AAGxB,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,UAAU,EACpB,OAAO,CAAC,EAAE,YAAY,EACtB,OAAO,CAAC,EAAE,MAAM,IAAI,GACnB,mBAAmB,CAErB;AAED,UAAU,mBAAmB;IAC3B,MAAM,CAAC,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,cAAc,CAAC,SAAS,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB"}
|
package/dist/connection.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { INVALID_REQUEST, PARSE_ERROR, } from "@toolgrain/protocol";
|
|
1
|
+
import { INVALID_PARAMS, INVALID_REQUEST, PARSE_ERROR, } from "@toolgrain/protocol";
|
|
2
2
|
import { reportError } from "./diagnostics.js";
|
|
3
3
|
import { executeRequest } from "./execute-request.js";
|
|
4
4
|
import { createLegacyConnection } from "./legacy/connection.js";
|
|
5
5
|
import { createPendingRequests } from "./pending-requests.js";
|
|
6
6
|
import { decode } from "./protocol/decode.js";
|
|
7
7
|
import { protocolError } from "./protocol/responses.js";
|
|
8
|
+
import { isProgressToken } from "./tools/progress.js";
|
|
8
9
|
import { claimTransport } from "./transport-ownership.js";
|
|
9
10
|
export function createConnection(dispatch, onError, onClose) {
|
|
10
11
|
return new Connection(dispatch, onError, onClose);
|
|
@@ -135,6 +136,13 @@ class Connection {
|
|
|
135
136
|
await this.send(protocolError(INVALID_REQUEST, "Request ID is already in use", request.id));
|
|
136
137
|
return;
|
|
137
138
|
}
|
|
139
|
+
const token = request.params?._meta
|
|
140
|
+
?.progressToken;
|
|
141
|
+
if (isProgressToken(token) && !pending.claimProgress(token)) {
|
|
142
|
+
pending.finish();
|
|
143
|
+
await this.send(protocolError(INVALID_PARAMS, "Progress token is already in use", request.id));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
138
146
|
const initialization = this.legacy.begin(request, prepared.protocolVersion);
|
|
139
147
|
try {
|
|
140
148
|
const response = await executeRequest(this.dispatch, request, {
|
package/dist/connection.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,WAAW,GAGZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAqB,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAElE,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { RequestId } from "@toolgrain/protocol";
|
|
1
|
+
import type { ProgressToken, RequestId } from "@toolgrain/protocol";
|
|
2
2
|
/** Owns duplicate detection and cancellation signals for in-flight requests. */
|
|
3
3
|
export declare function createPendingRequests(isOpen: () => boolean): {
|
|
4
4
|
begin(id: RequestId): {
|
|
5
5
|
signal: AbortSignal;
|
|
6
6
|
isActive: () => boolean;
|
|
7
|
+
claimProgress(token: ProgressToken): boolean;
|
|
7
8
|
finish: () => void;
|
|
8
9
|
} | undefined;
|
|
9
10
|
cancel(id: RequestId): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending-requests.d.ts","sourceRoot":"","sources":["../src/pending-requests.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"pending-requests.d.ts","sourceRoot":"","sources":["../src/pending-requests.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEpE,gFAAgF;AAChF,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,OAAO;IAIvD,KAAK,KAAK,SAAS;;;6BAQM,aAAa;;;IAmBtC,MAAM,KAAK,SAAS;IAKpB,KAAK;EAMR"}
|
package/dist/pending-requests.js
CHANGED
|
@@ -1,16 +1,33 @@
|
|
|
1
1
|
/** Owns duplicate detection and cancellation signals for in-flight requests. */
|
|
2
2
|
export function createPendingRequests(isOpen) {
|
|
3
3
|
const pending = new Map();
|
|
4
|
+
const progressTokens = new Set();
|
|
4
5
|
return {
|
|
5
6
|
begin(id) {
|
|
6
7
|
if (pending.has(id))
|
|
7
8
|
return;
|
|
8
9
|
const controller = new AbortController();
|
|
9
10
|
pending.set(id, controller);
|
|
11
|
+
let releaseProgress;
|
|
10
12
|
return {
|
|
11
13
|
signal: controller.signal,
|
|
12
14
|
isActive: () => isOpen() && !controller.signal.aborted,
|
|
15
|
+
claimProgress(token) {
|
|
16
|
+
if (progressTokens.has(token))
|
|
17
|
+
return false;
|
|
18
|
+
progressTokens.add(token);
|
|
19
|
+
releaseProgress = () => {
|
|
20
|
+
progressTokens.delete(token);
|
|
21
|
+
controller.signal.removeEventListener("abort", release);
|
|
22
|
+
releaseProgress = undefined;
|
|
23
|
+
};
|
|
24
|
+
// Aborting releases the token even if the cancelled handler keeps running.
|
|
25
|
+
const release = () => releaseProgress?.();
|
|
26
|
+
controller.signal.addEventListener("abort", release, { once: true });
|
|
27
|
+
return true;
|
|
28
|
+
},
|
|
13
29
|
finish: () => {
|
|
30
|
+
releaseProgress?.();
|
|
14
31
|
pending.delete(id);
|
|
15
32
|
},
|
|
16
33
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pending-requests.js","sourceRoot":"","sources":["../src/pending-requests.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,UAAU,qBAAqB,CAAC,MAAqB;IACzD,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAC;IACtD,OAAO;QACL,KAAK,CAAC,EAAa;YACjB,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO;YAC5B,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;YACzC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAC5B,OAAO;gBACL,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO;gBACtD,MAAM,EAAE,GAAG,EAAE;oBACX,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACrB,CAAC;aACF,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,EAAa;YAClB,OAAO;iBACJ,GAAG,CAAC,EAAE,CAAC;gBACR,EAAE,KAAK,CAAC,IAAI,YAAY,CAAC,6BAA6B,EAAE,YAAY,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,KAAK;YACH,KAAK,MAAM,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE;gBACvC,UAAU,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,mBAAmB,EAAE,YAAY,CAAC,CAAC,CAAC;YACxE,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"pending-requests.js","sourceRoot":"","sources":["../src/pending-requests.ts"],"names":[],"mappings":"AAEA,gFAAgF;AAChF,MAAM,UAAU,qBAAqB,CAAC,MAAqB;IACzD,MAAM,OAAO,GAAG,IAAI,GAAG,EAA8B,CAAC;IACtD,MAAM,cAAc,GAAG,IAAI,GAAG,EAAiB,CAAC;IAChD,OAAO;QACL,KAAK,CAAC,EAAa;YACjB,IAAI,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO;YAC5B,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;YACzC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;YAC5B,IAAI,eAAyC,CAAC;YAC9C,OAAO;gBACL,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,QAAQ,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO;gBACtD,aAAa,CAAC,KAAoB;oBAChC,IAAI,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC;wBAAE,OAAO,KAAK,CAAC;oBAC5C,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;oBAC1B,eAAe,GAAG,GAAG,EAAE;wBACrB,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;wBAC7B,UAAU,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;wBACxD,eAAe,GAAG,SAAS,CAAC;oBAC9B,CAAC,CAAC;oBACF,2EAA2E;oBAC3E,MAAM,OAAO,GAAG,GAAG,EAAE,CAAC,eAAe,EAAE,EAAE,CAAC;oBAC1C,UAAU,CAAC,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;oBACrE,OAAO,IAAI,CAAC;gBACd,CAAC;gBACD,MAAM,EAAE,GAAG,EAAE;oBACX,eAAe,EAAE,EAAE,CAAC;oBACpB,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACrB,CAAC;aACF,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,EAAa;YAClB,OAAO;iBACJ,GAAG,CAAC,EAAE,CAAC;gBACR,EAAE,KAAK,CAAC,IAAI,YAAY,CAAC,6BAA6B,EAAE,YAAY,CAAC,CAAC,CAAC;QAC3E,CAAC;QACD,KAAK;YACH,KAAK,MAAM,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE;gBACvC,UAAU,CAAC,KAAK,CAAC,IAAI,YAAY,CAAC,mBAAmB,EAAE,YAAY,CAAC,CAAC,CAAC;YACxE,OAAO,CAAC,KAAK,EAAE,CAAC;QAClB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
package/dist/tools/context.d.ts
CHANGED
|
@@ -31,8 +31,9 @@ export interface ToolContext {
|
|
|
31
31
|
readonly request: ToolRequest;
|
|
32
32
|
/**
|
|
33
33
|
* Report request-scoped progress; safe to pass to helpers without binding.
|
|
34
|
-
* Await delivery/backpressure
|
|
35
|
-
* or delivery channel exists,
|
|
34
|
+
* Await delivery/backpressure; updates are spaced at least 100 ms per invocation.
|
|
35
|
+
* Resolves without sending when no progress token or delivery channel exists,
|
|
36
|
+
* or after cancellation/completion. Active updates
|
|
36
37
|
* require finite values and strictly increasing progress; invalid updates reject.
|
|
37
38
|
* Delivery failure cancels the request and reaches transport diagnostics.
|
|
38
39
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/tools/context.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EAEnB,KAAK,SAAS,EACd,KAAK,wBAAwB,EAC9B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,kBAAkB,IAAI,wBAAwB,EAC9C,cAAc,IAAI,oBAAoB,EACvC,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAC1E,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,gGAAgG;AAChG,MAAM,WAAW,UAAU;IACzB,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,cAAc,GAAG,oBAAoB,CAAC,CAAC;IACpE,sFAAsF;IACtF,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAClC,kBAAkB,GAAG,wBAAwB,CAC9C,CAAC;CACH;AAED,+FAA+F;AAC/F,MAAM,WAAW,WAAW;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,eAAe,EAAE,wBAAwB,CAAC;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAClD;AAED,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,wFAAwF;IACxF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B
|
|
1
|
+
{"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/tools/context.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,cAAc,EAEnB,KAAK,SAAS,EACd,KAAK,wBAAwB,EAC9B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EACV,kBAAkB,IAAI,wBAAwB,EAC9C,cAAc,IAAI,oBAAoB,EACvC,MAAM,gCAAgC,CAAC;AAExC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAC1E,OAAO,EACL,KAAK,eAAe,EAErB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,gGAAgG;AAChG,MAAM,WAAW,UAAU;IACzB,2FAA2F;IAC3F,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC,cAAc,GAAG,oBAAoB,CAAC,CAAC;IACpE,sFAAsF;IACtF,QAAQ,CAAC,YAAY,CAAC,EAAE,YAAY,CAClC,kBAAkB,GAAG,wBAAwB,CAC9C,CAAC;CACH;AAED,+FAA+F;AAC/F,MAAM,WAAW,WAAW;IAC1B,oFAAoF;IACpF,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,gEAAgE;IAChE,QAAQ,CAAC,eAAe,EAAE,wBAAwB,CAAC;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,MAAM,CAAC,EAAE,UAAU,CAAC;IAC7B,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAClD;AAED,8EAA8E;AAC9E,MAAM,WAAW,WAAW;IAC1B,wFAAwF;IACxF,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAC;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClE;AAED,wFAAwF;AACxF,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,gGAAgG;IAChG,OAAO,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC/B,8FAA8F;IAC9F,OAAO,CAAC,EAAE,SAAS,yBAAyB,EAAE,CAAC;CAChD;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,WAAW,CAmBzE;AAED,6FAA6F;AAC7F,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,WAAW,EACnB,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,EAAE,WAAW,EACnC,IAAI,CAAC,EAAE,YAAY,EACnB,SAAS,GAAE,eAAyC,EACpD,cAAc,GAAE,WAAW,CAAC,gBAAgB,CAA2B,GACtE,WAAW,CAkBb;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CACxB,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,EACxB,YAAY,EAAE,UAAU,CAAC,cAAc,CAAC,GACvC,UAAU,CAKZ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/tools/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,gBAAgB,GAGjB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,OAAO,EAEL,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/tools/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,gBAAgB,GAGjB,MAAM,qBAAqB,CAAC;AAK7B,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,OAAO,EAEL,qBAAqB,GACtB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAqDzD;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,OAAO,GAAuB,EAAE;IAC1D,mFAAmF;IACnF,MAAM,EACJ,EAAE,GAAG,CAAC,EACN,eAAe,GAAG,gBAAgB,EAClC,MAAM,EACN,IAAI,GAAG,EAAE,GACV,GAAG,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC;IAC1B,OAAO,iBAAiB,CACtB,OAAO,CAAC,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC,MAAM,EAC9C;QACE,EAAE;QACF,eAAe;QACf,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC;QAC3C,IAAI;KACL,EACD,SAAS,EACT,qBAAqB,CAAC,OAAO,CAAC,OAAO,CAAC,CACvC,CAAC;AACJ,CAAC;AAED,6FAA6F;AAC7F,MAAM,UAAU,iBAAiB,CAC/B,MAAmB,EACnB,EAAE,MAAM,EAAE,GAAG,OAAO,EAAe,EACnC,IAAmB,EACnB,SAAS,GAAoB,qBAAqB,EAAE,EACpD,cAAc,GAAkC,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;IAEvE,MAAM,GAAG,GAAG,UAAU,CACpB;QACE,MAAM;QACN,cAAc;QACd,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC;YACrB,GAAG,OAAO;YACV,GAAG,CAAC,MAAM,KAAK,SAAS;gBACtB,CAAC,CAAC,EAAE;gBACJ,CAAC,CAAC;oBACE,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;iBACjE,CAAC;YACN,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC;SAC/B,CAAC;KACH,EACD,IAAI,CACL,CAAC;IACF,OAAO,kBAAkB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;AAC5C,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,UAAU,CACxB,IAAwB,EACxB,YAAwC;IAExC,OAAO;QACL,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;QACvC,GAAG,CAAC,YAAY,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC;KACxD,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAI,KAAQ;IAC7B,OAAO,UAAU,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;AAC5C,CAAC;AAED,SAAS,UAAU,CAAI,KAAQ;IAC7B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACrB,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;YAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/dist/tools/progress.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ProgressNotification } from "@toolgrain/protocol";
|
|
1
|
+
import type { ProgressNotification, ProgressToken } from "@toolgrain/protocol";
|
|
2
2
|
/** One progress update; values are finite numbers and progress must strictly increase. */
|
|
3
3
|
export interface ToolProgress {
|
|
4
4
|
/** Work completed so far, in application-defined units; the first value may be zero. */
|
|
@@ -8,8 +8,10 @@ export interface ToolProgress {
|
|
|
8
8
|
/** Optional public status message sent to the client. */
|
|
9
9
|
readonly message?: string;
|
|
10
10
|
}
|
|
11
|
+
/** Tokens share the request-ID wire format, but have a separate lifetime. */
|
|
12
|
+
export declare function isProgressToken(token: unknown): token is ProgressToken;
|
|
11
13
|
/** One invocation owns its reporter, ordering, and completion boundary. */
|
|
12
|
-
export declare function createProgressReporter(
|
|
14
|
+
export declare function createProgressReporter(requestSignal: AbortSignal, token: unknown, send?: (message: ProgressNotification) => void | Promise<void>): {
|
|
13
15
|
report: (value: ToolProgress) => Promise<void>;
|
|
14
16
|
finish(): Promise<void>;
|
|
15
17
|
close(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAI/E,0FAA0F;AAC1F,MAAM,WAAW,YAAY;IAC3B,wFAAwF;IACxF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AAQD,6EAA6E;AAC7E,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAOtE;AAkBD,2EAA2E;AAC3E,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,WAAW,EAC1B,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;oBASvC,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC;;;EA2DpD"}
|
package/dist/tools/progress.js
CHANGED
|
@@ -5,15 +5,39 @@ const update = z.object({
|
|
|
5
5
|
total: z.number().finite().optional(),
|
|
6
6
|
message: z.string().optional(),
|
|
7
7
|
});
|
|
8
|
-
/**
|
|
9
|
-
export function
|
|
10
|
-
|
|
8
|
+
/** Tokens share the request-ID wire format, but have a separate lifetime. */
|
|
9
|
+
export function isProgressToken(token) {
|
|
10
|
+
return (typeof token === "string" ||
|
|
11
11
|
(typeof token === "number" &&
|
|
12
12
|
Number.isFinite(token) &&
|
|
13
|
-
Number.isInteger(token));
|
|
13
|
+
Number.isInteger(token)));
|
|
14
|
+
}
|
|
15
|
+
/** Cancellation must release the timer as well as the awaiting handler. */
|
|
16
|
+
function waitForProgress(milliseconds, signal) {
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
const abort = () => {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
reject(signal.reason);
|
|
21
|
+
};
|
|
22
|
+
const timer = setTimeout(() => {
|
|
23
|
+
signal.removeEventListener("abort", abort);
|
|
24
|
+
resolve();
|
|
25
|
+
}, milliseconds);
|
|
26
|
+
if (signal.aborted)
|
|
27
|
+
abort();
|
|
28
|
+
else
|
|
29
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/** One invocation owns its reporter, ordering, and completion boundary. */
|
|
33
|
+
export function createProgressReporter(requestSignal, token, send) {
|
|
34
|
+
const lifetime = new AbortController();
|
|
35
|
+
const signal = AbortSignal.any([requestSignal, lifetime.signal]);
|
|
36
|
+
const validToken = isProgressToken(token);
|
|
14
37
|
let open = true;
|
|
15
38
|
let previous = Number.NEGATIVE_INFINITY;
|
|
16
39
|
let writes = Promise.resolve();
|
|
40
|
+
let lastSent = Number.NEGATIVE_INFINITY;
|
|
17
41
|
const report = (value) => {
|
|
18
42
|
if (!open || signal.aborted || !validToken || !send)
|
|
19
43
|
return Promise.resolve();
|
|
@@ -38,11 +62,20 @@ export function createProgressReporter(signal, token, send) {
|
|
|
38
62
|
},
|
|
39
63
|
};
|
|
40
64
|
const write = writes.then(async () => {
|
|
41
|
-
if (!signal.aborted)
|
|
65
|
+
if (!signal.aborted) {
|
|
66
|
+
// Pace each operation without dropping updates or bypassing backpressure.
|
|
67
|
+
let remaining = 100 - (performance.now() - lastSent);
|
|
68
|
+
while (remaining > 0) {
|
|
69
|
+
await waitForProgress(Math.ceil(remaining), signal);
|
|
70
|
+
remaining = 100 - (performance.now() - lastSent);
|
|
71
|
+
}
|
|
42
72
|
await abortable(Promise.resolve().then(() => {
|
|
43
|
-
if (!signal.aborted)
|
|
73
|
+
if (!signal.aborted) {
|
|
74
|
+
lastSent = performance.now();
|
|
44
75
|
return send(message);
|
|
76
|
+
}
|
|
45
77
|
}), signal);
|
|
78
|
+
}
|
|
46
79
|
});
|
|
47
80
|
// The awaiting handler observes failures; keep the queue usable for cleanup.
|
|
48
81
|
writes = write.catch(() => { });
|
|
@@ -56,6 +89,7 @@ export function createProgressReporter(signal, token, send) {
|
|
|
56
89
|
},
|
|
57
90
|
close() {
|
|
58
91
|
open = false;
|
|
92
|
+
lifetime.abort(new DOMException("Progress reporting closed", "AbortError"));
|
|
59
93
|
},
|
|
60
94
|
};
|
|
61
95
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAYxC,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;IACtB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE;IAC7B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrC,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC/B,CAAC,CAAC;AAEH,
|
|
1
|
+
{"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toolgrain/mcp",
|
|
3
|
-
"version": "0.3.0-alpha.
|
|
3
|
+
"version": "0.3.0-alpha.1",
|
|
4
4
|
"description": "Define reusable, typed MCP tools and servers with Zod validation.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"@toolgrain/repo-tools": "0.0.0"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@toolgrain/protocol": "0.3.0-alpha.
|
|
62
|
+
"@toolgrain/protocol": "0.3.0-alpha.1"
|
|
63
63
|
},
|
|
64
64
|
"engines": {
|
|
65
65
|
"node": "^24.20.0",
|
package/src/connection.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
INVALID_PARAMS,
|
|
2
3
|
INVALID_REQUEST,
|
|
3
4
|
PARSE_ERROR,
|
|
4
5
|
type ProgressNotification,
|
|
@@ -13,6 +14,7 @@ import { decode } from "./protocol/decode.js";
|
|
|
13
14
|
import type { Request } from "./protocol/messages.js";
|
|
14
15
|
import { protocolError } from "./protocol/responses.js";
|
|
15
16
|
import type { Server } from "./server.js";
|
|
17
|
+
import { isProgressToken } from "./tools/progress.js";
|
|
16
18
|
import type {
|
|
17
19
|
RequestTransport,
|
|
18
20
|
Transport,
|
|
@@ -189,6 +191,19 @@ class Connection implements ConnectionLifecycle {
|
|
|
189
191
|
);
|
|
190
192
|
return;
|
|
191
193
|
}
|
|
194
|
+
const token = (request.params?._meta as Record<string, unknown> | undefined)
|
|
195
|
+
?.progressToken;
|
|
196
|
+
if (isProgressToken(token) && !pending.claimProgress(token)) {
|
|
197
|
+
pending.finish();
|
|
198
|
+
await this.send(
|
|
199
|
+
protocolError(
|
|
200
|
+
INVALID_PARAMS,
|
|
201
|
+
"Progress token is already in use",
|
|
202
|
+
request.id,
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
192
207
|
const initialization = this.legacy.begin(request, prepared.protocolVersion);
|
|
193
208
|
try {
|
|
194
209
|
const response = await executeRequest(
|
package/src/pending-requests.ts
CHANGED
|
@@ -1,17 +1,33 @@
|
|
|
1
|
-
import type { RequestId } from "@toolgrain/protocol";
|
|
1
|
+
import type { ProgressToken, RequestId } from "@toolgrain/protocol";
|
|
2
2
|
|
|
3
3
|
/** Owns duplicate detection and cancellation signals for in-flight requests. */
|
|
4
4
|
export function createPendingRequests(isOpen: () => boolean) {
|
|
5
5
|
const pending = new Map<RequestId, AbortController>();
|
|
6
|
+
const progressTokens = new Set<ProgressToken>();
|
|
6
7
|
return {
|
|
7
8
|
begin(id: RequestId) {
|
|
8
9
|
if (pending.has(id)) return;
|
|
9
10
|
const controller = new AbortController();
|
|
10
11
|
pending.set(id, controller);
|
|
12
|
+
let releaseProgress: (() => void) | undefined;
|
|
11
13
|
return {
|
|
12
14
|
signal: controller.signal,
|
|
13
15
|
isActive: () => isOpen() && !controller.signal.aborted,
|
|
16
|
+
claimProgress(token: ProgressToken) {
|
|
17
|
+
if (progressTokens.has(token)) return false;
|
|
18
|
+
progressTokens.add(token);
|
|
19
|
+
releaseProgress = () => {
|
|
20
|
+
progressTokens.delete(token);
|
|
21
|
+
controller.signal.removeEventListener("abort", release);
|
|
22
|
+
releaseProgress = undefined;
|
|
23
|
+
};
|
|
24
|
+
// Aborting releases the token even if the cancelled handler keeps running.
|
|
25
|
+
const release = () => releaseProgress?.();
|
|
26
|
+
controller.signal.addEventListener("abort", release, { once: true });
|
|
27
|
+
return true;
|
|
28
|
+
},
|
|
14
29
|
finish: () => {
|
|
30
|
+
releaseProgress?.();
|
|
15
31
|
pending.delete(id);
|
|
16
32
|
},
|
|
17
33
|
};
|
package/src/tools/context.ts
CHANGED
|
@@ -50,8 +50,9 @@ export interface ToolContext {
|
|
|
50
50
|
readonly request: ToolRequest;
|
|
51
51
|
/**
|
|
52
52
|
* Report request-scoped progress; safe to pass to helpers without binding.
|
|
53
|
-
* Await delivery/backpressure
|
|
54
|
-
* or delivery channel exists,
|
|
53
|
+
* Await delivery/backpressure; updates are spaced at least 100 ms per invocation.
|
|
54
|
+
* Resolves without sending when no progress token or delivery channel exists,
|
|
55
|
+
* or after cancellation/completion. Active updates
|
|
55
56
|
* require finite values and strictly increasing progress; invalid updates reject.
|
|
56
57
|
* Delivery failure cancels the request and reaches transport diagnostics.
|
|
57
58
|
*/
|
package/src/tools/progress.ts
CHANGED
|
@@ -18,20 +18,45 @@ const update = z.object({
|
|
|
18
18
|
message: z.string().optional(),
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
+
/** Tokens share the request-ID wire format, but have a separate lifetime. */
|
|
22
|
+
export function isProgressToken(token: unknown): token is ProgressToken {
|
|
23
|
+
return (
|
|
24
|
+
typeof token === "string" ||
|
|
25
|
+
(typeof token === "number" &&
|
|
26
|
+
Number.isFinite(token) &&
|
|
27
|
+
Number.isInteger(token))
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Cancellation must release the timer as well as the awaiting handler. */
|
|
32
|
+
function waitForProgress(milliseconds: number, signal: AbortSignal) {
|
|
33
|
+
return new Promise<void>((resolve, reject) => {
|
|
34
|
+
const abort = () => {
|
|
35
|
+
clearTimeout(timer);
|
|
36
|
+
reject(signal.reason);
|
|
37
|
+
};
|
|
38
|
+
const timer = setTimeout(() => {
|
|
39
|
+
signal.removeEventListener("abort", abort);
|
|
40
|
+
resolve();
|
|
41
|
+
}, milliseconds);
|
|
42
|
+
if (signal.aborted) abort();
|
|
43
|
+
else signal.addEventListener("abort", abort, { once: true });
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
21
47
|
/** One invocation owns its reporter, ordering, and completion boundary. */
|
|
22
48
|
export function createProgressReporter(
|
|
23
|
-
|
|
49
|
+
requestSignal: AbortSignal,
|
|
24
50
|
token: unknown,
|
|
25
51
|
send?: (message: ProgressNotification) => void | Promise<void>,
|
|
26
52
|
) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Number.isFinite(token) &&
|
|
31
|
-
Number.isInteger(token));
|
|
53
|
+
const lifetime = new AbortController();
|
|
54
|
+
const signal = AbortSignal.any([requestSignal, lifetime.signal]);
|
|
55
|
+
const validToken = isProgressToken(token);
|
|
32
56
|
let open = true;
|
|
33
57
|
let previous = Number.NEGATIVE_INFINITY;
|
|
34
58
|
let writes = Promise.resolve();
|
|
59
|
+
let lastSent = Number.NEGATIVE_INFINITY;
|
|
35
60
|
const report = (value: ToolProgress): Promise<void> => {
|
|
36
61
|
if (!open || signal.aborted || !validToken || !send)
|
|
37
62
|
return Promise.resolve();
|
|
@@ -56,13 +81,23 @@ export function createProgressReporter(
|
|
|
56
81
|
},
|
|
57
82
|
};
|
|
58
83
|
const write = writes.then(async () => {
|
|
59
|
-
if (!signal.aborted)
|
|
84
|
+
if (!signal.aborted) {
|
|
85
|
+
// Pace each operation without dropping updates or bypassing backpressure.
|
|
86
|
+
let remaining = 100 - (performance.now() - lastSent);
|
|
87
|
+
while (remaining > 0) {
|
|
88
|
+
await waitForProgress(Math.ceil(remaining), signal);
|
|
89
|
+
remaining = 100 - (performance.now() - lastSent);
|
|
90
|
+
}
|
|
60
91
|
await abortable(
|
|
61
92
|
Promise.resolve().then(() => {
|
|
62
|
-
if (!signal.aborted)
|
|
93
|
+
if (!signal.aborted) {
|
|
94
|
+
lastSent = performance.now();
|
|
95
|
+
return send(message);
|
|
96
|
+
}
|
|
63
97
|
}),
|
|
64
98
|
signal,
|
|
65
99
|
);
|
|
100
|
+
}
|
|
66
101
|
});
|
|
67
102
|
// The awaiting handler observes failures; keep the queue usable for cleanup.
|
|
68
103
|
writes = write.catch(() => {});
|
|
@@ -76,6 +111,9 @@ export function createProgressReporter(
|
|
|
76
111
|
},
|
|
77
112
|
close() {
|
|
78
113
|
open = false;
|
|
114
|
+
lifetime.abort(
|
|
115
|
+
new DOMException("Progress reporting closed", "AbortError"),
|
|
116
|
+
);
|
|
79
117
|
},
|
|
80
118
|
};
|
|
81
119
|
}
|