@fruggr/zendesk-mcp-server 3.3.0 → 3.3.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.
@@ -30,9 +30,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30
30
  SOFTWARE.
31
31
  ```
32
32
 
33
- ## @modelcontextprotocol/core@2.2.0
33
+ ## @modelcontextprotocol/core@2.3.1
34
34
 
35
- License: MIT
35
+ License: Apache-2.0
36
36
 
37
37
  ```
38
38
  The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0.
@@ -253,9 +253,9 @@ CC-BY-4.0. See https://creativecommons.org/licenses/by/4.0/legalcode for
253
253
  the full license text.
254
254
  ```
255
255
 
256
- ## @modelcontextprotocol/node@2.1.0
256
+ ## @modelcontextprotocol/node@2.1.1
257
257
 
258
- License: MIT
258
+ License: Apache-2.0
259
259
 
260
260
  ```
261
261
  The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0.
@@ -476,9 +476,9 @@ CC-BY-4.0. See https://creativecommons.org/licenses/by/4.0/legalcode for
476
476
  the full license text.
477
477
  ```
478
478
 
479
- ## @modelcontextprotocol/server@2.2.0
479
+ ## @modelcontextprotocol/server@2.3.1
480
480
 
481
- License: MIT
481
+ License: Apache-2.0
482
482
 
483
483
  ```
484
484
  The MCP project is undergoing a licensing transition from the MIT License to the Apache License, Version 2.0 ("Apache-2.0"). All new code and specification contributions to the project are licensed under Apache-2.0. Documentation contributions (excluding specifications) are licensed under CC-BY-4.0.
@@ -2629,7 +2629,7 @@ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
2629
2629
  SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
2630
2630
  ```
2631
2631
 
2632
- ## mdast-util-find-and-replace@3.0.2
2632
+ ## mdast-util-find-and-replace@3.0.3
2633
2633
 
2634
2634
  License: MIT
2635
2635
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { _ as __toESM, a as zendeskGet, d as createStartupError, f as errnoCode, g as __require, h as __exportAll, i as WebStandardStreamableHTTPServerTransport, l as getOAuthUrls, m as __commonJSMin, o as REQUEST_TIMEOUT_MS, r as silentLogger, s as deadlineSignal, t as createMcpServer, u as STARTUP_DOCS } from "./index.js";
3
- import { d as escapeHtml, l as requestedScope, n as configDir, o as generateCodeChallenge, s as generateCodeVerifier, u as supportedScopes } from "./token-persistence-BeetFqPF.js";
2
+ import { _ as __commonJSMin, a as verifyBearerToken, b as __toESM, c as zendeskGet, f as getOAuthUrls, h as errnoCode, i as WebStandardStreamableHTTPServerTransport, l as REQUEST_TIMEOUT_MS, m as createStartupError, o as OAuthError, p as STARTUP_DOCS, r as silentLogger, s as OAuthErrorCode, t as createMcpServer, u as deadlineSignal, v as __exportAll, y as __require } from "./index.js";
3
+ import { d as escapeHtml, l as requestedScope, n as configDir, o as generateCodeChallenge, s as generateCodeVerifier, u as supportedScopes } from "./token-persistence-BUHh23BM.js";
4
4
  import * as util$1 from "node:util";
5
5
  import { format, inspect } from "node:util";
6
6
  import * as crypto$2 from "node:crypto";
@@ -16,7 +16,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
16
16
  import { accessSync, chmodSync, constants, mkdirSync, readFileSync, renameSync, statSync, writeFileSync } from "node:fs";
17
17
  import { Http2ServerRequest, constants as constants$1 } from "http2";
18
18
  import crypto$1 from "crypto";
19
- //#region node_modules/.pnpm/@hono+node-server@1.19.17_hono@4.13.12/node_modules/@hono/node-server/dist/index.mjs
19
+ //#region node_modules/.pnpm/@hono+node-server@1.19.17_hono@4.13.13/node_modules/@hono/node-server/dist/index.mjs
20
20
  var RequestError = class extends Error {
21
21
  constructor(message, options) {
22
22
  super(message, options);
@@ -504,7 +504,7 @@ var getRequestListener = (fetchCallback, options = {}) => {
504
504
  };
505
505
  };
506
506
  //#endregion
507
- //#region node_modules/.pnpm/@modelcontextprotocol+node@2.1.0_@modelcontextprotocol+server@2.2.0_hono@4.13.12/node_modules/@modelcontextprotocol/node/dist/index.mjs
507
+ //#region node_modules/.pnpm/@modelcontextprotocol+node@2.1.1_@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/node/dist/index.mjs
508
508
  /**
509
509
  * Server transport for Streamable HTTP: this implements the MCP Streamable HTTP transport specification.
510
510
  * It supports both SSE streaming and direct HTTP responses.
@@ -543,8 +543,12 @@ var getRequestListener = (fetchCallback, options = {}) => {
543
543
  *
544
544
  * @example Using with a pre-parsed request body (e.g. Express)
545
545
  * ```ts source="./streamableHttp.examples.ts#NodeStreamableHTTPServerTransport_express"
