@chrischall/mcp-utils 2.10.0 → 2.12.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 (114) hide show
  1. package/README.md +448 -15
  2. package/dist/concurrency/index.d.ts +3 -1
  3. package/dist/concurrency/index.d.ts.map +1 -1
  4. package/dist/concurrency/index.js +3 -1
  5. package/dist/concurrency/index.js.map +1 -1
  6. package/dist/concurrency/verify-after-write.d.ts +84 -0
  7. package/dist/concurrency/verify-after-write.d.ts.map +1 -0
  8. package/dist/concurrency/verify-after-write.js +108 -0
  9. package/dist/concurrency/verify-after-write.js.map +1 -0
  10. package/dist/errors/index.d.ts +35 -1
  11. package/dist/errors/index.d.ts.map +1 -1
  12. package/dist/errors/index.js +79 -1
  13. package/dist/errors/index.js.map +1 -1
  14. package/dist/fetchproxy/fallback.d.ts +130 -0
  15. package/dist/fetchproxy/fallback.d.ts.map +1 -0
  16. package/dist/fetchproxy/fallback.js +199 -0
  17. package/dist/fetchproxy/fallback.js.map +1 -0
  18. package/dist/fetchproxy/index.d.ts +31 -1
  19. package/dist/fetchproxy/index.d.ts.map +1 -1
  20. package/dist/fetchproxy/index.js +120 -13
  21. package/dist/fetchproxy/index.js.map +1 -1
  22. package/dist/fs/confine.d.ts +9 -0
  23. package/dist/fs/confine.d.ts.map +1 -1
  24. package/dist/fs/confine.js +11 -2
  25. package/dist/fs/confine.js.map +1 -1
  26. package/dist/fs/index.d.ts +9 -2
  27. package/dist/fs/index.d.ts.map +1 -1
  28. package/dist/fs/index.js +13 -4
  29. package/dist/fs/index.js.map +1 -1
  30. package/dist/fs/magic.d.ts +35 -0
  31. package/dist/fs/magic.d.ts.map +1 -0
  32. package/dist/fs/magic.js +110 -0
  33. package/dist/fs/magic.js.map +1 -0
  34. package/dist/fs/output.d.ts +14 -10
  35. package/dist/fs/output.d.ts.map +1 -1
  36. package/dist/fs/output.js +21 -45
  37. package/dist/fs/output.js.map +1 -1
  38. package/dist/fs/upload-guard.d.ts +97 -0
  39. package/dist/fs/upload-guard.d.ts.map +1 -0
  40. package/dist/fs/upload-guard.js +185 -0
  41. package/dist/fs/upload-guard.js.map +1 -0
  42. package/dist/fs/write.d.ts +100 -0
  43. package/dist/fs/write.d.ts.map +1 -0
  44. package/dist/fs/write.js +207 -0
  45. package/dist/fs/write.js.map +1 -0
  46. package/dist/graphql/client.d.ts +223 -0
  47. package/dist/graphql/client.d.ts.map +1 -0
  48. package/dist/graphql/client.js +335 -0
  49. package/dist/graphql/client.js.map +1 -0
  50. package/dist/graphql/index.d.ts +20 -0
  51. package/dist/graphql/index.d.ts.map +1 -0
  52. package/dist/graphql/index.js +20 -0
  53. package/dist/graphql/index.js.map +1 -0
  54. package/dist/graphql/operation-kind.d.ts +58 -0
  55. package/dist/graphql/operation-kind.d.ts.map +1 -0
  56. package/dist/graphql/operation-kind.js +237 -0
  57. package/dist/graphql/operation-kind.js.map +1 -0
  58. package/dist/html/index.d.ts +33 -0
  59. package/dist/html/index.d.ts.map +1 -1
  60. package/dist/html/index.js +81 -1
  61. package/dist/html/index.js.map +1 -1
  62. package/dist/http/index.d.ts +11 -1
  63. package/dist/http/index.d.ts.map +1 -1
  64. package/dist/http/index.js +35 -7
  65. package/dist/http/index.js.map +1 -1
  66. package/dist/response/index.d.ts +1 -0
  67. package/dist/response/index.d.ts.map +1 -1
  68. package/dist/response/index.js +1 -0
  69. package/dist/response/index.js.map +1 -1
  70. package/dist/response/untrusted.d.ts +72 -0
  71. package/dist/response/untrusted.d.ts.map +1 -0
  72. package/dist/response/untrusted.js +55 -0
  73. package/dist/response/untrusted.js.map +1 -0
  74. package/dist/scrape/index.d.ts +75 -0
  75. package/dist/scrape/index.d.ts.map +1 -1
  76. package/dist/scrape/index.js +272 -0
  77. package/dist/scrape/index.js.map +1 -1
  78. package/dist/server/confirm-env.d.ts +12 -2
  79. package/dist/server/confirm-env.d.ts.map +1 -1
  80. package/dist/server/confirm-env.js +49 -3
  81. package/dist/server/confirm-env.js.map +1 -1
  82. package/dist/server/confirm-spent-file.d.ts +7 -0
  83. package/dist/server/confirm-spent-file.d.ts.map +1 -0
  84. package/dist/server/confirm-spent-file.js +118 -0
  85. package/dist/server/confirm-spent-file.js.map +1 -0
  86. package/dist/server/confirm-token.d.ts +12 -2
  87. package/dist/server/confirm-token.d.ts.map +1 -1
  88. package/dist/server/confirm-token.js +8 -1
  89. package/dist/server/confirm-token.js.map +1 -1
  90. package/dist/server/confirm-write.d.ts +152 -0
  91. package/dist/server/confirm-write.d.ts.map +1 -0
  92. package/dist/server/confirm-write.js +107 -0
  93. package/dist/server/confirm-write.js.map +1 -0
  94. package/dist/server/index.d.ts +2 -0
  95. package/dist/server/index.d.ts.map +1 -1
  96. package/dist/server/index.js +2 -0
  97. package/dist/server/index.js.map +1 -1
  98. package/dist/session/atomic-write.d.ts +15 -0
  99. package/dist/session/atomic-write.d.ts.map +1 -0
  100. package/dist/session/atomic-write.js +42 -0
  101. package/dist/session/atomic-write.js.map +1 -0
  102. package/dist/session/file-lock.d.ts +57 -0
  103. package/dist/session/file-lock.d.ts.map +1 -0
  104. package/dist/session/file-lock.js +215 -0
  105. package/dist/session/file-lock.js.map +1 -0
  106. package/dist/session/index.d.ts +118 -2
  107. package/dist/session/index.d.ts.map +1 -1
  108. package/dist/session/index.js +200 -82
  109. package/dist/session/index.js.map +1 -1
  110. package/dist/session/login-page.d.ts +73 -0
  111. package/dist/session/login-page.d.ts.map +1 -0
  112. package/dist/session/login-page.js +143 -0
  113. package/dist/session/login-page.js.map +1 -0
  114. package/package.json +5 -1
