@toolgrain/mcp 0.2.0-rc.0 → 0.3.0-alpha.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.
Files changed (65) hide show
  1. package/CHANGELOG.md +18 -4
  2. package/README.md +6 -3
  3. package/dist/abort.d.ts.map +1 -0
  4. package/dist/abort.js.map +1 -0
  5. package/dist/auth/authorization.js +1 -1
  6. package/dist/auth/authorization.js.map +1 -1
  7. package/dist/connection.d.ts.map +1 -1
  8. package/dist/connection.js +5 -1
  9. package/dist/connection.js.map +1 -1
  10. package/dist/dispatcher.d.ts +4 -2
  11. package/dist/dispatcher.d.ts.map +1 -1
  12. package/dist/dispatcher.js.map +1 -1
  13. package/dist/execute-request.d.ts.map +1 -1
  14. package/dist/execute-request.js +9 -1
  15. package/dist/execute-request.js.map +1 -1
  16. package/dist/index.d.ts +1 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js.map +1 -1
  19. package/dist/legacy/dispatcher.d.ts.map +1 -1
  20. package/dist/legacy/dispatcher.js +1 -1
  21. package/dist/legacy/dispatcher.js.map +1 -1
  22. package/dist/legacy/messages.d.ts +12 -4
  23. package/dist/legacy/messages.d.ts.map +1 -1
  24. package/dist/legacy/messages.js +2 -2
  25. package/dist/legacy/messages.js.map +1 -1
  26. package/dist/modern/dispatcher.d.ts.map +1 -1
  27. package/dist/modern/dispatcher.js +1 -1
  28. package/dist/modern/dispatcher.js.map +1 -1
  29. package/dist/modern/metadata.d.ts.map +1 -1
  30. package/dist/modern/metadata.js +1 -2
  31. package/dist/modern/metadata.js.map +1 -1
  32. package/dist/server.d.ts +8 -1
  33. package/dist/server.d.ts.map +1 -1
  34. package/dist/server.js +49 -2
  35. package/dist/server.js.map +1 -1
  36. package/dist/tools/context.d.ts +10 -1
  37. package/dist/tools/context.d.ts.map +1 -1
  38. package/dist/tools/context.js +2 -1
  39. package/dist/tools/context.js.map +1 -1
  40. package/dist/tools/progress.d.ts +17 -0
  41. package/dist/tools/progress.d.ts.map +1 -0
  42. package/dist/tools/progress.js +62 -0
  43. package/dist/tools/progress.js.map +1 -0
  44. package/dist/transport.d.ts +4 -4
  45. package/dist/transport.d.ts.map +1 -1
  46. package/dist/transport.js.map +1 -1
  47. package/package.json +3 -3
  48. package/src/auth/authorization.ts +1 -1
  49. package/src/connection.ts +7 -2
  50. package/src/dispatcher.ts +4 -1
  51. package/src/execute-request.ts +16 -1
  52. package/src/index.ts +1 -0
  53. package/src/legacy/dispatcher.ts +1 -0
  54. package/src/legacy/messages.ts +2 -2
  55. package/src/modern/dispatcher.ts +1 -0
  56. package/src/modern/metadata.ts +1 -2
  57. package/src/server.ts +76 -10
  58. package/src/tools/context.ts +11 -0
  59. package/src/tools/progress.ts +81 -0
  60. package/src/transport.ts +7 -4
  61. package/dist/auth/abort.d.ts.map +0 -1
  62. package/dist/auth/abort.js.map +0 -1
  63. /package/dist/{auth/abort.d.ts → abort.d.ts} +0 -0
  64. /package/dist/{auth/abort.js → abort.js} +0 -0
  65. /package/src/{auth/abort.ts → abort.ts} +0 -0
