@fruggr/zendesk-mcp-server 2.17.0 → 2.17.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.
Files changed (2) hide show
  1. package/dist/index.js +74 -71
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import { homedir, release } from "node:os";
6
6
  import open from "open";
7
7
  import { dirname, join } from "node:path";
8
8
  import { fileURLToPath, pathToFileURL } from "node:url";
9
+ import { parseArgs } from "node:util";
9
10
  import * as z from "zod/v4";
10
11
  import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
11
12
  import * as cheerio from "cheerio";
@@ -706,97 +707,99 @@ const parsePort = (raw, label) => {
706
707
  if (!DIGITS_ONLY.test(raw)) throw new Error(`Invalid ${label} value. Expected an integer 0-65535.`);
707
708
  return Number(raw);
708
709
  };
709
- const parsePortEnv = (raw, label) => raw === void 0 || raw === "" ? void 0 : parsePort(raw, label);
710
- const appendTo = (key) => (result, value) => {
711
- const list = result[key] ?? [];
712
- list.push(value);
713
- result[key] = list;
714
- };
715
- const STANDALONE_FLAGS = /* @__PURE__ */ new Map([
716
- ["--read-only", (result) => {
717
- result.readOnly = true;
718
- }],
719
- ["--no-topology", (result) => {
720
- result.topology = false;
721
- }],
722
- ["--no-promoted-articles", (result) => {
723
- result.promotedArticles = false;
724
- }],
725
- ["--dev", (result) => {
726
- result.dev = true;
727
- }]
710
+ const parsePortEnv = (raw, label) => raw === void 0 ? void 0 : parsePort(raw, label);
711
+ const requireNonEmptyEnv = (name) => {
712
+ const raw = process.env[name];
713
+ if (raw === "") throw new Error(`Empty ${name}. Set it to a value, or unset it entirely.`);
714
+ return raw;
715
+ };
716
+ const CLI_OPTIONS = {
717
+ mode: { type: "string" },
718
+ namespace: {
719
+ type: "string",
720
+ multiple: true
721
+ },
722
+ tool: {
723
+ type: "string",
724
+ multiple: true
725
+ },
726
+ "log-level": { type: "string" },
727
+ "hc-resource-scheme": { type: "string" },
728
+ transport: { type: "string" },
729
+ host: { type: "string" },
730
+ port: { type: "string" },
731
+ "public-url": { type: "string" },
732
+ "cors-origin": {
733
+ type: "string",
734
+ multiple: true
735
+ },
736
+ "callback-port": { type: "string" },
737
+ "read-only": { type: "boolean" },
738
+ "no-topology": { type: "boolean" },
739
+ "no-promoted-articles": { type: "boolean" },
740
+ dev: { type: "boolean" }
741
+ };
742
+ new Set(Object.entries(CLI_OPTIONS).filter(([, spec]) => spec.type === "string").map(([name]) => `--${name}`));
743
+ const FIELD_BY_FLAG = /* @__PURE__ */ new Map([
744
+ ["mode", "mode"],
745
+ ["namespace", "namespaces"],
746
+ ["tool", "tools"],
747
+ ["log-level", "logLevel"],
748
+ ["hc-resource-scheme", "hcResourceScheme"],
749
+ ["transport", "transport"],
750
+ ["host", "host"],
751
+ ["public-url", "publicUrl"],
752
+ ["cors-origin", "corsOrigins"]
728
753
  ]);
729
- const VALUED_FLAGS = /* @__PURE__ */ new Map([
730
- ["--mode", (result, value) => {
731
- result.mode = value;
732
- }],
733
- ["--hc-resource-scheme", (result, value) => {
734
- result.hcResourceScheme = value;
735
- }],
736
- ["--log-level", (result, value) => {
737
- result.logLevel = value;
738
- }],
739
- ["--transport", (result, value) => {
740
- result.transport = value;
741
- }],
742
- ["--host", (result, value) => {
743
- result.host = value;
744
- }],
745
- ["--public-url", (result, value) => {
746
- result.publicUrl = value;
747
- }],
748
- ["--port", (result, value) => {
749
- result.port = parsePort(value, "--port");
750
- }],
751
- ["--callback-port", (result, value) => {
752
- result.callbackPort = parsePort(value, "--callback-port");
753
- }],
754
- ["--namespace", appendTo("namespaces")],
755
- ["--tool", appendTo("tools")],
756
- ["--cors-origin", appendTo("corsOrigins")]
754
+ const STANDALONE_EFFECTS = /* @__PURE__ */ new Map([
755
+ ["read-only", { readOnly: true }],
756
+ ["no-topology", { topology: false }],
757
+ ["no-promoted-articles", { promotedArticles: false }],
758
+ ["dev", { dev: true }]
757
759
  ]);
758
760
  const parseCliArgs = (args) => {
759
- const result = {};
760
- for (let i = 0; i < args.length; i++) {
761
- const arg = args[i];
762
- if (arg === void 0) continue;
763
- const standalone = STANDALONE_FLAGS.get(arg);
764
- if (standalone) {
765
- standalone(result);
766
- continue;
767
- }
768
- const valued = VALUED_FLAGS.get(arg);
769
- const next = args[i + 1];
770
- if (valued && next) {
771
- valued(result, next);
772
- i++;
761
+ const { values, positionals } = parseArgs({
762
+ args,
763
+ options: CLI_OPTIONS,
764
+ allowPositionals: true
765
+ });
766
+ if (positionals.length > 1) throw new Error(`Expected one positional argument (the subdomain), got ${positionals.length}. A repeatable flag has to be repeated (--namespace tickets --namespace help_center); it does not take a space-separated list.`);
767
+ const result = { subdomain: positionals[0] };
768
+ for (const [flag, value] of Object.entries(values)) {
769
+ if (Array.isArray(value) ? value.includes("") : value === "") throw new Error(`Empty value for --${flag}. Provide a value, or omit the flag.`);
770
+ const effect = STANDALONE_EFFECTS.get(flag);
771
+ if (effect) {
772
+ Object.assign(result, effect);
773
773
  continue;
774
774
  }
775
- if (!arg.startsWith("-") && result.subdomain === void 0) result.subdomain = arg;
775
+ const field = FIELD_BY_FLAG.get(flag);
776
+ if (field) Object.assign(result, { [field]: value });
776
777
  }
778
+ if (values.port !== void 0) result.port = parsePort(values.port, "--port");
779
+ if (values["callback-port"] !== void 0) result.callbackPort = parsePort(values["callback-port"], "--callback-port");
777
780
  return result;
778
781
  };
779
782
  const resolveTransportSettings = (cli) => {
780
783
  const corsFromEnv = (process.env["CORS_ORIGIN"] ?? "").split(",").map((s) => s.trim()).filter((s) => s.length > 0);
781
784
  return {
782
- transport: cli.transport ?? process.env["TRANSPORT"] ?? "stdio",
783
- host: cli.host ?? process.env["HOST"] ?? "0.0.0.0",
784
- port: cli.port ?? parsePortEnv(process.env["PORT"], "PORT") ?? 3e3,
785
- publicUrl: cli.publicUrl ?? process.env["PUBLIC_URL"],
785
+ transport: cli.transport ?? requireNonEmptyEnv("TRANSPORT") ?? "stdio",
786
+ host: cli.host ?? requireNonEmptyEnv("HOST") ?? "0.0.0.0",
787
+ port: cli.port ?? parsePortEnv(requireNonEmptyEnv("PORT"), "PORT") ?? 3e3,
788
+ publicUrl: cli.publicUrl ?? requireNonEmptyEnv("PUBLIC_URL"),
786
789
  corsOrigins: [...cli.corsOrigins ?? [], ...corsFromEnv]
787
790
  };
788
791
  };
789
792
  const loadConfig = (argv = process.argv.slice(2)) => {
790
793
  const cli = parseCliArgs(argv);
791
- const subdomain = cli.subdomain ?? process.env["ZENDESK_SUBDOMAIN"] ?? "";
792
- const oauthClientId = process.env["ZENDESK_OAUTH_CLIENT_ID"] ?? (subdomain ? `${subdomain}_zendesk` : "");
794
+ const subdomain = cli.subdomain ?? requireNonEmptyEnv("ZENDESK_SUBDOMAIN") ?? "";
795
+ const oauthClientId = requireNonEmptyEnv("ZENDESK_OAUTH_CLIENT_ID") ?? `${subdomain}_zendesk`;
793
796
  const mode = cli.tools?.length ? "all" : cli.mode ?? "namespace";
794
- const callbackPort = cli.callbackPort ?? parsePortEnv(process.env["ZENDESK_OAUTH_CALLBACK_PORT"], "ZENDESK_OAUTH_CALLBACK_PORT");
795
- const hcResourceScheme = cli.hcResourceScheme ?? (process.env["HC_RESOURCE_SCHEME"] || void 0);
797
+ const callbackPort = cli.callbackPort ?? parsePortEnv(requireNonEmptyEnv("ZENDESK_OAUTH_CALLBACK_PORT"), "ZENDESK_OAUTH_CALLBACK_PORT");
798
+ const hcResourceScheme = cli.hcResourceScheme ?? requireNonEmptyEnv("HC_RESOURCE_SCHEME");
796
799
  return ConfigSchema.parse({
797
800
  subdomain,
798
801
  oauthClientId,
799
- logLevel: cli.logLevel ?? process.env["LOG_LEVEL"] ?? "info",
802
+ logLevel: cli.logLevel ?? requireNonEmptyEnv("LOG_LEVEL") ?? "info",
800
803
  mode,
801
804
  readOnly: cli.readOnly ?? false,
802
805
  namespaces: cli.namespaces,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fruggr/zendesk-mcp-server",
3
- "version": "2.17.0",
3
+ "version": "2.17.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",