@@ -0,0 +1,73 @@
1
+ /**
2
+ * "Did the site just hand me its login page?" — the session-expiry predicate
3
+ * for sites that answer an expired cookie session with an HTTP 200 (or a
4
+ * redirect) rather than a 401.
5
+ *
6
+ * Three fleet servers decided this locally, each differently (fleet-audit#1155):
7
+ * artsonia-mcp's `looksUnauthenticated` (final URL, a manual 3xx `Location`, or
8
+ * the login form rendered in place), signupgenius-mcp's `isSessionExpired`
9
+ * (401, or a non-JSON 200 carrying the login form's markers), and
10
+ * canvas-parent-mcp (401 only). artsonia's first version matched body PROSE and
11
+ * a fan comment reading "You need to log in…" forced a needless re-login on
12
+ * every read of that artwork — so the rules here are STRUCTURAL only:
13
+ *
14
+ * - a configured status (default `401`);
15
+ * - the response's final URL, or its `Location`, whose path+search matches
16
+ * `url` — a login URL merely mentioned in an (encoded) query parameter of
17
+ * another page does not count;
18
+ * - a `<form>` whose `action` matches `form.action`, containing (before its
19
+ * `</form>` or the next `<form>`) an input named `form.field` — or, with no
20
+ * field named, any `type=password` input. User-generated text arrives
21
+ * HTML-escaped, so it cannot forge that structure. The body is only scanned
22
+ * when the response is HTML (or says nothing about its type): JSON carries
23
+ * user text too (signupgenius' lesson).
24
+ *
25
+ * Every scan is linear in the body — this runs on every response of the
26
+ * session — and the body considered is capped at {@link LOGIN_PAGE_SCAN_MAX}.
27
+ */
28
+ /** A response as {@link looksLikeLoginPage} sees it. Every field is optional. */
29
+ export interface LoginPageView {
30
+ status?: number;
31
+ /** The final URL (after any followed redirects). */
32
+ url?: string;
33
+ /** A `Location` header, for a redirect that was not followed. */
34
+ location?: string | null;
35
+ /** The body text, when the caller has it. */
36
+ body?: string;
37
+ /** The `content-type`; a non-HTML type skips the form scan. */
38
+ contentType?: string | null;
39
+ }
40
+ /** What identifies a site's login page. Omitted rules are not checked. */
41
+ export interface LoginPageSignals {
42
+ /** Matched against the path + search of the final URL and of `Location`. */
43
+ url?: RegExp;
44
+ /**
45
+ * The login form. `action` filters forms by their `action` attribute (any
46
+ * form when omitted); `field` names the input that must sit inside it
47
+ * (case-insensitive), defaulting to any `type=password` input.
48
+ */
49
+ form?: {
50
+ action?: RegExp;
51
+ field?: string;
52
+ };
53
+ /** Statuses that mean expired by themselves. Default `[401]`; `[]` for none. */
54
+ statuses?: readonly number[];
55
+ }
56
+ /** Bytes of body {@link looksLikeLoginPage} considers. A login page is small. */
57
+ export declare const LOGIN_PAGE_SCAN_MAX: number;
58
+ /**
59
+ * Whether `page` is the site's login page by `signals` — sync and pure, for a
60
+ * caller that already has the pieces (a custom transport, a fetchproxy result,
61
+ * a signed-out check outside the session manager).
62
+ */
63
+ export declare function looksLikeLoginPage(page: LoginPageView, signals: LoginPageSignals): boolean;
64
+ /**
65
+ * An `isExpired` for {@link CookieSessionManager}: true when the response is
66
+ * the login page by `signals` (see {@link looksLikeLoginPage}).
67
+ *
68
+ * Takes a web `Response` — its body read from a CLONE, and only when a `form`
69
+ * rule needs it, so the caller can still read the original — or any object
70
+ * with the {@link LoginPageView} fields (artsonia's `{ url, location, body }`).
71
+ */
72
+ export declare function expiredByLoginPage(signals: LoginPageSignals): (res: Response | LoginPageView) => Promise<boolean>;
73
+ //# sourceMappingURL=login-page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-page.d.ts","sourceRoot":"","sources":["../../src/session/login-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,iFAAiF;AACjF,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iEAAiE;IACjE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,6CAA6C;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B,4EAA4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,gFAAgF;IAChF,QAAQ,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAED,iFAAiF;AACjF,eAAO,MAAM,mBAAmB,QAAa,CAAC;AA0D9C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAe1F;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,gBAAgB,GACxB,CAAC,GAAG,EAAE,QAAQ,GAAG,aAAa,KAAK,OAAO,CAAC,OAAO,CAAC,CAqBrD"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * "Did the site just hand me its login page?" — the session-expiry predicate
3
+ * for sites that answer an expired cookie session with an HTTP 200 (or a
4
+ * redirect) rather than a 401.
5
+ *
6
+ * Three fleet servers decided this locally, each differently (fleet-audit#1155):
7
+ * artsonia-mcp's `looksUnauthenticated` (final URL, a manual 3xx `Location`, or
8
+ * the login form rendered in place), signupgenius-mcp's `isSessionExpired`
9
+ * (401, or a non-JSON 200 carrying the login form's markers), and
10
+ * canvas-parent-mcp (401 only). artsonia's first version matched body PROSE and
11
+ * a fan comment reading "You need to log in…" forced a needless re-login on
12
+ * every read of that artwork — so the rules here are STRUCTURAL only:
13
+ *
14
+ * - a configured status (default `401`);
15
+ * - the response's final URL, or its `Location`, whose path+search matches
16
+ * `url` — a login URL merely mentioned in an (encoded) query parameter of
17
+ * another page does not count;
18
+ * - a `<form>` whose `action` matches `form.action`, containing (before its
19
+ * `</form>` or the next `<form>`) an input named `form.field` — or, with no
20
+ * field named, any `type=password` input. User-generated text arrives
21
+ * HTML-escaped, so it cannot forge that structure. The body is only scanned
22
+ * when the response is HTML (or says nothing about its type): JSON carries
23
+ * user text too (signupgenius' lesson).
24
+ *
25
+ * Every scan is linear in the body — this runs on every response of the
26
+ * session — and the body considered is capped at {@link LOGIN_PAGE_SCAN_MAX}.
27
+ */
28
+ /** Bytes of body {@link looksLikeLoginPage} considers. A login page is small. */
29
+ export const LOGIN_PAGE_SCAN_MAX = 512 * 1024;
30
+ /** Longest form open tag read for its `action` (a real one is far shorter). */
31
+ const MAX_TAG = 4096;
32
+ const FORM_OPEN = /<form\b/gi;
33
+ const FORM_CLOSE = /<\/form\s*>/gi;
34
+ const ACTION_ATTR = /\baction\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>"']+))/i;
35
+ const PASSWORD_INPUT = /\btype\s*=\s*["']?password(?=["'\s/>])/i;
36
+ function escapeRegExp(s) {
37
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
38
+ }
39
+ /** Path + search of `u` (relative values resolved against a dummy origin); `u` itself if unparseable. */
40
+ function pathAndSearch(u) {
41
+ try {
42
+ const parsed = new URL(u, 'http://localhost');
43
+ return parsed.pathname + parsed.search;
44
+ }
45
+ catch {
46
+ return u;
47
+ }
48
+ }
49
+ function indicesOf(re, body) {
50
+ const out = [];
51
+ re.lastIndex = 0;
52
+ for (let m = re.exec(body); m !== null; m = re.exec(body))
53
+ out.push(m.index);
54
+ return out;
55
+ }
56
+ function rendersLoginForm(body, form) {
57
+ const fieldRe = form.field !== undefined
58
+ ? new RegExp(`\\bname\\s*=\\s*["']?${escapeRegExp(form.field)}(?=["'\\s/>])`, 'i')
59
+ : PASSWORD_INPUT;
60
+ const opens = indicesOf(FORM_OPEN, body);
61
+ const closes = indicesOf(FORM_CLOSE, body);
62
+ let c = 0;
63
+ for (let i = 0; i < opens.length; i++) {
64
+ const start = opens[i];
65
+ // Forms do not nest: a form's content ends at its `</form>` or the next `<form>`.
66
+ while (c < closes.length && closes[c] < start)
67
+ c++;
68
+ const nextOpen = opens[i + 1] ?? body.length;
69
+ const end = Math.min(closes[c] ?? body.length, nextOpen);
70
+ const region = body.slice(start, end);
71
+ if (form.action !== undefined) {
72
+ const tagEnd = region.indexOf('>');
73
+ const tag = region.slice(0, tagEnd === -1 ? Math.min(region.length, MAX_TAG) : Math.min(tagEnd, MAX_TAG));
74
+ const m = ACTION_ATTR.exec(tag);
75
+ const action = m === null ? undefined : (m[1] ?? m[2] ?? m[3]);
76
+ if (action === undefined || !form.action.test(action))
77
+ continue;
78
+ }
79
+ if (fieldRe.test(region))
80
+ return true;
81
+ }
82
+ return false;
83
+ }
84
+ /**
85
+ * Whether `page` is the site's login page by `signals` — sync and pure, for a
86
+ * caller that already has the pieces (a custom transport, a fetchproxy result,
87
+ * a signed-out check outside the session manager).
88
+ */
89
+ export function looksLikeLoginPage(page, signals) {
90
+ const statuses = signals.statuses ?? [401];
91
+ if (page.status !== undefined && statuses.includes(page.status))
92
+ return true;
93
+ if (signals.url !== undefined) {
94
+ const re = signals.url;
95
+ if (page.url && re.test(pathAndSearch(page.url)))
96
+ return true;
97
+ if (page.location && re.test(pathAndSearch(page.location)))
98
+ return true;
99
+ }
100
+ if (signals.form !== undefined && page.body) {
101
+ const ct = page.contentType;
102
+ if (ct && !/html/i.test(ct))
103
+ return false;
104
+ const body = page.body.length > LOGIN_PAGE_SCAN_MAX ? page.body.slice(0, LOGIN_PAGE_SCAN_MAX) : page.body;
105
+ return rendersLoginForm(body, signals.form);
106
+ }
107
+ return false;
108
+ }
109
+ /**
110
+ * An `isExpired` for {@link CookieSessionManager}: true when the response is
111
+ * the login page by `signals` (see {@link looksLikeLoginPage}).
112
+ *
113
+ * Takes a web `Response` — its body read from a CLONE, and only when a `form`
114
+ * rule needs it, so the caller can still read the original — or any object
115
+ * with the {@link LoginPageView} fields (artsonia's `{ url, location, body }`).
116
+ */
117
+ export function expiredByLoginPage(signals) {
118
+ return async (res) => {
119
+ if (res instanceof Response) {
120
+ const view = {
121
+ status: res.status,
122
+ url: res.url,
123
+ location: res.headers.get('location'),
124
+ contentType: res.headers.get('content-type'),
125
+ };
126
+ if (looksLikeLoginPage(view, { ...signals, form: undefined }))
127
+ return true;
128
+ if (signals.form === undefined || res.body === null)
129
+ return false;
130
+ if (view.contentType && !/html/i.test(view.contentType))
131
+ return false;
132
+ try {
133
+ view.body = await res.clone().text();
134
+ }
135
+ catch {
136
+ return false;
137
+ }
138
+ return looksLikeLoginPage(view, signals);
139
+ }
140
+ return looksLikeLoginPage(res, signals);
141
+ };
142
+ }
143
+ //# sourceMappingURL=login-page.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-page.js","sourceRoot":"","sources":["../../src/session/login-page.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AA6BH,iFAAiF;AACjF,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,GAAG,IAAI,CAAC;AAE9C,+EAA+E;AAC/E,MAAM,OAAO,GAAG,IAAI,CAAC;AAErB,MAAM,SAAS,GAAG,WAAW,CAAC;AAC9B,MAAM,UAAU,GAAG,eAAe,CAAC;AACnC,MAAM,WAAW,GAAG,qDAAqD,CAAC;AAC1E,MAAM,cAAc,GAAG,yCAAyC,CAAC;AAEjE,SAAS,YAAY,CAAC,CAAS;IAC7B,OAAO,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;AAClD,CAAC;AAED,yGAAyG;AACzG,SAAS,aAAa,CAAC,CAAS;IAC9B,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAC9C,OAAO,MAAM,CAAC,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,EAAU,EAAE,IAAY;IACzC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,EAAE,CAAC,SAAS,GAAG,CAAC,CAAC;IACjB,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAC7E,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,IAA2C;IACjF,MAAM,OAAO,GACX,IAAI,CAAC,KAAK,KAAK,SAAS;QACtB,CAAC,CAAC,IAAI,MAAM,CAAC,wBAAwB,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,EAAE,GAAG,CAAC;QAClF,CAAC,CAAC,cAAc,CAAC;IACrB,MAAM,KAAK,GAAG,SAAS,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;IAC3C,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAE,CAAC;QACxB,kFAAkF;QAClF,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,IAAI,MAAM,CAAC,CAAC,CAAE,GAAG,KAAK;YAAE,CAAC,EAAE,CAAC;QACpD,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC;QAC7C,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACzD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QACtC,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACnC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;YAC1G,MAAM,CAAC,GAAG,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAChC,MAAM,MAAM,GAAG,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC/D,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;gBAAE,SAAS;QAClE,CAAC;QACD,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,IAAI,CAAC;IACxC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAmB,EAAE,OAAyB;IAC/E,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,CAAC;IAC3C,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,IAAI,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAC7E,IAAI,OAAO,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC9B,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC;QACvB,IAAI,IAAI,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;QAC9D,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;IAC1E,CAAC;IACD,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5C,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,CAAC;QAC5B,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAAE,OAAO,KAAK,CAAC;QAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC1G,OAAO,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAyB;IAEzB,OAAO,KAAK,EAAE,GAAG,EAAE,EAAE;QACnB,IAAI,GAAG,YAAY,QAAQ,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAkB;gBAC1B,MAAM,EAAE,GAAG,CAAC,MAAM;gBAClB,GAAG,EAAE,GAAG,CAAC,GAAG;gBACZ,QAAQ,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC;gBACrC,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;aAC7C,CAAC;YACF,IAAI,kBAAkB,CAAC,IAAI,EAAE,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC3E,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,IAAI,GAAG,CAAC,IAAI,KAAK,IAAI;gBAAE,OAAO,KAAK,CAAC;YAClE,IAAI,IAAI,CAAC,WAAW,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;gBAAE,OAAO,KAAK,CAAC;YACtE,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,GAAG,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,CAAC;YACvC,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;YACD,OAAO,kBAAkB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,kBAAkB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrischall/mcp-utils",
3
- "version": "2.10.0",
3
+ "version": "2.12.0",
4
4
  "description": "Shared scaffolding for the chrischall MCP fleet — server bootstrap, tool-result formatting, helpful errors, hardened env/config, a bearer API-client kit, zod atoms, session registries, a fetchproxy transport adapter, auth resolver skeletons, an in-memory test harness, and opt-in HTML helpers. The generic MCP glue hoisted out of ~19 sibling servers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -39,6 +39,10 @@
39
39
  "./healthcheck": {
40
40
  "import": "./dist/healthcheck/index.js",
41
41
  "types": "./dist/healthcheck/index.d.ts"
42
+ },
43
+ "./graphql": {
44
+ "import": "./dist/graphql/index.js",
45
+ "types": "./dist/graphql/index.d.ts"
42
46
  }
43
47
  },
44
48
  "files": [