@@ -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;AA2CzD;;;;;;;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;IAEpD,MAAM,GAAG,GAAG,UAAU,CACpB;QACE,MAAM;QACN,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"}
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;AAoDzD;;;;;;;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"}
@@ -0,0 +1,17 @@
1
+ import type { ProgressNotification } from "@toolgrain/protocol";
2
+ /** One progress update; values are finite numbers and progress must strictly increase. */
3
+ export interface ToolProgress {
4
+ /** Work completed so far, in application-defined units; the first value may be zero. */
5
+ readonly progress: number;
6
+ /** Optional total work, in the same units; omit when unknown. */
7
+ readonly total?: number;
8
+ /** Optional public status message sent to the client. */
9
+ readonly message?: string;
10
+ }
11
+ /** One invocation owns its reporter, ordering, and completion boundary. */
12
+ export declare function createProgressReporter(signal: AbortSignal, token: unknown, send?: (message: ProgressNotification) => void | Promise<void>): {
13
+ report: (value: ToolProgress) => Promise<void>;
14
+ finish(): Promise<void>;
15
+ close(): void;
16
+ };
17
+ //# sourceMappingURL=progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.d.ts","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAiB,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,2EAA2E;AAC3E,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,WAAW,EACnB,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;oBAUvC,YAAY,KAAG,OAAO,CAAC,IAAI,CAAC;;;EA8CpD"}
@@ -0,0 +1,62 @@
1
+ import { z } from "zod";
2
+ import { abortable } from "../abort.js";
3
+ const update = z.object({
4
+ progress: z.number().finite(),
5
+ total: z.number().finite().optional(),
6
+ message: z.string().optional(),
7
+ });
8
+ /** One invocation owns its reporter, ordering, and completion boundary. */
9
+ export function createProgressReporter(signal, token, send) {
10
+ const validToken = typeof token === "string" ||
11
+ (typeof token === "number" &&
12
+ Number.isFinite(token) &&
13
+ Number.isInteger(token));
14
+ let open = true;
15
+ let previous = Number.NEGATIVE_INFINITY;
16
+ let writes = Promise.resolve();
17
+ const report = (value) => {
18
+ if (!open || signal.aborted || !validToken || !send)
19
+ return Promise.resolve();
20
+ const parsed = update.safeParse(value);
21
+ if (!parsed.success)
22
+ return Promise.reject(new TypeError("Invalid progress update"));
23
+ if (parsed.data.progress <= previous)
24
+ return Promise.reject(new TypeError("Progress must strictly increase"));
25
+ previous = parsed.data.progress;
26
+ const message = {
27
+ jsonrpc: "2.0",
28
+ method: "notifications/progress",
29
+ params: {
30
+ progress: parsed.data.progress,
31
+ ...(parsed.data.total === undefined
32
+ ? {}
33
+ : { total: parsed.data.total }),
34
+ ...(parsed.data.message === undefined
35
+ ? {}
36
+ : { message: parsed.data.message }),
37
+ progressToken: token,
38
+ },
39
+ };
40
+ const write = writes.then(async () => {
41
+ if (!signal.aborted)
42
+ await abortable(Promise.resolve().then(() => {
43
+ if (!signal.aborted)
44
+ return send(message);
45
+ }), signal);
46
+ });
47
+ // The awaiting handler observes failures; keep the queue usable for cleanup.
48
+ writes = write.catch(() => { });
49
+ return write;
50
+ };
51
+ return {
52
+ report,
53
+ async finish() {
54
+ open = false;
55
+ await writes;
56
+ },
57
+ close() {
58
+ open = false;
59
+ },
60
+ };
61
+ }
62
+ //# sourceMappingURL=progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"progress.js","sourceRoot":"","sources":["../../src/tools/progress.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { ProtocolResponse } from "@toolgrain/protocol";
1
+ import type { ProgressNotification, ProtocolResponse } from "@toolgrain/protocol";
2
2
  import type { Server } from "./server.js";
3
3
  /** Callbacks are synchronous; message processing runs independently. */
