@fruggr/zendesk-mcp-server 3.4.0 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -224,13 +224,16 @@ clients, MCP client wiring, CORS and what stays the operator's job:
224
224
  ## Tool surface
225
225
 
226
226
  Tools are grouped into namespaces: **Tickets**, **Help Center**, **Users &
227
- Organizations**, **Search**, and **Requests** — the end-user surface, which is
228
- opt-in and covered under [End-user mode](#end-user-mode). The server registers
227
+ Organizations**, **Search**, **Connector preferences** — this server's own
228
+ per-user settings, such as SLA alert thresholds, never written to Zendesk — and
229
+ **Requests** — the end-user surface, which is opt-in and covered under
230
+ [End-user mode](#end-user-mode). The server registers
229
231
  them in one of three modes, so you can trade granularity against context budget:
230
232
 
231
233
  - **`all`**: every operation as its own tool, for clients with good tool selection;
232
234
  - **`namespace`** (default): one proxy tool per namespace, a balanced middle ground;
233
- - **`single`**: a single `zendesk` tool, for models with limited tool slots.
235
+ - **`single`**: a single `zendesk` tool, for models with limited tool slots (the
236
+ connector's own preferences keep their `connector_preferences` proxy beside it).
234
237
 
235
238
  Proxies take `{ "operation": "<tool_name>", "params": { … } }` and validate
236
239
  `params` through the original schema. `--namespace`, `--tool` and `--read-only`
@@ -5198,7 +5198,7 @@ The above copyright notice and this permission notice shall be included in all c
5198
5198
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5199
5199
  ```
5200
5200
 
5201
- ## zod@4.6.6
5201
+ ## zod@4.6.7
5202
5202
 
5203
5203
  License: MIT
5204
5204
 
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { C as __exportAll, S as __commonJSMin, T as __toESM, _ as OAuthErrorCode, a as silentLogger, b as errnoCode, c as deadlineSignal, d as DEFAULT_MAX_REQUEST_BODY_SIZE, f as WebStandardStreamableHTTPServerTransport, g as OAuthError, h as verifyBearerToken, m as isLegacyRequest, n as defaultServerFactory, o as zendeskGet, p as createMcpHandler, r as createRequestStateCodec, s as REQUEST_TIMEOUT_MS, t as createServerCaches, u as getOAuthUrls, v as STARTUP_DOCS, w as __require, y as createStartupError } 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-r0v9RO3q.js";
2
+ import { A as __commonJSMin, C as isLegacyRequest, D as STARTUP_DOCS, E as OAuthErrorCode, M as __require, N as __toESM, O as createStartupError, S as createMcpHandler, T as OAuthError, _ as deadlineSignal, a as watchGrantAlerts, b as DEFAULT_MAX_REQUEST_BODY_SIZE, c as configDir, g as REQUEST_TIMEOUT_MS, h as zendeskGet, i as defaultServerFactory, j as __exportAll, k as errnoCode, m as silentLogger, n as listenedUris, o as startSlaRuntime, p as createRequestStateCodec, r as createServerCaches, t as createListenFeed, w as verifyBearerToken, x as WebStandardStreamableHTTPServerTransport, y as getOAuthUrls } from "./index.js";
3
+ import { a as supportedScopes, i as requestedScope, n as generateCodeVerifier, o as escapeHtml, t as generateCodeChallenge } from "./pkce-DE_v9NQy.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";
@@ -36703,7 +36703,6 @@ const sendUnauthorized = (res, issuer) => {
36703
36703
  sendJsonRpcError(res, 401, -32e3, MISSING_BEARER_MESSAGE, { "WWW-Authenticate": wwwAuthenticate });
36704
36704
  };
36705
36705
  const asNodeRequest = (req) => req;
36706
- const isListenRequest = (body) => typeof body === "object" && body !== null && body.method === "subscriptions/listen";
36707
36706
  const MAX_BODY_BYTES = 4194304;
36708
36707
  const readJsonBody = (req, maxBodyBytes) => new Promise((resolve) => {
36709
36708
  const chunks = [];
@@ -36772,6 +36771,7 @@ const startHttpTransport = async (config, logger = silentLogger, options = {}) =
36772
36771
  const buildAuthorizationServer = await prepareAuthorizationServer(config, logger);
36773
36772
  let authorizationServer;
36774
36773
  const sessions = /* @__PURE__ */ new Map();
36774
+ const sla = startSlaRuntime(config);
36775
36775
  const idleTimeoutMs = options.sessionIdleTimeoutMs ?? SESSION_IDLE_TIMEOUT_MS;
36776
36776
  const maxBodyBytes = options.maxBodyBytes ?? 4194304;
36777
36777
  const serverFactory = options.serverFactory ?? defaultServerFactory;
@@ -36807,6 +36807,7 @@ const startHttpTransport = async (config, logger = silentLogger, options = {}) =
36807
36807
  getToken: () => auth.zendeskToken,
36808
36808
  logger,
36809
36809
  onUnauthorized: () => void as.revokeGrant(auth.grantId),
36810
+ slaRuntime: sla.runtime,
36810
36811
  requestStateCodec: createRequestStateCodec({
36811
36812
  key: as.requestStateKey,
36812
36813
  principal: () => auth.grantId
@@ -36860,18 +36861,36 @@ const startHttpTransport = async (config, logger = silentLogger, options = {}) =
36860
36861
  onUnauthorized,
36861
36862
  caches,
36862
36863
  requestStateCodec,
36864
+ slaRuntime: sla.runtime,
36865
+ alertPolling: "grant",
36863
36866
  notifyResourceUpdated: (uri) => handler.notify.resourceUpdated(uri)
36864
36867
  });
36865
36868
  }, {
36866
36869
  legacy: "reject",
36867
36870
  onerror: (err) => logger.debug("mcp_request_rejected", { error: err.message })
36868
36871
  });
36872
+ const listens = createListenFeed();
36873
+ const stopAlerts = watchGrantAlerts({
36874
+ config,
36875
+ getToken,
36876
+ caches,
36877
+ slaRuntime: sla.runtime,
36878
+ logger,
36879
+ listens,
36880
+ notify: (uri) => handler.notify.resourceUpdated(uri),
36881
+ onUnauthorized
36882
+ });
36869
36883
  const entry = {
36870
36884
  auth,
36871
36885
  revoked: false,
36872
36886
  inFlight: 0,
36887
+ listens,
36873
36888
  handle: toNodeHandler(handler),
36874
- close: () => handler.close()
36889
+ close: () => {
36890
+ stopAlerts();
36891
+ listens.closeAll();
36892
+ return handler.close();
36893
+ }
36875
36894
  };
36876
36895
  return entry;
36877
36896
  };
@@ -36890,7 +36909,13 @@ const startHttpTransport = async (config, logger = silentLogger, options = {}) =
36890
36909
  expiresAt: token.expiresAt,
36891
36910
  extra: { verified: token }
36892
36911
  };
36893
- const counted = !isListenRequest(body);
36912
+ const listened = listenedUris(body);
36913
+ const counted = listened === void 0;
36914
+ if (listened) {
36915
+ const stream = Symbol("listen");
36916
+ res.once("close", () => entry.listens.close(stream));
36917
+ entry.listens.open(stream, listened);
36918
+ }
36894
36919
  if (counted) entry.inFlight += 1;
36895
36920
  try {
36896
36921
  await entry.handle(request, res, body);
@@ -37004,6 +37029,7 @@ const startHttpTransport = async (config, logger = silentLogger, options = {}) =
37004
37029
  port: boundPort,
37005
37030
  close: async () => {
37006
37031
  clearInterval(sweeper);
37032
+ sla.stop();
37007
37033
  await Promise.all([...sessions.values()].map((session) => session.close()));
37008
37034
  sessions.clear();
37009
37035
  await grants.closeAll();