546
- * app.post('/mcp', (req, res) => {
547
- * transport.handleRequest(req, res, req.body);
546
+ * app.post('/mcp', async (req, res) => {
547
+ * // Stateless example: create a server and a transport per request.
548
+ * const server = new McpServer({ name: 'my-server', version: '1.0.0' });
549
+ * const transport = new NodeStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
550
+ * await server.connect(transport);
551
+ * await transport.handleRequest(req, res, req.body);
548
552
  * });
549
553
  * ```
550
554
  */
@@ -36178,16 +36182,16 @@ const resolveMasterSecret = (input, logger = silentLogger) => {
36178
36182
  //#endregion
36179
36183
  //#region src/auth/server/authorization-server.ts
36180
36184
  const decoder = new TextDecoder();
36181
- const asVerified = (claims, issuer, resource) => {
36182
- const audiences = Array.isArray(claims.aud) ? claims.aud : [claims.aud];
36183
- if (!(claims.iss === issuer && audiences.includes(resource) && typeof claims.exp === "number" && claims.exp * 1e3 > Date.now() && typeof claims.zd === "string" && typeof claims.gid === "string")) return void 0;
36185
+ const asVerified = (claims, issuer) => {
36186
+ if (!(claims.iss === issuer && typeof claims.exp === "number" && typeof claims.zd === "string" && typeof claims.gid === "string")) return void 0;
36184
36187
  return {
36185
36188
  zendeskAccessToken: claims.zd,
36186
36189
  grantId: claims.gid,
36187
36190
  clientId: String(claims.client_id),
36188
36191
  subject: String(claims.sub),
36189
36192
  canWrite: typeof claims.scope === "string" && claims.scope.split(" ").includes("write"),
36190
- expiresAt: claims.exp
36193
+ expiresAt: claims.exp,
36194
+ resource: typeof claims.aud === "string" ? claims.aud : void 0
36191
36195
  };
36192
36196
  };
36193
36197
  const createAuthorizationServer = (options) => {
@@ -36252,7 +36256,7 @@ const createAuthorizationServer = (options) => {
36252
36256
  const keySet = ring.find((set) => set.accessToken.kid === kid);
36253
36257
  if (!keySet) return void 0;
36254
36258
  const { plaintext } = await compactDecrypt(bearer, keySet.accessToken.key);
36255
- return asVerified(JSON.parse(decoder.decode(plaintext)), issuer, resource);
36259
+ return asVerified(JSON.parse(decoder.decode(plaintext)), issuer);
36256
36260
  };
36257
36261
  return {
36258
36262
  issuer,
@@ -36414,11 +36418,36 @@ const resolvePublicUrl = (config, logger = silentLogger) => {
36414
36418
  return `http://${config.host}:${config.port}`;
36415
36419
  };
36416
36420
  const MISSING_BEARER_MESSAGE = "Missing or invalid access token. Sign in through this server: its OAuth authorization server is named in the protected resource metadata.";
36417
- const extractBearer = (request) => {
36421
+ const SPACES = / +/;
36422
+ /**
36423
+ * The access token on this request, or `undefined` when it must be refused.
36424
+ * The SDK parses the header and checks the audience against our `/mcp`
36425
+ * resource (`expectedResource`) and the expiry; decrypting and every other
36426
+ * claim stay with the authorization server.
36427
+ */
36428
+ const authenticate = async (request, as) => {
36418
36429
  const header = request.headers["authorization"];
36419
- if (typeof header !== "string") return void 0;
36420
- if (!header.toLowerCase().startsWith("bearer ")) return void 0;
36421
- return header.slice(7).trim();
36430
+ const verifier = { verifyAccessToken: async (bearer) => {
36431
+ const token = await as.verifyAccessToken(bearer);
36432
+ if (!token) throw new OAuthError(OAuthErrorCode.InvalidToken, "Invalid access token");
36433
+ return {
36434
+ token: bearer,
36435
+ clientId: token.clientId,
36436
+ scopes: [],
36437
+ expiresAt: token.expiresAt,
36438
+ ...token.resource && URL.canParse(token.resource) && { resource: new URL(token.resource) },
36439
+ extra: { verified: token }
36440
+ };
36441
+ } };
36442
+ try {
36443
+ const normalized = typeof header === "string" ? header.replace(SPACES, " ") : void 0;
36444
+ return (await verifyBearerToken(normalized, {
36445
+ verifier,
36446
+ expectedResource: new URL(as.resource)
36447
+ })).extra?.["verified"];
36448
+ } catch {
36449
+ return;
36450
+ }
36422
36451
  };
36423
36452
  const PROTECTED_RESOURCE_PATHS = ["/.well-known/oauth-protected-resource/mcp", "/.well-known/oauth-protected-resource"];
36424
36453
  const sendJson = (res, status, body) => {
@@ -36572,8 +36601,7 @@ const startHttpTransport = async (config, logger = silentLogger, options = {}) =
36572
36601
  await transport.handleRequest(req, res, body);
36573
36602
  };
36574
36603
  const handleMcpRequest = async (req, res, as) => {
36575
- const bearer = extractBearer(req);
36576
- const token = bearer ? await as.verifyAccessToken(bearer) : void 0;
36604
+ const token = await authenticate(req, as);
36577
36605
  if (!token) {
36578
36606
  sendUnauthorized(res, as.issuer);
36579
36607
  return;
package/dist/index.js CHANGED
@@ -7546,6 +7546,92 @@ const isStartupError = (err) => err instanceof Error && err.name === "StartupErr
7546
7546
  /** The errno code of a failed filesystem call, for the message. */
7547
7547
  const errnoCode = (err) => err?.code ?? "unknown error";
7548
7548
  //#endregion
7549
+ //#region src/utils/validation.ts
7550
+ const formatPath = (path) => path.map(String).join(".");
7551
+ const formatIssue$1 = (issue) => issue.path.length > 0 ? `${formatPath(issue.path)}: ${issue.message}` : issue.message;
7552
+ /**
7553
+ * `path: message` per issue, joined by `; ` since zod messages carry commas.
7554
+ * Ours rather than the SDK's (not exported): one wording for every mode and for
7555
+ * startup config errors, stable across SDK bumps.
7556
+ */
7557
+ const formatIssues = (issues) => issues.map(formatIssue$1).join("; ");
7558
+ const unwrap$1 = (node) => node._zod.def.innerType ? unwrap$1(node._zod.def.innerType) : node;
7559
+ /**
7560
+ * Keys declared by the object at `path`, or undefined when no plain object sits
7561
+ * there (a record, say, whose keys zod checks against an enum).
7562
+ */
7563
+ const keysAt = (node, path) => {
7564
+ const { def } = unwrap$1(node)._zod;
7565
+ if (path.length === 0) return def.shape && Object.keys(def.shape);
7566
+ const [head, ...rest] = path;
7567
+ const child = typeof head === "number" ? def.element : def.shape?.[String(head)];
7568
+ return child && keysAt(child, rest);
7569
+ };
7570
+ const keyList = (keys) => [...keys].sort().join(", ") || "(none)";
7571
+ /** One sentence per object holding unknown keys, the top level first. */
7572
+ const unknownKeysMessage = (schema, issues, topLevelKeys) => [...issues].sort((a, b) => a.path.length - b.path.length).map(({ path, keys }) => {
7573
+ if (path.length === 0) return `Unknown parameter(s): ${keys.join(", ")}. Valid parameters: ${topLevelKeys}.`;
7574
+ const parent = formatPath(path);
7575
+ const unknown = `Unknown parameter(s): ${keys.map((key) => `${parent}.${key}`).join(", ")}.`;
7576
+ const valid = keysAt(schema, path);
7577
+ return valid ? `${unknown} Valid parameters for ${parent}: ${keyList(valid)}.` : unknown;
7578
+ }).join(" ");
7579
+ /**
7580
+ * Build a strict validator for a tool's input schema, computing the strict
7581
+ * schema and the valid-key list once at construction rather than per call.
7582
+ *
7583
+ * Zod objects default to `strip`, which silently drops unknown keys. That hid
7584
+ * #100: a caller passing `per_page` to list_tickets (whose parameter is
7585
+ * `page_size`) had the key dropped, so a large unpaginated page came back. The
7586
+ * validator rejects unknown keys and names both the offending ones and the
7587
+ * valid parameters, so a misremembered name fails loudly. Any other failure is
7588
+ * one `path: message` per issue (#206). When both occur only the unknown keys
7589
+ * are reported: a guessed name is the one error a caller cannot spot by
7590
+ * re-reading its own arguments.
7591
+ */
7592
+ const createStrictValidator = (schema, strict = schema.strict()) => {
7593
+ const topLevelKeys = keyList(Object.keys(schema.shape));
7594
+ return (params) => {
7595
+ const result = strict.safeParse(params);
7596
+ if (result.success) return {
7597
+ success: true,
7598
+ data: result.data
7599
+ };
7600
+ const unknownKeyIssues = result.error.issues.filter((issue) => issue.code === "unrecognized_keys");
7601
+ return {
7602
+ success: false,
7603
+ message: unknownKeyIssues.length > 0 ? unknownKeysMessage(schema, unknownKeyIssues, topLevelKeys) : `Invalid parameter(s): ${formatIssues(result.error.issues)}`
7604
+ };
7605
+ };
7606
+ };
7607
+ /** The proxy dispatch path (namespace/single modes): throws the validator's message. */
7608
+ const createStrictParamsParser = (schema) => {
7609
+ const validate = createStrictValidator(schema);
7610
+ return (params) => {
7611
+ const result = validate(params);
7612
+ if (!result.success) throw new Error(result.message);
7613
+ return result.data;
7614
+ };
7615
+ };
7616
+ /**
7617
+ * The same validator as a Standard Schema, for `all` mode: the SDK calls this
7618
+ * `validate` and prints our pathless issue after its own prefix, so all three
7619
+ * modes word an error alike. The JSON Schema is zod's own, untouched.
7620
+ */
7621
+ const toStrictStandardSchema = (schema) => {
7622
+ const strict = schema.strict();
7623
+ const validate = createStrictValidator(schema, strict);
7624
+ return { "~standard": {
7625
+ version: 1,
7626
+ vendor: "zendesk-mcp-server",
7627
+ validate: (value) => {
7628
+ const result = validate(value);
7629
+ return result.success ? { value: result.data } : { issues: [{ message: result.message }] };
7630
+ },
7631
+ jsonSchema: strict["~standard"].jsonSchema
7632
+ } };
7633
+ };
7634
+ //#endregion
7549
7635
  //#region src/config.ts
7550
7636
  const ToolMode = _enum([
7551
7637
  "single",
@@ -7790,7 +7876,7 @@ const parseStrict = (args) => {
7790
7876
  const validated = (input) => {
7791
7877
  const result = ConfigSchema.safeParse(input);
7792
7878
  if (result.success) return result.data;
7793
- throw createStartupError(`${result.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")}.`, STARTUP_DOCS.cliReference, result.error);
7879
+ throw createStartupError(`${formatIssues(result.error.issues)}.`, STARTUP_DOCS.cliReference, result.error);
7794
7880
  };
7795
7881
  const parseCliArgs = (args) => {
7796
7882
  const { values, positionals } = parseStrict(args);
@@ -8240,7 +8326,7 @@ const createBrandSubdomainResolver = (subdomain, getToken) => {
8240
8326
  };
8241
8327
  };
8242
8328
  //#endregion
8243
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.2.0/node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
8329
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/chunk-Br0eD_fh.mjs
8244
8330
  var __create = Object.create;
8245
8331
  var __defProp = Object.defineProperty;
8246
8332
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -8272,7 +8358,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
8272
8358
  enumerable: true
8273
8359
  }) : target, mod));
8274
8360
  //#endregion
8275
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.2.0/node_modules/@modelcontextprotocol/server/dist/dialects-DoSzNhcb.mjs
8361
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/dialects-DoSzNhcb.mjs
8276
8362
  /**
8277
8363
  * Canonical `$schema` URIs per supported dialect (http + https variants, trailing-`#` stripped).
8278
8364
  */
@@ -9935,7 +10021,7 @@ const OAuthTokenRevocationRequestSchema = object({
9935
10021
  token_type_hint: string$2().optional()
9936
10022
  }).strip();
9937
10023
  //#endregion
9938
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.2.0/node_modules/@modelcontextprotocol/server/dist/src-BHSMhZ_W.mjs
10024
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/src-Cqbh3MYc.mjs
9939
10025
  /**
9940
10026
  * Cross-bundle `instanceof` support for the SDK error classes.
9941
10027
  *
@@ -12990,7 +13076,7 @@ function build() {
12990
13076
  const subscriptionsListenParamsShape = { notifications: SubscriptionFilterSchema$1 };
12991
13077
  const SubscriptionsListenRequestSchema$1 = wireRequest("subscriptions/listen", subscriptionsListenParamsShape);
12992
13078
  /**
12993
- * Anchor SubscriptionsListenResultMeta — required subscriptionId stamp on
13079
+ * Anchor SubscriptionsListenResultMetaObject — required subscriptionId stamp on
12994
13080
  * the graceful-close result. Extends `ResultMetaObject` since spec PR
12995
13081
  * #3002 (composed, so the serverInfo key and its leniency stay single-sourced).
12996
13082
  */
@@ -13263,7 +13349,8 @@ function build() {
13263
13349
  ListResourceTemplatesResultResponseSchema: wireResultResponse(ListResourceTemplatesResultSchema$1),
13264
13350
  ReadResourceResultResponseSchema: wireResultResponse(union([ReadResourceResultSchema$1, InputRequiredResultSchema])),
13265
13351
  CompleteResultResponseSchema: wireResultResponse(CompleteResultSchema$1),
13266
- DiscoverResultResponseSchema: wireResultResponse(DiscoverResultSchema$1)
13352
+ DiscoverResultResponseSchema: wireResultResponse(DiscoverResultSchema$1),
13353
+ SubscriptionsListenResultResponseSchema: wireResultResponse(SubscriptionsListenResultSchema$1)
13267
13354
  };
13268
13355
  }
13269
13356
  let memo;
@@ -14007,6 +14094,10 @@ function isSpecRequestMethod(method) {
14007
14094
  function isSpecNotificationMethod(method) {
14008
14095
  return ALL_CODECS.some((codec) => codec.hasNotificationMethod(method));
14009
14096
  }
14097
+ /** The names SEP-2663 re-defines for the Tasks extension on the era that removed them from core. */
14098
+ function isExtensionReusedRequestMethod(method) {
14099
+ return method === "tasks/get" || method === "tasks/cancel";
14100
+ }
14010
14101
  const ALL_CODECS = [rev2025Codec, rev2026Codec];
14011
14102
  var schemas_exports = /* @__PURE__ */ __exportAll({
14012
14103
  AnnotationsSchema: () => AnnotationsSchema,
@@ -15039,6 +15130,8 @@ var Protocol = class {
15039
15130
  _transport;
15040
15131
  _requestMessageId = 0;
15041
15132
  _requestHandlers = /* @__PURE__ */ new Map();
15133
+ /** Methods registered with an explicit schema; the era gate in `_onrequest` reads it for the Tasks extension names. */
15134
+ _customSchemaRequestMethods = /* @__PURE__ */ new Set();
15042
15135
  _requestHandlerAbortControllers = /* @__PURE__ */ new Map();
15043
15136
  _notificationHandlers = /* @__PURE__ */ new Map();
15044
15137
  _responseHandlers = /* @__PURE__ */ new Map();
@@ -15316,7 +15409,7 @@ var Protocol = class {
15316
15409
  return;
15317
15410
  }
15318
15411
  }
15319
- if (isSpecRequestMethod(request.method) && !codec.hasRequestMethod(request.method)) {
15412
+ if (isSpecRequestMethod(request.method) && !codec.hasRequestMethod(request.method) && !(isExtensionReusedRequestMethod(request.method) && this._customSchemaRequestMethods.has(request.method))) {
15320
15413
  sendErrorResponse(ProtocolErrorCode.MethodNotFound, "Method not found");
15321
15414
  return;
15322
15415
  }
@@ -15449,8 +15542,11 @@ var Protocol = class {
15449
15542
  }
15450
15543
  request(request, schemaOrOptions, maybeOptions) {
15451
15544
  const codec = this._resolveOutboundCodec(request.method);
15545
+ if (isStandardSchema(schemaOrOptions)) {
15546
+ if (!isExtensionReusedRequestMethod(request.method)) this._assertOutboundRequestInEra(codec, request.method);
15547
+ return this._requestWithSchemaViaCodec(codec, request, schemaOrOptions, maybeOptions);
15548
+ }
15452
15549
  this._assertOutboundRequestInEra(codec, request.method);
15453
- if (isStandardSchema(schemaOrOptions)) return this._requestWithSchemaViaCodec(codec, request, schemaOrOptions, maybeOptions);
15454
15550
  const validate = codecResultValidator(codec, request.method);
15455
15551
  if (validate === void 0) throw new TypeError(`'${request.method}' is not a spec method; pass a result schema as the second argument to request().`);
15456
15552
  return this._requestWithSchemaViaCodec(codec, request, validate, schemaOrOptions);
@@ -15694,6 +15790,8 @@ var Protocol = class {
15694
15790
  return maybeHandler(parsed.data, ctx);
15695
15791
  };
15696
15792
  else throw new TypeError("setRequestHandler: handler is required");
15793
+ if (typeof schemasOrHandler === "function") this._customSchemaRequestMethods.delete(method);
15794
+ else this._customSchemaRequestMethods.add(method);
15697
15795
  this._requestHandlers.set(method, this._wrapHandler(method, stored));
15698
15796
  }
15699
15797
  /**
@@ -15722,6 +15820,7 @@ var Protocol = class {
15722
15820
  */
15723
15821
  removeRequestHandler(method) {
15724
15822
  this._requestHandlers.delete(method);
15823
+ this._customSchemaRequestMethods.delete(method);
15725
15824
  }
15726
15825
  /**
15727
15826
  * Asserts that a request handler has not already been set for the given method, in preparation for a new one being automatically installed.
@@ -16303,7 +16402,7 @@ function normalizeRawShapeSchema(schema) {
16303
16402
  return schema;
16304
16403
  }
16305
16404
  //#endregion
16306
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.2.0/node_modules/@modelcontextprotocol/server/dist/ajvProvider-CEoC__sr.mjs
16405
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/ajvProvider-CEoC__sr.mjs
16307
16406
  var require_code$1 = /* @__PURE__ */ __commonJSMin(((exports) => {
16308
16407
  Object.defineProperty(exports, "__esModule", { value: true });
16309
16408
  exports.regexpCode = exports.getEsmExportName = exports.getProperty = exports.safeStringify = exports.stringify = exports.strConcat = exports.addCodeArg = exports.str = exports._ = exports.nil = exports._Code = exports.Name = exports.IDENTIFIER = exports._CodeOrName = void 0;
@@ -23438,7 +23537,7 @@ var AjvJsonSchemaValidator = class {
23438
23537
  };
23439
23538
  import_ajv.Ajv;
23440
23539
  //#endregion
23441
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.2.0/node_modules/@modelcontextprotocol/server/dist/mcp-DYuW2ZSs.mjs
23540
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/mcp-DIH4cS6P.mjs
23442
23541
  const COMPLETABLE_SYMBOL = Symbol.for("mcp.completable");
23443
23542
  /**
23444
23543
  * Checks if a schema is completable (has completion metadata).
@@ -23460,6 +23559,15 @@ function armSseKeepAlive(intervalMs, onTick) {
23460
23559
  timer.unref?.();
23461
23560
  return timer;
23462
23561
  }
23562
+ function comparableResource(value) {
23563
+ const text = String(value);
23564
+ const hash = text.indexOf("#");
23565
+ return (hash === -1 ? text : text.slice(0, hash)).replace(/\/$/, "");
23566
+ }
23567
+ function sameResource(reported, expected) {
23568
+ if (!reported) return false;
23569
+ return comparableResource(reported) === comparableResource(expected);
23570
+ }
23463
23571
  function headerQuotedValue(value) {
23464
23572
  return value.replaceAll(/[\\"]/g, String.raw`\$&`).replaceAll(/[^\u0020-\u007E]/g, " ");
23465
23573
  }
@@ -23479,6 +23587,38 @@ function buildWwwAuthenticateHeader(errorCode, description, requiredScopes, reso
23479
23587
  return header;
23480
23588
  }
23481
23589
  /**
23590
+ * Validate a raw `Authorization` header value as a Bearer token and return
23591
+ * the verified {@link AuthInfo}.
23592
+ *
23593
+ * The runtime-neutral core of Bearer authentication: it parses the header,
23594
+ * runs the verifier, compares the token's resource with `expectedResource`,
23595
+ * enforces `requiredScopes`, and rejects tokens without an
23596
+ * expiration or past it. On any failure it throws an {@link OAuthError} —
23597
+ * pass that to {@link bearerAuthChallengeResponse} for the matching HTTP
23598
+ * answer, or use {@link requireBearerAuth} to get both steps as one call.
23599
+ *
23600
+ * Framework adapters build on this: `requireBearerAuth` from
23601
+ * `@modelcontextprotocol/express` feeds it `req.headers.authorization`.
23602
+ */
23603
+ async function verifyBearerToken(authorizationHeader, options) {
23604
+ const { verifier, requiredScopes = [], expectedResource } = options;
23605
+ if (!authorizationHeader) throw new OAuthError(OAuthErrorCode.InvalidToken, "Missing Authorization header");
23606
+ const [type, token] = authorizationHeader.split(" ");
23607
+ if (type?.toLowerCase() !== "bearer" || !token) throw new OAuthError(OAuthErrorCode.InvalidToken, "Invalid Authorization header format, expected 'Bearer TOKEN'");
23608
+ const authInfo = await verifier.verifyAccessToken(token);
23609
+ if (expectedResource !== void 0 && !sameResource(authInfo.resource, expectedResource)) throw new OAuthError(OAuthErrorCode.InvalidToken, "Token was not issued for this resource");
23610
+ if (requiredScopes.length > 0) {
23611
+ if (!requiredScopes.every((scope) => authInfo.scopes.includes(scope))) throw new OAuthError(OAuthErrorCode.InsufficientScope, "Insufficient scope");
23612
+ }
23613
+ if (typeof authInfo.expiresAt !== "number" || Number.isNaN(authInfo.expiresAt)) throw new OAuthError(OAuthErrorCode.InvalidToken, "Token has no expiration time");
23614
+ else if (authInfo.expiresAt < Date.now() / 1e3) throw new OAuthError(OAuthErrorCode.InvalidToken, "Token has expired");
23615
+ if (options.resourceMetadataUrl !== void 0 && authInfo.resourceMetadataUrl === void 0) return {
23616
+ ...authInfo,
23617
+ resourceMetadataUrl: options.resourceMetadataUrl
23618
+ };
23619
+ return authInfo;
23620
+ }
23621
+ /**
23482
23622
  * Build the HTTP answer for a Bearer authentication failure.
23483
23623
  *
23484
23624
  * Maps an {@link OAuthError} to its status — `401` for `invalid_token` and
@@ -23791,6 +23931,11 @@ var Server = class extends Protocol {
23791
23931
  if (modernProtocolVersions(this._supportedProtocolVersions).length > 0) this.setRequestHandler("server/discover", () => this._ondiscover());
23792
23932
  if (this._capabilities.logging) this._registerLoggingHandler();
23793
23933
  }
23934
+ /** Attaches to the given transport; rejects while this instance is connected to another one. */
23935
+ async connect(transport) {
23936
+ if (this.transport !== void 0) throw new SdkError(SdkErrorCode.AlreadyConnected, "Use a separate Server instance per connection: this instance is already connected to a transport. To connect it to a new transport, call close() first.");
23937
+ await super.connect(transport);
23938
+ }
23794
23939
  /**
23795
23940
  * Registers the built-in `logging/setLevel` request handler.
23796
23941
  *
@@ -24336,6 +24481,30 @@ var Server = class extends Protocol {
24336
24481
  function discoverAdvertisedCapabilities(capabilities) {
24337
24482
  return { ...capabilities };
24338
24483
  }
24484
+ function toolInputElementCount(value, max) {
24485
+ let count = 0;
24486
+ const stack = [value];
24487
+ while (stack.length > 0) {
24488
+ const node = stack.pop();
24489
+ if (node === null || typeof node !== "object") continue;
24490
+ if (Array.isArray(node)) for (const child of node) {
24491
+ if (++count > max) return count;
24492
+ if (child !== null && typeof child === "object") stack.push(child);
24493
+ }
24494
+ else for (const key in node) {
24495
+ if (!Object.prototype.hasOwnProperty.call(node, key)) continue;
24496
+ if (++count > max) return count;
24497
+ const child = node[key];
24498
+ if (child !== null && typeof child === "object") stack.push(child);
24499
+ }
24500
+ }
24501
+ return count;
24502
+ }
24503
+ function resolveMaxToolInputElements(value) {
24504
+ if (value === void 0 || value === Infinity) return void 0;
24505
+ if (typeof value !== "number" || Number.isNaN(value) || value < 1) throw new RangeError(`maxToolInputElements must be a number of at least 1, or Infinity, got ${String(value)}`);
24506
+ return value;
24507
+ }
24339
24508
  /**
24340
24509
  * High-level MCP server that provides a simpler API for working with resources, tools, and prompts.
24341
24510
  * For advanced usage (like sending notifications or setting custom request handlers), use the underlying
@@ -24354,16 +24523,12 @@ var McpServer = class {
24354
24523
  * The underlying {@linkcode Server} instance, useful for advanced operations like sending notifications.
24355
24524
  */
24356
24525
  server;
24526
+ _maxToolInputElements;
24357
24527
  _registeredResources = {};
24358
24528
  _registeredResourceTemplates = {};
24359
24529
  _registeredTools = {};
24360
24530
  _registeredPrompts = {};
24361
- /**
24362
- * Per-tool JSON-converted `inputSchema`, memoized so the SEP-2243
24363
- * registration-time scan and the pre-dispatch validation step share one
24364
- * conversion instead of paying it twice per request under the
24365
- * per-request-factory `createMcpHandler` model.
24366
- */
24531
+ /** Per-tool JSON-converted `inputSchema`, filled on first use by `toolInputSchemaJson()`. */
24367
24532
  _toolInputSchemaJson = {};
24368
24533
  /**
24369
24534
  * The JSON-serialized `inputSchema` of a registered tool, or `undefined`
@@ -24388,6 +24553,7 @@ var McpServer = class {
24388
24553
  }
24389
24554
  constructor(serverInfo, options) {
24390
24555
  this.server = new Server(serverInfo, options);
24556
+ this._maxToolInputElements = resolveMaxToolInputElements(options?.maxToolInputElements);
24391
24557
  if (options?.capabilities?.tools) this.setToolRequestHandlers();
24392
24558
  if (options?.capabilities?.resources) this.setResourceRequestHandlers();
24393
24559
  if (options?.capabilities?.prompts) this.setPromptRequestHandlers();
@@ -24459,7 +24625,7 @@ var McpServer = class {
24459
24625
  name,
24460
24626
  title: tool.title,
24461
24627
  description: tool.description,
24462
- inputSchema: tool.inputSchema ? standardSchemaToJsonSchema(tool.inputSchema, "input") : EMPTY_OBJECT_JSON_SCHEMA,
24628
+ inputSchema: tool.inputSchema ? convertListedInputSchema(name, tool.inputSchema) : EMPTY_OBJECT_JSON_SCHEMA,
24463
24629
  annotations: tool.annotations,
24464
24630
  icons: tool.icons,
24465
24631
  execution: tool.execution,
@@ -24504,6 +24670,7 @@ var McpServer = class {
24504
24670
  * Validates tool input arguments against the tool's input schema.
24505
24671
  */
24506
24672
  async validateToolInput(tool, args, toolName) {
24673
+ if (this._maxToolInputElements !== void 0 && toolInputElementCount(args, this._maxToolInputElements) > this._maxToolInputElements) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for tool ${toolName}: arguments contain more than the maximum of ${this._maxToolInputElements} elements`);
24507
24674
  if (!tool.inputSchema) return;
24508
24675
  const parseResult = await validateStandardSchema(tool.inputSchema, args ?? {});
24509
24676
  if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Input validation error: Invalid arguments for tool ${toolName}: ${parseResult.error}`);
@@ -24777,19 +24944,19 @@ var McpServer = class {
24777
24944
  }
24778
24945
  _createRegisteredTool(name, title, description, inputSchema, outputSchema, annotations, icons, execution, scopeChallenge, _meta, handler) {
24779
24946
  validateAndWarnToolName(name);
24780
- if (inputSchema !== void 0) try {
24781
- const json = standardSchemaToJsonSchema(inputSchema, "input");
24782
- this._toolInputSchemaJson[name] = json;
24783
- const scan = scanXMcpHeaderDeclarations(json);
24784
- if (!scan.valid) console.warn(`[mcp-sdk] tool '${name}' carries an invalid x-mcp-header declaration and will be excluded by conforming Streamable HTTP clients: ${scan.reason}`);
24785
- } catch {}
24786
24947
  let currentHandler = handler;
24948
+ let outputSchemaJson;
24787
24949
  const registeredTool = {
24788
24950
  title,
24789
24951
  description,
24790
24952
  inputSchema,
24791
24953
  outputSchema,
24792
- outputSchemaJson: convertOutputSchemaJson(outputSchema),
24954
+ get outputSchemaJson() {
24955
+ return outputSchemaJson ??= convertOutputSchemaJson(registeredTool.outputSchema);
24956
+ },
24957
+ set outputSchemaJson(value) {
24958
+ outputSchemaJson = value;
24959
+ },
24793
24960
  annotations,
24794
24961
  icons,
24795
24962
  execution,
@@ -24950,6 +25117,13 @@ const EMPTY_OBJECT_JSON_SCHEMA = {
24950
25117
  type: "object",
24951
25118
  properties: {}
24952
25119
  };
25120
+ /** Converts a tool's `inputSchema` for `tools/list` and warns on an invalid SEP-2243 `x-mcp-header` declaration. */
25121
+ function convertListedInputSchema(name, inputSchema) {
25122
+ const json = standardSchemaToJsonSchema(inputSchema, "input");
25123
+ const scan = scanXMcpHeaderDeclarations(json);
25124
+ if (!scan.valid) console.warn(`[mcp-sdk] tool '${name}' carries an invalid x-mcp-header declaration and will be excluded by conforming Streamable HTTP clients: ${scan.reason}`);
25125
+ return json;
25126
+ }
24953
25127
  /**
24954
25128
  * Convert a registered `outputSchema` to JSON Schema, memoised on {@link RegisteredTool.outputSchemaJson}
24955
25129
  * so `tools/call` passes the SAME advertised schema to the wire codec's `projectCallToolResult` that
@@ -24972,7 +25146,7 @@ function createPromptHandler(name, argsSchema, callback) {
24972
25146
  if (argsSchema) {
24973
25147
  const typedCallback = callback;
24974
25148
  return async (args, ctx) => {
24975
- const parseResult = await validateStandardSchema(argsSchema, args);
25149
+ const parseResult = await validateStandardSchema(argsSchema, args ?? {});
24976
25150
  if (!parseResult.success) throw new ProtocolError(ProtocolErrorCode.InvalidParams, `Invalid arguments for prompt ${name}: ${parseResult.error}`);
24977
25151
  return typedCallback(parseResult.data, ctx);
24978
25152
  };
@@ -25009,7 +25183,7 @@ function unwrapOptionalSchema(schema) {
25009
25183
  return schema.def?.innerType ?? schema;
25010
25184
  }
25011
25185
  //#endregion
25012
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.2.0/node_modules/@modelcontextprotocol/server/dist/index.mjs
25186
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/index.mjs
25013
25187
  /** Default upper bound, in bytes, on a request body read by the HTTP entry points (4 MiB). */
25014
25188
  const DEFAULT_MAX_REQUEST_BODY_SIZE = 4194304;
25015
25189
  /** Upper bound on the number of messages accepted in one JSON-RPC batch array. */
@@ -25098,6 +25272,10 @@ async function readRequestBody(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_SIZE
25098
25272
  * @example Hono.js
25099
25273
  * ```ts source="./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_hono"
25100
25274
  * app.all('/mcp', async c => {
25275
+ * // Stateless example: create a server and a transport per request.
25276
+ * const server = new McpServer({ name: 'my-server', version: '1.0.0' });
25277
+ * const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
25278
+ * await server.connect(transport);
25101
25279
  * return transport.handleRequest(c.req.raw);
25102
25280
  * });
25103
25281
  * ```
@@ -25106,6 +25284,10 @@ async function readRequestBody(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_SIZE
25106
25284
  * ```ts source="./streamableHttp.examples.ts#WebStandardStreamableHTTPServerTransport_workers"
25107
25285
  * const worker = {
25108
25286
  * async fetch(request: Request): Promise<Response> {
25287
+ * // Stateless example: create a server and a transport per request.
25288
+ * const server = new McpServer({ name: 'my-server', version: '1.0.0' });
25289
+ * const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
25290
+ * await server.connect(transport);
25109
25291
  * return transport.handleRequest(request);
25110
25292
  * }
25111
25293
  * };
@@ -25114,6 +25296,7 @@ async function readRequestBody(request, maxBytes = DEFAULT_MAX_REQUEST_BODY_SIZE
25114
25296
  var WebStandardStreamableHTTPServerTransport = class {
25115
25297
  sessionIdGenerator;
25116
25298
  _started = false;
25299
+ _hasHandledRequest = false;
25117
25300
  _closed = false;
25118
25301
  _streamMapping = /* @__PURE__ */ new Map();
25119
25302
  _requestToStreamMapping = /* @__PURE__ */ new Map();
@@ -25235,6 +25418,8 @@ var WebStandardStreamableHTTPServerTransport = class {
25235
25418
  */
25236
25419
  async handleRequest(req, options) {
25237
25420
  if (this._closed) return this.createJsonErrorResponse(404, -32001, "Session not found");
25421
+ if (!this.sessionIdGenerator && this._hasHandledRequest) throw new Error("Stateless transport cannot be reused across requests. Create a new transport per request.");
25422
+ this._hasHandledRequest = true;
25238
25423
  const validationError = this.validateRequestHeaders(req);
25239
25424
  if (validationError) return validationError;
25240
25425
  switch (req.method) {
@@ -83080,7 +83265,7 @@ function escapeStringRegexp(string) {
83080
83265
  return string.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&").replace(/-/g, "\\x2d");
83081
83266
  }
83082
83267
  //#endregion
83083
- //#region node_modules/.pnpm/mdast-util-find-and-replace@3.0.2/node_modules/mdast-util-find-and-replace/lib/index.js
83268
+ //#region node_modules/.pnpm/mdast-util-find-and-replace@3.0.3/node_modules/mdast-util-find-and-replace/lib/index.js
83084
83269
  /**
83085
83270
  * @import {Nodes, Parents, PhrasingContent, Root, Text} from 'mdast'
83086
83271
  * @import {BuildVisitor, Test, VisitorResult} from 'unist-util-visit-parents'
@@ -83157,7 +83342,11 @@ function findAndReplace(tree, list, options) {
83157
83342
  const pairs = toPairs(list);
83158
83343
  let pairIndex = -1;
83159
83344
  while (++pairIndex < pairs.length) visitParents(tree, "text", visitor);
83160
- /** @type {BuildVisitor<Root, 'text'>} */
83345
+ /**
83346
+ * Visit a text node, and handle it if it is not in an ignored parent.
83347
+ *
83348
+ * @type {BuildVisitor<Root, 'text'>}
83349
+ */
83161
83350
  function visitor(node, parents) {
83162
83351
  let index = -1;
83163
83352
  /** @type {Parents | undefined} */
@@ -83175,11 +83364,11 @@ function findAndReplace(tree, list, options) {
83175
83364
  * Handle a text node which is not in an ignored parent.
83176
83365
  *
83177
83366
  * @param {Text} node
83178
- * Text node.
83367
+ * Text node to search in.
83179
83368
  * @param {Array<Parents>} parents
83180
- * Parents.
83369
+ * Ancestors of `node`.
83181
83370
  * @returns {VisitorResult}
83182
- * Result.
83371
+ * Where to continue visiting.
83183
83372
  */
83184
83373
  function handler(node, parents) {
83185
83374
  const parent = parents[parents.length - 1];
@@ -83207,12 +83396,12 @@ function findAndReplace(tree, list, options) {
83207
83396
  } : void 0;
83208
83397
  if (value === false) find.lastIndex = position + 1;
83209
83398
  else {
83210
- if (start !== position) nodes.push({
83399
+ if (start !== position) add({
83211
83400
  type: "text",
83212
83401
  value: node.value.slice(start, position)
83213
83402
  });
83214
- if (Array.isArray(value)) nodes.push(...value);
83215
- else if (value) nodes.push(value);
83403
+ if (Array.isArray(value)) for (const child of value) add(child);
83404
+ else if (value) add(value);
83216
83405
  start = position + match[0].length;
83217
83406
  change = true;
83218
83407
  }
@@ -83220,13 +83409,26 @@ function findAndReplace(tree, list, options) {
83220
83409
  match = find.exec(node.value);
83221
83410
  }
83222
83411
  if (change) {
83223
- if (start < node.value.length) nodes.push({
83412
+ if (start < node.value.length) add({
83224
83413
  type: "text",
83225
83414
  value: node.value.slice(start)
83226
83415
  });
83227
83416
  parent.children.splice(index, 1, ...nodes);
83228
83417
  } else nodes = [node];
83229
83418
  return index + nodes.length;
83419
+ /**
83420
+ * Add a node.
83421
+ *
83422
+ * @param {PhrasingContent} child
83423
+ * Node to add.
83424
+ * @returns {undefined}
83425
+ * Nothing.
83426
+ */
83427
+ function add(child) {
83428
+ const previous = nodes[nodes.length - 1];
83429
+ if (previous && previous.type === "text" && child.type === "text") previous.value += child.value;
83430
+ else nodes.push(child);
83431
+ }
83230
83432
  }
83231
83433
  }
83232
83434
  /**
@@ -83238,9 +83440,9 @@ function findAndReplace(tree, list, options) {
83238
83440
  * Clean pairs.
83239
83441
  */
83240
83442
  function toPairs(tupleOrList) {
83443
+ if (!Array.isArray(tupleOrList)) throw new TypeError("Expected find and replace tuple or list of tuples");
83241
83444
  /** @type {Pairs} */
83242
83445
  const result = [];
83243
- if (!Array.isArray(tupleOrList)) throw new TypeError("Expected find and replace tuple or list of tuples");
83244
83446
  /** @type {FindAndReplaceList} */
83245
83447
  const list = !tupleOrList[0] || Array.isArray(tupleOrList[0]) ? tupleOrList : [tupleOrList];
83246
83448
  let index = -1;
@@ -83254,9 +83456,9 @@ function toPairs(tupleOrList) {
83254
83456
  * Turn a find into an expression.
83255
83457
  *
83256
83458
  * @param {Find} find
83257
- * Find.
83459
+ * Pattern to find.
83258
83460
  * @returns {RegExp}
83259
- * Expression.
83461
+ * Global expression.
83260
83462
  */
83261
83463
  function toExpression(find) {
83262
83464
  return typeof find === "string" ? new RegExp(escapeStringRegexp(find), "g") : find;
@@ -83265,9 +83467,9 @@ function toExpression(find) {
83265
83467
  * Turn a replace into a function.
83266
83468
  *
83267
83469
  * @param {Replace} replace
83268
- * Replace.
83470
+ * Thing to replace with.
83269
83471
  * @returns {ReplaceFunction}
83270
- * Function.
83472
+ * Function that returns that thing.
83271
83473
  */
83272
83474
  function toFunction(replace) {
83273
83475
  return typeof replace === "function" ? replace : function() {
@@ -105892,7 +106094,7 @@ const attachmentSchema = object({
105892
106094
  error: (issue) => `Attachment too large: ${issue.input.length} base64 characters, limit ${MAX_BASE64_INPUT_CHARS}. Downscale the file, split the upload, or link to it instead of uploading.`
105893
106095
  }).base64().describe(`File content encoded as base64. At most ${MAX_BASE64_INPUT_CHARS} characters (about ${MAX_BASE64_INPUT_MB} MB of file), and the attachments of one call must stay under that total; the HTTP transport additionally caps request bodies at 4 MB.`),
105894
106096
  content_type: string$2().min(1).default("application/octet-stream").describe("MIME type, e.g. \"text/plain\", \"image/png\", \"application/pdf\".")
105895
- });
106097
+ }).strict();
105896
106098
  /**
105897
106099
  * The optional `attachments` array parameter, capped as a whole.
105898
106100
  *
@@ -106222,7 +106424,7 @@ const createRequestTools = (ctx) => {
106222
106424
  custom_fields: array(object({
106223
106425
  id: number$2().int(),
106224
106426
  value: unknown$3()
106225
- })).optional().describe("Answers to the form's own questions, as { id, value } pairs. Take both the ids and the accepted values from get_request_form; an id this form does not carry, or a value a dropdown does not offer, is refused here because Zendesk would drop it without an error. Option values are the tag strings, never numbers."),
106427
+ }).strict()).optional().describe("Answers to the form's own questions, as { id, value } pairs. Take both the ids and the accepted values from get_request_form; an id this form does not carry, or a value a dropdown does not offer, is refused here because Zendesk would drop it without an error. Option values are the tag strings, never numbers."),
106226
106428
  attachments: attachmentsParam("Files to attach to the request, such as a screenshot or a log, with their content base64-encoded.")
106227
106429
  }),
106228
106430
  annotations: {
@@ -107012,7 +107214,7 @@ const createTicketTools = (ctx) => {
107012
107214
  custom_fields: array(object({
107013
107215
  id: number$2().int(),
107014
107216
  value: unknown$3()
107015
- })).optional().describe("Custom field values as { id, value } pairs (field ids come from your Zendesk admin settings). Call list_ticket_fields first to discover the numeric field ids and, for dropdown/multiselect fields, the exact option values Zendesk accepts.")
107217
+ }).strict()).optional().describe("Custom field values as { id, value } pairs (field ids come from your Zendesk admin settings). Call list_ticket_fields first to discover the numeric field ids and, for dropdown/multiselect fields, the exact option values Zendesk accepts.")
107016
107218
  }),
107017
107219
  annotations: {
107018
107220
  readOnlyHint: false,
@@ -107069,7 +107271,7 @@ const createTicketTools = (ctx) => {
107069
107271
  custom_fields: array(object({
107070
107272
  id: number$2().int(),
107071
107273
  value: unknown$3()
107072
- })).optional().describe("Custom field values as { id, value } pairs (field ids come from your Zendesk admin settings). Call list_ticket_fields first to discover the numeric field ids and, for dropdown/multiselect fields, the exact option values Zendesk accepts."),
107274
+ }).strict()).optional().describe("Custom field values as { id, value } pairs (field ids come from your Zendesk admin settings). Call list_ticket_fields first to discover the numeric field ids and, for dropdown/multiselect fields, the exact option values Zendesk accepts."),
107073
107275
  followers: object({
107074
107276
  add: subscriberIdList("User ids to start following the ticket. Omit to only remove."),
107075
107277
  remove: subscriberIdList("User ids to stop following the ticket. Omit to only add.")
@@ -107733,32 +107935,6 @@ const readPackageInfo = () => {
107733
107935
  return cached;
107734
107936
  };
107735
107937
  //#endregion
107736
- //#region src/utils/validation.ts
107737
- /**
107738
- * Build a strict params parser for a tool's input schema, computing the strict
107739
- * schema and the valid-key list once at construction rather than per call.
107740
- *
107741
- * Zod objects default to `strip`, which silently drops unknown keys. That hid
107742
- * #100: a caller passing `per_page` to list_tickets (whose parameter is
107743
- * `page_size`) had the key dropped, so a large unpaginated page came back. The
107744
- * parser rejects unknown keys and names both the offending ones and the valid
107745
- * parameters, so a misremembered name fails loudly.
107746
- *
107747
- * Used on the proxy dispatch path (namespace/single modes); in `all` mode the
107748
- * SDK validates against the strict schema we register.
107749
- */
107750
- const createStrictParamsParser = (schema) => {
107751
- const strict = schema.strict();
107752
- const validKeys = Object.keys(schema.shape).sort().join(", ");
107753
- return (params) => {
107754
- const result = strict.safeParse(params);
107755
- if (result.success) return result.data;
107756
- const unknownKeys = result.error.issues.flatMap((issue) => issue.code === "unrecognized_keys" ? issue.keys : []);
107757
- if (unknownKeys.length > 0) throw new Error(`Unknown parameter(s): ${unknownKeys.join(", ")}. Valid parameters: ${validKeys || "(none)"}.`);
107758
- throw result.error;
107759
- };
107760
- };
107761
- //#endregion
107762
107938
  //#region src/server.ts
107763
107939
  /**
107764
107940
  * Invoke a tool handler, notifying `onUnauthorized` when Zendesk rejects the
@@ -107892,7 +108068,7 @@ const registerToolset = (server, { config, getToken, resolveBrandSubdomain, onUn
107892
108068
  for (const tool of filteredTools) registered.push(server.registerTool(tool.name, {
107893
108069
  title: tool.title,
107894
108070
  description: tool.description,
107895
- inputSchema: tool.inputSchema.strict(),
108071
+ inputSchema: toStrictStandardSchema(tool.inputSchema),
107896
108072
  annotations: tool.annotations
107897
108073
  }, async (params) => runHandler(tool, params, onUnauthorized)));
107898
108074
  break;
@@ -107996,7 +108172,7 @@ const createMcpServer = (config, getToken, logger = silentLogger, onUnauthorized
107996
108172
  return server;
107997
108173
  };
107998
108174
  //#endregion
107999
- //#region node_modules/.pnpm/@modelcontextprotocol+server@2.2.0/node_modules/@modelcontextprotocol/server/dist/stdio.mjs
108175
+ //#region node_modules/.pnpm/@modelcontextprotocol+server@2.3.1/node_modules/@modelcontextprotocol/server/dist/stdio.mjs
108000
108176
  /**
108001
108177
  * Marks a stdout `'error'` listener that a closed transport left attached purely to
108002
108178
  * swallow late write failures (see {@link StdioServerTransport.close}). A fresh
@@ -108424,7 +108600,7 @@ const installShutdown = (options) => {
108424
108600
  //#endregion
108425
108601
  //#region src/index.ts
108426
108602
  const buildStdioTokenStore = async (config, logger) => {
108427
- const { createTokenStore } = await import("./token-store-Aq7gP39o.js");
108603
+ const { createTokenStore } = await import("./token-store-BLAkLLQO.js");
108428
108604
  return createTokenStore({
108429
108605
  subdomain: config.subdomain,
108430
108606
  oauthClientId: config.oauthClientId,
@@ -108465,7 +108641,7 @@ const main = async () => {
108465
108641
  return;
108466
108642
  }
108467
108643
  if (config.dev) logger.warn("dev_mode_ignored_http");
108468
- const { startHttpTransport } = await import("./http-GIKJdf05.js");
108644
+ const { startHttpTransport } = await import("./http-BYYYoQaK.js");
108469
108645
  const http = await startHttpTransport(config, logger);
108470
108646
  installShutdown({
108471
108647
  watchStdin: false,
@@ -108479,4 +108655,4 @@ main().catch((error) => {
108479
108655
  process.exit(1);
108480
108656
  });
108481
108657
  //#endregion
108482
- export { __toESM$1 as _, zendeskGet as a, DEFAULT_CALLBACK_PORT as c, createStartupError as d, errnoCode as f, __require as g, __exportAll$1 as h, WebStandardStreamableHTTPServerTransport as i, getOAuthUrls as l, __commonJSMin$1 as m, readPackageInfo as n, REQUEST_TIMEOUT_MS as o, readEnv as p, silentLogger as r, deadlineSignal as s, createMcpServer as t, STARTUP_DOCS as u };
108658
+ export { __commonJSMin$1 as _, verifyBearerToken as a, __toESM$1 as b, zendeskGet as c, DEFAULT_CALLBACK_PORT as d, getOAuthUrls as f, readEnv as g, errnoCode as h, WebStandardStreamableHTTPServerTransport as i, REQUEST_TIMEOUT_MS as l, createStartupError as m, readPackageInfo as n, OAuthError as o, STARTUP_DOCS as p, silentLogger as r, OAuthErrorCode as s, createMcpServer as t, deadlineSignal as u, __exportAll$1 as v, __require as y };
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "bomFormat": "CycloneDX",
3
3
  "specVersion": "1.6",
4
- "serialNumber": "urn:uuid:b998bb6f-ce4c-50fa-a843-1dbcc6584fe3",
4
+ "serialNumber": "urn:uuid:e7c0861b-27d8-53fc-9aa5-7312e5af7ead",
5
5
  "version": 1,
6
6
  "metadata": {
7
7
  "component": {
8
8
  "type": "application",
9
- "bom-ref": "pkg:npm/%40fruggr/zendesk-mcp-server@3.3.0",
9
+ "bom-ref": "pkg:npm/%40fruggr/zendesk-mcp-server@3.3.1",
10
10
  "name": "@fruggr/zendesk-mcp-server",
11
- "version": "3.3.0",
12
- "purl": "pkg:npm/%40fruggr/zendesk-mcp-server@3.3.0"
11
+ "version": "3.3.1",
12
+ "purl": "pkg:npm/%40fruggr/zendesk-mcp-server@3.3.1"
13
13
  }
14
14
  },
15
15
  "components": [
@@ -27,37 +27,37 @@
27
27
  },
28
28
  {
29
29
  "type": "library",
30
- "bom-ref": "pkg:npm/%40modelcontextprotocol/core@2.2.0",
30
+ "bom-ref": "pkg:npm/%40modelcontextprotocol/core@2.3.1",
31
31
  "name": "@modelcontextprotocol/core",
32
- "version": "2.2.0",
33
- "purl": "pkg:npm/%40modelcontextprotocol/core@2.2.0",
32
+ "version": "2.3.1",
33
+ "purl": "pkg:npm/%40modelcontextprotocol/core@2.3.1",
34
34
  "licenses": [
35
35
  {
36
- "expression": "MIT"
36
+ "expression": "Apache-2.0"
37
37
  }
38
38
  ]
39
39
  },
40
40
  {
41
41
  "type": "library",
42
- "bom-ref": "pkg:npm/%40modelcontextprotocol/node@2.1.0",
42
+ "bom-ref": "pkg:npm/%40modelcontextprotocol/node@2.1.1",
43
43
  "name": "@modelcontextprotocol/node",
44
- "version": "2.1.0",
45
- "purl": "pkg:npm/%40modelcontextprotocol/node@2.1.0",
44
+ "version": "2.1.1",
45
+ "purl": "pkg:npm/%40modelcontextprotocol/node@2.1.1",
46
46
  "licenses": [
47
47
  {
48
- "expression": "MIT"
48
+ "expression": "Apache-2.0"
49
49
  }
50
50
  ]
51
51
  },
52
52
  {
53
53
  "type": "library",
54
- "bom-ref": "pkg:npm/%40modelcontextprotocol/server@2.2.0",
54
+ "bom-ref": "pkg:npm/%40modelcontextprotocol/server@2.3.1",
55
55
  "name": "@modelcontextprotocol/server",
56
- "version": "2.2.0",
57
- "purl": "pkg:npm/%40modelcontextprotocol/server@2.2.0",
56
+ "version": "2.3.1",
57
+ "purl": "pkg:npm/%40modelcontextprotocol/server@2.3.1",
58
58
  "licenses": [
59
59
  {
60
- "expression": "MIT"
60
+ "expression": "Apache-2.0"
61
61
  }
62
62
  ]
63
63
  },
@@ -999,10 +999,10 @@
999
999
  },
1000
1000
  {
1001
1001
  "type": "library",
1002
- "bom-ref": "pkg:npm/mdast-util-find-and-replace@3.0.2",
1002
+ "bom-ref": "pkg:npm/mdast-util-find-and-replace@3.0.3",
1003
1003
  "name": "mdast-util-find-and-replace",
1004
- "version": "3.0.2",
1005
- "purl": "pkg:npm/mdast-util-find-and-replace@3.0.2",
1004
+ "version": "3.0.3",
1005
+ "purl": "pkg:npm/mdast-util-find-and-replace@3.0.3",
1006
1006
  "licenses": [
1007
1007
  {
1008
1008
  "expression": "MIT"
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { n as readPackageInfo, p as readEnv, r as silentLogger } from "./index.js";
2
+ import { g as readEnv, n as readPackageInfo, r as silentLogger } from "./index.js";
3
3
  import { createHash, randomBytes } from "node:crypto";
4
4
  import { dirname, join } from "node:path";
5
5
  import { chmodSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { l as getOAuthUrls, r as silentLogger } from "./index.js";
3
- import { a as saveToken, c as grantCovers, d as escapeHtml, i as resolveTokenPath, l as requestedScope, o as generateCodeChallenge, r as loadToken, s as generateCodeVerifier, t as clearToken } from "./token-persistence-BeetFqPF.js";
2
+ import { f as getOAuthUrls, r as silentLogger } from "./index.js";
3
+ import { a as saveToken, c as grantCovers, d as escapeHtml, i as resolveTokenPath, l as requestedScope, o as generateCodeChallenge, r as loadToken, s as generateCodeVerifier, t as clearToken } from "./token-persistence-BUHh23BM.js";
4
4
  import { promisify } from "node:util";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fruggr/zendesk-mcp-server",
3
- "version": "3.3.0",
3
+ "version": "3.3.1",
4
4
  "mcpName": "io.github.fruggr/zendesk-mcp-server",
5
5
  "description": "Deep Zendesk MCP server for your AI assistant: search, draft, update and translate Help Center articles and manage Support tickets end to end — comments, triage and image attachments.",
6
6
  "type": "module",
@@ -72,12 +72,12 @@
72
72
  "engines": {
73
73
  "node": ">=22"
74
74
  },
75
- "packageManager": "pnpm@12.9.1+sha512.06b055fff5cd20dc12781de173ced3310ce0951bf2ec620211a16045d5444d5c93a663210764ef29a653a610459ba0368f0e74f84d80c54723cf95aae7bc0d6d",
75
+ "packageManager": "pnpm@12.11.1+sha512.5b0e3288239a7ea4155d2094f547066e122a1376b3cd785a680c9112e116913e64aaaecf20dd83cc34030fa67013f5c5511f9cf430dd8384188b4c60b8a2ad1e",
76
76
  "devDependencies": {
77
77
  "@biomejs/biome": "2.5.14",
78
- "@modelcontextprotocol/client": "2.2.0",
79
- "@modelcontextprotocol/node": "2.1.0",
80
- "@modelcontextprotocol/server": "2.2.0",
78
+ "@modelcontextprotocol/client": "2.3.1",
79
+ "@modelcontextprotocol/node": "2.1.1",
80
+ "@modelcontextprotocol/server": "2.3.1",
81
81
  "@semantic-release/changelog": "^7.0.0",
82
82
  "@semantic-release/exec": "^7.1.0",
83
83
  "@semantic-release/git": "^11.0.0",
@@ -98,7 +98,7 @@
98
98
  "hast-util-to-mdast": "10.1.2",
99
99
  "jose": "6.2.12",
100
100
  "lefthook": "^2.1.10",
101
- "msw": "^2.12.13",
101
+ "msw": "^3.0.0",
102
102
  "oidc-provider": "~9.12.2",
103
103
  "open": "11.0.4",
104
104
  "rehype-parse": "9.0.1",