4
4
  export interface TransportCallbacks {
@@ -21,10 +21,10 @@ export interface Transport {
21
21
  */
22
22
  start(callbacks: TransportCallbacks): Promise<void>;
23
23
  /**
24
- * Send one protocol response, resolving after the adapter's write/backpressure handling.
25
- * @param message - Response envelope already converted for the client's protocol revision.
24
+ * Send one response or progress notification, resolving after write/backpressure handling.
25
+ * @param message - Envelope already converted for the client's protocol revision.
26
26
  */
27
- send(message: ProtocolResponse): Promise<void>;
27
+ send(message: ProtocolResponse | ProgressNotification): Promise<void>;
28
28
  /** Release adapter-owned resources; closing is terminal and must tolerate repeated calls. */
29
29
  close(): Promise<void>;
30
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,6FAA6F;IAC7F,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,oFAAoF;IACpF,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,yFAAyF;IACzF,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,4FAA4F;IAC5F,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,gFAAgF;AAChF,MAAM,WAAW,SAAS;IACxB,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B;;;OAGG;IACH,KAAK,CAAC,SAAS,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,6FAA6F;IAC7F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,wFAAwF;IACxF,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB;;;;OAIG;IACH,KAAK,CACH,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,SAAS,CAAC,GACzD,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,0FAA0F;IAC1F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,GAAG,gBAAgB,GACtC,SAAS,IAAI,gBAAgB,CAE/B"}
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,wEAAwE;AACxE,MAAM,WAAW,kBAAkB;IACjC,6FAA6F;IAC7F,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,oFAAoF;IACpF,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,yFAAyF;IACzF,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,4FAA4F;IAC5F,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,gFAAgF;AAChF,MAAM,WAAW,SAAS;IACxB,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B;;;OAGG;IACH,KAAK,CAAC,SAAS,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,6FAA6F;IAC7F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B,wFAAwF;IACxF,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB;;;;OAIG;IACH,KAAK,CACH,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,SAAS,CAAC,GACzD,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,0FAA0F;IAC1F,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,GAAG,gBAAgB,GACtC,SAAS,IAAI,gBAAgB,CAE/B"}
@@ -1 +1 @@
1
- {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAkDA,MAAM,UAAU,kBAAkB,CAChC,SAAuC;IAEvC,OAAO,MAAM,IAAI,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AAqDA,MAAM,UAAU,kBAAkB,CAChC,SAAuC;IAEvC,OAAO,MAAM,IAAI,SAAS,IAAI,SAAS,CAAC,IAAI,KAAK,SAAS,CAAC;AAC7D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toolgrain/mcp",
3
- "version": "0.2.0-rc.0",
3
+ "version": "0.3.0-alpha.0",
4
4
  "description": "Define reusable, typed MCP tools and servers with Zod validation.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -23,7 +23,7 @@
23
23
  "publishConfig": {
24
24
  "access": "public",
25
25
  "registry": "https://registry.npmjs.org/",
26
- "tag": "rc"
26
+ "tag": "alpha"
27
27
  },
28
28
  "type": "module",
29
29
  "exports": {
@@ -59,7 +59,7 @@
59
59
  "@toolgrain/repo-tools": "0.0.0"
60
60
  },
61
61
  "dependencies": {
62
- "@toolgrain/protocol": "0.2.0-rc.0"
62
+ "@toolgrain/protocol": "0.3.0-alpha.0"
63
63
  },
64
64
  "engines": {
65
65
  "node": "^24.20.0",
@@ -1,4 +1,4 @@
1
- import { abortable } from "./abort.js";
1
+ import { abortable } from "../abort.js";
2
2
  import { identitySnapshot } from "./context.js";
3
3
  import { AuthError } from "./errors.js";
4
4
  import { validateAuthScopes } from "./scopes.js";
package/src/connection.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  INVALID_REQUEST,
3
3
  PARSE_ERROR,
4
+ type ProgressNotification,
4
5
  type ProtocolResponse,
5
6
  } from "@toolgrain/protocol";
6
7
  import { type ErrorHandler, reportError } from "./diagnostics.js";
@@ -134,7 +135,7 @@ class Connection implements ConnectionLifecycle {
134
135
  }
135
136
 
136
137
  private send(
137
- message: ProtocolResponse,
138
+ message: ProtocolResponse | ProgressNotification,
138
139
  isActive: () => boolean = () => this.isOpen(),
139
140
  ): Promise<void> {
140
141
  const write = this.writes.then(async () => {
@@ -193,7 +194,11 @@ class Connection implements ConnectionLifecycle {
193
194
  const response = await executeRequest(
194
195
  this.dispatch,
195
196
  request,
196
- { signal: pending.signal, client: this.legacy.client() },
197
+ {
198
+ signal: pending.signal,
199
+ client: this.legacy.client(),
200
+ notify: (message) => this.send(message, pending.isActive),
201
+ },
197
202
  (error) => this.report(error),
198
203
  prepared.protocolVersion,
199
204
  );
package/src/dispatcher.ts CHANGED
@@ -2,6 +2,7 @@ import {
2
2
  type Implementation,
3
3
  type JSONRPCErrorResponse,
4
4
  LEGACY_PROTOCOL_VERSION,
5
+ type ProgressNotification,
5
6
  type ProtocolResponse,
6
7
  type SupportedProtocolVersion,
7
8
  } from "@toolgrain/protocol";
@@ -16,7 +17,7 @@ import { createModernDispatcher } from "./modern/dispatcher.js";
16
17
  import type { Request } from "./protocol/messages.js";
17
18
  import { validateVersionContext } from "./protocol/version-metadata.js";
18
19
  import type { ToolAdapter } from "./tools/adapter.js";
19
- import type { ToolClient } from "./tools/context.js";
20
+ import type { ToolClient, ToolContext } from "./tools/context.js";
20
21
 
21
22
  /** Per-request execution state supplied by the connection or independent caller. */
22
23
  export interface RequestScope {
@@ -24,6 +25,8 @@ export interface RequestScope {
24
25
  /** Client details negotiated by legacy initialization, when the connection has them. */
25
26
  client?: ToolClient | undefined;
26
27
  auth?: AuthIdentity;
28
+ notify?: (message: ProgressNotification) => void | Promise<void>;
29
+ reportProgress?: ToolContext["reportProgress"];
27
30
  }
28
31
 
29
32
  export type Dispatcher = (
@@ -7,6 +7,7 @@ import { type ErrorHandler, reportError } from "./diagnostics.js";
7
7
  import type { Dispatcher, RequestScope } from "./dispatcher.js";
8
8
  import type { Request } from "./protocol/messages.js";
9
9
  import { ProtocolError, protocolError } from "./protocol/responses.js";
10
+ import { createProgressReporter } from "./tools/progress.js";
10
11
 
11
12
  /** Shared dispatch boundary for connection-scoped and independent requests. */
12
13
  export async function executeRequest(
@@ -17,8 +18,20 @@ export async function executeRequest(
17
18
  protocolVersion?: SupportedProtocolVersion,
18
19
  ): Promise<ProtocolResponse | undefined> {
19
20
  const isActive = () => !scope.signal.aborted;
21
+ let progress: ReturnType<typeof createProgressReporter> | undefined;
20
22
  try {
21
- const response = await dispatch(request, scope, protocolVersion);
23
+ progress = createProgressReporter(
24
+ scope.signal,
25
+ (request.params?._meta as Record<string, unknown> | undefined)
26
+ ?.progressToken,
27
+ scope.notify,
28
+ );
29
+ const response = await dispatch(
30
+ request,
31
+ { ...scope, reportProgress: progress.report },
32
+ protocolVersion,
33
+ );
34
+ await progress.finish();
22
35
  return isActive() ? response : undefined;
23
36
  } catch (error) {
24
37
  if (!isActive()) return;
@@ -31,5 +44,7 @@ export async function executeRequest(
31
44
  request.id,
32
45
  undefined,
33
46
  );
47
+ } finally {
48
+ progress?.close();
34
49
  }
35
50
  }
package/src/index.ts CHANGED
@@ -26,6 +26,7 @@ export { toolContext } from "./tools/context.js";
26
26
  export type { Tool, ToolDefinition, ToolMetadata } from "./tools/definition.js";
27
27
  export { tool } from "./tools/definition.js";
28
28
  export { ToolNameError } from "./tools/name.js";
29
+ export type { ToolProgress } from "./tools/progress.js";
29
30
  export type { ToolMap } from "./tools/registry.js";
30
31
  export type {
31
32
  McpErrorResult,
@@ -61,6 +61,7 @@ export function createLegacyDispatcher(
61
61
  },
62
62
  scope.auth,
63
63
  providers,
64
+ scope.reportProgress,
64
65
  ),
65
66
  );
66
67
  break;
@@ -1,8 +1,8 @@
1
1
  import { z } from "zod";
2
- import { object } from "../protocol/messages.js";
2
+ import { object, requestId } from "../protocol/messages.js";
3
3
 
4
4
  export const commonParams = z.looseObject({
5
- _meta: object.optional(),
5
+ _meta: z.looseObject({ progressToken: requestId.optional() }).optional(),
6
6
  });
7
7
  export const initializeParams = commonParams.extend({
8
8
  protocolVersion: z.string(),
@@ -66,6 +66,7 @@ export function createModernDispatcher(
66
66
  },
67
67
  scope.auth,
68
68
  providers,
69
+ scope.reportProgress,
69
70
  ),
70
71
  );
71
72
  break;
@@ -1,9 +1,8 @@
1
1
  import { z } from "zod";
2
2
  import { icon } from "../protocol/icon.js";
3
- import { object } from "../protocol/messages.js";
3
+ import { object, requestId } from "../protocol/messages.js";
4
4
 
5
5
  const capabilityObject = z.record(z.string(), z.json());
6
- const requestId = z.union([z.string(), z.number().finite()]);
7
6
  const capabilities = z.looseObject({
8
7
  experimental: z.record(z.string(), capabilityObject).optional(),
9
8
  roots: object.optional(),
package/src/server.ts CHANGED
@@ -1,8 +1,10 @@
1
1
  import type {
2
+ ProgressNotification,
2
3
  ProtocolResponse,
3
4
  Tool as ProtocolTool,
4
5
  SupportedProtocolVersion,
5
6
  } from "@toolgrain/protocol";
7
+ import { abortable } from "./abort.js";
6
8
  import { createAuthorization } from "./auth/authorization.js";
7
9
  import { AuthError, isAuthError } from "./auth/errors.js";
8
10
  import type { AuthMetadata, AuthProvider, AuthScope } from "./auth/types.js";
@@ -21,6 +23,13 @@ import {
21
23
  type Transport,
22
24
  } from "./transport.js";
23
25
 
26
+ // Internal adapter contract: final delivery remains part of the active request.
27
+ // A global symbol preserves the hook across compatible package copies.
28
+ const responseDelivery = Symbol.for("@toolgrain/mcp/response-delivery/v1");
29
+ interface ResponseDeliveryOptions {
30
+ [responseDelivery]?: (response: ProtocolResponse) => void | Promise<void>;
31
+ }
32
+
24
33
  /** Tool registrations, identity, and shared configuration for an MCP server. */
25
34
  export interface ServerOptions<Tools extends ToolMap = ToolMap> {
26
35
  /** Server implementation name advertised to clients. */
@@ -54,6 +63,13 @@ export interface HandleOptions {
54
63
  protocolVersion?: SupportedProtocolVersion;
55
64
  /** Single-use scope issued by this server's `authorize()`; dispose it in `finally`. */
56
65
  auth?: AuthScope;
66
+ /**
67
+ * Deliver progress notifications for this request, honoring asynchronous
68
+ * backpressure. Omit for a one-response caller; progress then becomes a no-op.
69
+ * The caller owns framing and must abort `signal` when its response channel closes.
70
+ * Callback failure cancels this invocation and reaches `onError(error)` without tool details.
71
+ */
72
+ onNotification?: (message: ProgressNotification) => void | Promise<void>;
57
73
  }
58
74
 
59
75
  /** An MCP dispatcher with one optional transport attachment and terminal shutdown. */
@@ -147,6 +163,9 @@ export function createServer<const Tools extends ToolMap>(
147
163
  running.add(controller);
148
164
  return {
149
165
  signal: controller.signal,
166
+ abort(reason: unknown) {
167
+ controller.abort(reason);
168
+ },
150
169
  finish() {
151
170
  signal?.removeEventListener("abort", abort);
152
171
  running.delete(controller);
@@ -185,10 +204,8 @@ export function createServer<const Tools extends ToolMap>(
185
204
  throw error;
186
205
  }
187
206
  },
188
- async handle(
189
- message,
190
- { signal, protocolVersion, auth }: HandleOptions = {},
191
- ) {
207
+ async handle(message, handleOptions: HandleOptions = {}) {
208
+ const { signal, protocolVersion, auth, onNotification } = handleOptions;
192
209
  if (closed) throw new Error("Server is closed");
193
210
  const identity = authorization.consume(auth);
194
211
  if (auth?.signal.aborted) return;
@@ -204,13 +221,62 @@ export function createServer<const Tools extends ToolMap>(
204
221
  ? AbortSignal.any([pending.signal, auth.signal])
205
222
  : pending.signal;
206
223
  try {
207
- return await executeRequest(
208
- dispatch,
209
- incoming.request,
210
- { signal: combined, ...(identity ? { auth: identity } : {}) },
211
- options.onError,
212
- protocolVersion,
224
+ const response = await abortable(
225
+ executeRequest(
226
+ dispatch,
227
+ incoming.request,
228
+ {
229
+ signal: combined,
230
+ ...(identity ? { auth: identity } : {}),
231
+ ...(onNotification
232
+ ? {
233
+ notify: async (message: ProgressNotification) => {
234
+ try {
235
+ await onNotification(message);
236
+ } catch (error) {
237
+ if (combined.aborted) return;
238
+ reportError(options.onError, error);
239
+ pending.abort(
240
+ new DOMException(
241
+ "Progress delivery failed",
242
+ "AbortError",
243
+ ),
244
+ );
245
+ }
246
+ },
247
+ }
248
+ : {}),
249
+ },
250
+ options.onError,
251
+ protocolVersion,
252
+ ),
253
+ combined,
213
254
  );
255
+ const deliver = (handleOptions as ResponseDeliveryOptions)[
256
+ responseDelivery
257
+ ];
258
+ if (response && deliver) {
259
+ try {
260
+ await abortable(
261
+ Promise.resolve().then(() => {
262
+ combined.throwIfAborted();
263
+ return deliver(response);
264
+ }),
265
+ combined,
266
+ );
267
+ } catch (error) {
268
+ if (!combined.aborted) {
269
+ reportError(options.onError, error);
270
+ pending.abort(
271
+ new DOMException("Response delivery failed", "AbortError"),
272
+ );
273
+ }
274
+ return;
275
+ }
276
+ }
277
+ return response;
278
+ } catch (error) {
279
+ if (!combined.aborted) throw error;
214
280
  } finally {
215
281
  pending.finish();
216
282
  }
@@ -18,6 +18,7 @@ import {
18
18
  } from "../context/registry.js";
19
19
  import { createContextScope } from "../context/scope.js";
20
20
  import type { DeepReadonly } from "./json-value.js";
21
+ import type { ToolProgress } from "./progress.js";
21
22
 
22
23
  /** What the client reported about itself; unverified, so not a basis for security decisions. */
23
24
  export interface ToolClient {
@@ -47,6 +48,14 @@ export interface ToolContext {
47
48
  readonly signal: AbortSignal;
48
49
  /** Frozen request metadata; contains no verified authorization identity. */
49
50
  readonly request: ToolRequest;
51
+ /**
52
+ * Report request-scoped progress; safe to pass to helpers without binding.
53
+ * Await delivery/backpressure. Resolves without sending when no progress token
54
+ * or delivery channel exists, or after cancellation/completion. Active updates
55
+ * require finite values and strictly increasing progress; invalid updates reject.
56
+ * Delivery failure cancels the request and reaches transport diagnostics.
57
+ */
58
+ readonly reportProgress: (update: ToolProgress) => Promise<void>;
50
59
  }
51
60
 
52
61
  /** Optional metadata, cancellation, and provider bindings for a direct-call context. */
@@ -94,10 +103,12 @@ export function createToolContext(
94
103
  { client, ...request }: ToolRequest,
95
104
  auth?: AuthIdentity,
96
105
  providers: ContextRegistry = createContextRegistry(),
106
+ reportProgress: ToolContext["reportProgress"] = () => Promise.resolve(),
97
107
  ): ToolContext {
98
108
  const ctx = attachAuth(
99
109
  {
100
110
  signal,
111
+ reportProgress,
101
112
  request: Object.freeze({
102
113
  ...request,
103
114
  ...(client === undefined
@@ -0,0 +1,81 @@
1
+ import type { ProgressNotification, ProgressToken } from "@toolgrain/protocol";
2
+ import { z } from "zod";
3
+ import { abortable } from "../abort.js";
4
+
5
+ /** One progress update; values are finite numbers and progress must strictly increase. */
6
+ export interface ToolProgress {
7
+ /** Work completed so far, in application-defined units; the first value may be zero. */
8
+ readonly progress: number;
9
+ /** Optional total work, in the same units; omit when unknown. */
10
+ readonly total?: number;
11
+ /** Optional public status message sent to the client. */
12
+ readonly message?: string;
13
+ }
14
+
15
+ const update = z.object({
16
+ progress: z.number().finite(),
17
+ total: z.number().finite().optional(),
18
+ message: z.string().optional(),
19
+ });
20
+
21
+ /** One invocation owns its reporter, ordering, and completion boundary. */
22
+ export function createProgressReporter(
23
+ signal: AbortSignal,
24
+ token: unknown,
25
+ send?: (message: ProgressNotification) => void | Promise<void>,
26
+ ) {
27
+ const validToken =
28
+ typeof token === "string" ||
29
+ (typeof token === "number" &&
30
+ Number.isFinite(token) &&
31
+ Number.isInteger(token));
32
+ let open = true;
33
+ let previous = Number.NEGATIVE_INFINITY;
34
+ let writes = Promise.resolve();
35
+ const report = (value: ToolProgress): Promise<void> => {
36
+ if (!open || signal.aborted || !validToken || !send)
37
+ return Promise.resolve();
38
+ const parsed = update.safeParse(value);
39
+ if (!parsed.success)
40
+ return Promise.reject(new TypeError("Invalid progress update"));
41
+ if (parsed.data.progress <= previous)
42
+ return Promise.reject(new TypeError("Progress must strictly increase"));
43
+ previous = parsed.data.progress;
44
+ const message: ProgressNotification = {
45
+ jsonrpc: "2.0",
46
+ method: "notifications/progress",
47
+ params: {
48
+ progress: parsed.data.progress,
49
+ ...(parsed.data.total === undefined
50
+ ? {}
51
+ : { total: parsed.data.total }),
52
+ ...(parsed.data.message === undefined
53
+ ? {}
54
+ : { message: parsed.data.message }),
55
+ progressToken: token as ProgressToken,
56
+ },
57
+ };
58
+ const write = writes.then(async () => {
59
+ if (!signal.aborted)
60
+ await abortable(
61
+ Promise.resolve().then(() => {
62
+ if (!signal.aborted) return send(message);
63
+ }),
64
+ signal,
65
+ );
66
+ });
67
+ // The awaiting handler observes failures; keep the queue usable for cleanup.
68
+ writes = write.catch(() => {});
69
+ return write;
70
+ };
71
+ return {
72
+ report,
73
+ async finish() {
74
+ open = false;
75
+ await writes;
76
+ },
77
+ close() {
78
+ open = false;
79
+ },
80
+ };
81
+ }
package/src/transport.ts CHANGED
@@ -1,4 +1,7 @@
1
- import type { ProtocolResponse } from "@toolgrain/protocol";
1
+ import type {
2
+ ProgressNotification,
3
+ ProtocolResponse,
4
+ } from "@toolgrain/protocol";
2
5
  import type { Server } from "./server.js";
3
6
 
4
7
  /** Callbacks are synchronous; message processing runs independently. */
@@ -23,10 +26,10 @@ export interface Transport {
23
26
  */
24
27
  start(callbacks: TransportCallbacks): Promise<void>;
25
28
  /**
26
- * Send one protocol response, resolving after the adapter's write/backpressure handling.
27
- * @param message - Response envelope already converted for the client's protocol revision.
29
+ * Send one response or progress notification, resolving after write/backpressure handling.
30
+ * @param message - Envelope already converted for the client's protocol revision.
28
31
  */
29
- send(message: ProtocolResponse): Promise<void>;
32
+ send(message: ProtocolResponse | ProgressNotification): Promise<void>;
30
33
  /** Release adapter-owned resources; closing is terminal and must tolerate repeated calls. */
31
34
  close(): Promise<void>;
32
35
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"abort.d.ts","sourceRoot":"","sources":["../../src/auth/abort.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,CAAC,EACzB,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAChB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,CAAC,CAAC,CASZ"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"abort.js","sourceRoot":"","sources":["../../src/auth/abort.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,MAAM,UAAU,SAAS,CACvB,IAAgB,EAChB,MAAmB;IAEnB,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC1C,IAAI,MAAM,CAAC,OAAO;YAAE,KAAK,EAAE,CAAC;;YACvB,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7D,IAAI;aACD,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC;aACrB,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;AACL,CAAC"}
File without changes
File without changes
File without changes