@upstash/mcp-server 0.2.1 → 0.2.2

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/dist/index.js CHANGED
@@ -10,6 +10,12 @@ import { createServer } from "http";
10
10
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
11
11
 
12
12
  // src/log.ts
13
+ import { appendFileSync } from "fs";
14
+ import path from "path";
15
+ var debugLogPath = null;
16
+ function initDebugLog(projectRoot) {
17
+ debugLogPath = path.resolve(projectRoot, "upstash-debug.log");
18
+ }
13
19
  function log(...args) {
14
20
  const msg = `[DEBUG ${(/* @__PURE__ */ new Date()).toISOString()}] ${args.map((arg) => typeof arg === "string" ? arg : JSON.stringify(arg)).join(" ")}
15
21
  `;
@@ -17,11 +23,25 @@ function log(...args) {
17
23
  logs.push(msg);
18
24
  }
19
25
  process.stderr.write(msg);
26
+ if (debugLogPath) {
27
+ try {
28
+ appendFileSync(debugLogPath, msg);
29
+ } catch {
30
+ }
31
+ }
20
32
  }
21
33
  var logsStore = /* @__PURE__ */ new Map();
22
34
 
23
35
  // src/tools/utils.ts
24
36
  import { z } from "zod";
37
+
38
+ // src/tools/helpers.ts
39
+ var json = (value) => typeof value === "string" ? value : JSON.stringify(value, null, 2);
40
+ function tool(t) {
41
+ return t;
42
+ }
43
+
44
+ // src/tools/utils.ts
25
45
  var utilTools = {
26
46
  util_timestamps_to_date: tool({
27
47
  description: `Use this tool to convert a timestamp to a human-readable date`,
@@ -49,7 +69,8 @@ import { z as z2 } from "zod";
49
69
  // src/config.ts
50
70
  var config = {
51
71
  apiKey: "",
52
- email: ""
72
+ email: "",
73
+ disableTelemetry: false
53
74
  };
54
75
 
55
76
  // src/middlewares.ts
@@ -91,27 +112,56 @@ var applyMiddlewares = async (req, func) => {
91
112
  return next();
92
113
  };
93
114
 
115
+ // src/version.ts
116
+ var VERSION = "v0.2.2";
117
+
118
+ // src/telemetry.ts
119
+ function getRuntime() {
120
+ var _a, _b, _c;
121
+ if (globalThis.Bun !== void 0) {
122
+ return `bun@${globalThis.Bun.version}`;
123
+ }
124
+ if (globalThis.Deno !== void 0) {
125
+ const denoVersion = (_b = (_a = globalThis.Deno) == null ? void 0 : _a.version) == null ? void 0 : _b.deno;
126
+ return denoVersion ? `deno@${denoVersion}` : "deno";
127
+ }
128
+ if (typeof process !== "undefined" && ((_c = process.versions) == null ? void 0 : _c.node)) {
129
+ return `node@${process.versions.node}`;
130
+ }
131
+ return "unknown";
132
+ }
133
+ function getPlatform() {
134
+ if (typeof process !== "undefined" && process.platform) {
135
+ return process.platform;
136
+ }
137
+ return "unknown";
138
+ }
139
+ var telemetry = {
140
+ runtime: getRuntime(),
141
+ platform: getPlatform(),
142
+ sdk: `@upstash/mcp-server@${VERSION}`
143
+ };
144
+
94
145
  // src/http.ts
95
- import fetch from "node-fetch";
96
146
  var HttpClient = class {
97
147
  constructor(config2) {
98
148
  this.baseUrl = config2.baseUrl.replace(/\/$/, "");
99
149
  this.qstashToken = config2.qstashToken;
100
150
  }
101
- async get(path, query) {
102
- return this.requestWithMiddleware({ method: "GET", path, query });
151
+ async get(path2, query) {
152
+ return this.requestWithMiddleware({ method: "GET", path: path2, query });
103
153
  }
104
- async post(path, body, headers) {
105
- return this.requestWithMiddleware({ method: "POST", path, body, headers });
154
+ async post(path2, body, headers) {
155
+ return this.requestWithMiddleware({ method: "POST", path: path2, body, headers });
106
156
  }
107
- async put(path, body, headers) {
108
- return this.requestWithMiddleware({ method: "PUT", path, body, headers });
157
+ async put(path2, body, headers) {
158
+ return this.requestWithMiddleware({ method: "PUT", path: path2, body, headers });
109
159
  }
110
- async patch(path, body) {
111
- return this.requestWithMiddleware({ method: "PATCH", path, body });
160
+ async patch(path2, body) {
161
+ return this.requestWithMiddleware({ method: "PATCH", path: path2, body });
112
162
  }
113
- async delete(path, body) {
114
- return this.requestWithMiddleware({ method: "DELETE", path, body });
163
+ async delete(path2, body) {
164
+ return this.requestWithMiddleware({ method: "DELETE", path: path2, body });
115
165
  }
116
166
  async requestWithMiddleware(req) {
117
167
  const res = await applyMiddlewares(req, async (req2) => {
@@ -145,11 +195,17 @@ var HttpClient = class {
145
195
  const token = [config.email, config.apiKey].join(":");
146
196
  authHeader = `Basic ${Buffer.from(token).toString("base64")}`;
147
197
  }
198
+ const telemetryHeaders = config.disableTelemetry ? {} : {
199
+ "Upstash-Telemetry-Runtime": telemetry.runtime,
200
+ "Upstash-Telemetry-Platform": telemetry.platform,
201
+ "Upstash-Telemetry-Sdk": telemetry.sdk
202
+ };
148
203
  const init = {
149
204
  method: req.method,
150
205
  headers: {
151
206
  "Content-Type": "application/json",
152
207
  Authorization: authHeader,
208
+ ...telemetryHeaders,
153
209
  ...req.headers
154
210
  }
155
211
  };
@@ -269,7 +325,6 @@ var redisBackupTools = {
269
325
 
270
326
  // src/tools/redis/command.ts
271
327
  import { z as z3 } from "zod";
272
- import fetch2 from "node-fetch";
273
328
  var redisCommandTools = {
274
329
  redis_database_run_redis_commands: tool({
275
330
  description: `Run one or more Redis commands on a specific Upstash redis database. Either provide database_id OR both database_rest_url and database_rest_token.
@@ -308,7 +363,7 @@ NOTE: Multiple commands will be executed as a pipeline for better performance.`,
308
363
  const isSingleCommand = commands.length === 1;
309
364
  const url = isSingleCommand ? restUrl : restUrl + "/pipeline";
310
365
  const body = isSingleCommand ? JSON.stringify(commands[0]) : JSON.stringify(commands);
311
- const req = await fetch2(url, {
366
+ const req = await fetch(url, {
312
367
  method: "POST",
313
368
  body,
314
369
  headers: {
@@ -525,6 +580,7 @@ var parseUsageData = (data) => {
525
580
  if (!Array.isArray(data)) return "INVALID DATA";
526
581
  if (data.length === 0 || data.length === 1) return "NO DATA";
527
582
  const filteredData = data.filter((d) => d.x && d.y);
583
+ if (filteredData.length === 0) return "NO DATA";
528
584
  return {
529
585
  start: filteredData[0].x,
530
586
  // last one can be null, so use the second last
@@ -543,57 +599,76 @@ var redisTools = {
543
599
  };
544
600
 
545
601
  // src/tools/qstash/qstash.ts
546
- import { z as z5 } from "zod";
602
+ import { z as z6 } from "zod";
547
603
 
548
604
  // src/tools/qstash/utils.ts
549
- var cachedToken = null;
550
- var tokenExpiry = 0;
551
- async function getQStashToken() {
605
+ var LOCAL_QSTASH_TOKEN = "eyJVc2VySUQiOiJkZWZhdWx0VXNlciIsIlBhc3N3b3JkIjoiZGVmYXVsdFBhc3N3b3JkIn0=";
606
+ var REGION_URLS = {
607
+ eu: "https://qstash-eu-central-1.upstash.io",
608
+ us: "https://qstash-us-east-1.upstash.io"
609
+ };
610
+ var REGION_API_NAMES = {
611
+ eu: "eu-central-1",
612
+ us: "us-east-1"
613
+ };
614
+ var cachedUsers = null;
615
+ var cacheExpiry = 0;
616
+ async function getQStashCredentials(region) {
552
617
  const now = Date.now();
553
- if (cachedToken && now < tokenExpiry) {
554
- return cachedToken;
618
+ if (!cachedUsers || now >= cacheExpiry) {
619
+ try {
620
+ cachedUsers = await http.get("v2/qstash/users");
621
+ cacheExpiry = now + 60 * 1e3;
622
+ } catch (error) {
623
+ throw new Error(
624
+ `Failed to get QStash credentials: ${error instanceof Error ? error.message : String(error)}`
625
+ );
626
+ }
555
627
  }
556
- try {
557
- const user = await http.get("qstash/user");
558
- cachedToken = user.token;
559
- tokenExpiry = now + 60 * 60 * 1e3;
560
- return user.token;
561
- } catch (error) {
562
- throw new Error(
563
- `Failed to get QStash token: ${error instanceof Error ? error.message : String(error)}`
564
- );
628
+ const apiRegion = REGION_API_NAMES[region] ?? REGION_API_NAMES["eu"];
629
+ const match = cachedUsers.find((u) => u.region === apiRegion);
630
+ if (!match) {
631
+ throw new Error(`No QStash user found for region '${region}' (${apiRegion})`);
565
632
  }
633
+ return { token: match.token };
566
634
  }
567
- async function createQStashClientWithToken(creds = {}) {
568
- if (!(creds == null ? void 0 : creds.token)) {
569
- creds.token = await getQStashToken();
635
+ async function createQStashClientWithToken(options) {
636
+ const { qstash_creds, region, local_mode_port } = options;
637
+ if (qstash_creds) {
638
+ return createQStashClient({ url: qstash_creds.url, token: qstash_creds.token });
570
639
  }
571
- return createQStashClient({
572
- url: creds == null ? void 0 : creds.url,
573
- token: creds == null ? void 0 : creds.token
574
- });
640
+ if (region === "local") {
641
+ return createQStashClient({
642
+ url: `http://localhost:${local_mode_port}`,
643
+ token: LOCAL_QSTASH_TOKEN
644
+ });
645
+ }
646
+ const effectiveRegion = region && REGION_URLS[region] ? region : "eu";
647
+ const fetched = await getQStashCredentials(effectiveRegion);
648
+ return createQStashClient({ url: REGION_URLS[effectiveRegion], token: fetched.token });
575
649
  }
576
650
 
577
- // src/tools/qstash/qstash.ts
578
- var qstashCreds = {
579
- qstash_creds: z5.undefined()
580
- // qstash_creds: z
581
- // .object({
582
- // url: z.string(),
583
- // token: z.string(),
584
- // })
585
- // .optional()
586
- // .describe(
587
- // "Optional qstash credentials. Use for local qstash connections and external qstash deployments"
588
- // ),
651
+ // src/tools/qstash/common.ts
652
+ import { z as z5 } from "zod";
653
+ var qstashCommon = {
654
+ region: z5.enum(["eu", "us", "local"]).default("eu").describe("QStash region to use. To use local mode, pick `local`"),
655
+ local_mode_port: z5.number().default(8080).describe(
656
+ "Only provide when using local mode and if default does not work, the port is usually in the .env file"
657
+ ),
658
+ qstash_creds: z5.object({
659
+ url: z5.string(),
660
+ token: z5.string()
661
+ }).optional().describe("Custom qstash credentials, overrides `region`")
589
662
  };
663
+
664
+ // src/tools/qstash/qstash.ts
590
665
  var qstashTools = {
591
666
  qstash_get_user_token: tool({
592
667
  description: `Get the QSTASH_TOKEN and QSTASH_URL of the current user. This
593
668
  is not needed for the mcp tools since the token is automatically fetched from
594
669
  the Upstash API for them.`,
595
670
  handler: async () => {
596
- const user = await http.get("qstash/user");
671
+ const user = await http.get("v2/qstash/user");
597
672
  return [json(user)];
598
673
  }
599
674
  }),
@@ -601,42 +676,42 @@ var qstashTools = {
601
676
  description: `Publish a message to a destination URL using QStash. This
602
677
  sends an HTTP request to the specified destination via QStash's message
603
678
  queue. This can also be used to trigger a upstash workflow run.`,
604
- inputSchema: z5.object({
605
- destination: z5.string().describe("The destination URL to send the message to (e.g., 'https://example.com')"),
606
- body: z5.string().optional().describe("Request body (JSON string or plain text)"),
607
- method: z5.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional().describe("HTTP method (optional, defaults to POST)").default("POST"),
608
- delay: z5.string().optional().describe("Delay before message delivery (e.g., '10s', '5m', '1h')"),
609
- retries: z5.number().optional().describe("Number of retries on failure, default is 3"),
610
- callback: z5.string().optional().describe("Callback URL that will be called when the message is successfully delivered"),
611
- failureCallback: z5.string().optional().describe("Callback URL that will be called when the message is failed to deliver"),
612
- timeout: z5.string().optional().describe("Request timeout (e.g., '30s', '1h')"),
613
- queueName: z5.string().optional().describe(
679
+ inputSchema: z6.object({
680
+ destination: z6.string().describe("The destination URL to send the message to (e.g., 'https://example.com')"),
681
+ body: z6.string().optional().describe("Request body (JSON string or plain text)"),
682
+ method: z6.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional().describe("HTTP method (optional, defaults to POST)").default("POST"),
683
+ delay: z6.string().optional().describe("Delay before message delivery (e.g., '10s', '5m', '1h')"),
684
+ retries: z6.number().optional().describe("Number of retries on failure, default is 3"),
685
+ callback: z6.string().optional().describe("Callback URL that will be called when the message is successfully delivered"),
686
+ failureCallback: z6.string().optional().describe("Callback URL that will be called when the message is failed to deliver"),
687
+ timeout: z6.string().optional().describe("Request timeout (e.g., '30s', '1h')"),
688
+ queueName: z6.string().optional().describe(
614
689
  "Queue name to use, you have to first create the queue in upstash. Prefer the flow control key instead"
615
690
  ),
616
- flow_control: z5.object({
617
- key: z5.string().describe("Unique identifier for grouping messages under same flow control rules"),
618
- parallelism: z5.number().optional().describe("Max concurrent active calls (default: unlimited)"),
619
- rate: z5.number().optional().describe("Max calls per period (default: unlimited)"),
620
- period: z5.string().optional().describe("Time window for rate limit (e.g., '1s', '1m', '1h', default: '1s')")
691
+ flow_control: z6.object({
692
+ key: z6.string().describe("Unique identifier for grouping messages under same flow control rules"),
693
+ parallelism: z6.number().optional().describe("Max concurrent active calls (default: unlimited)"),
694
+ rate: z6.number().optional().describe("Max calls per period (default: unlimited)"),
695
+ period: z6.string().optional().describe("Time window for rate limit (e.g., '1s', '1m', '1h', default: '1s')")
621
696
  }).optional().describe("Flow control for rate limiting and parallelism management"),
622
- extraHeaders: z5.record(z5.string()).optional().describe("Extra headers to add to the request"),
623
- ...qstashCreds
697
+ extraHeaders: z6.record(z6.string()).optional().describe("Extra headers to add to the request"),
698
+ ...qstashCommon
624
699
  }),
625
- handler: async ({
626
- destination,
627
- body,
628
- method,
629
- extraHeaders,
630
- delay,
631
- retries,
632
- callback,
633
- failureCallback,
634
- timeout,
635
- queueName,
636
- flow_control,
637
- qstash_creds
638
- }) => {
639
- const client = await createQStashClientWithToken(qstash_creds);
700
+ handler: async (params) => {
701
+ const {
702
+ destination,
703
+ body,
704
+ method,
705
+ extraHeaders,
706
+ delay,
707
+ retries,
708
+ callback,
709
+ failureCallback,
710
+ timeout,
711
+ queueName,
712
+ flow_control
713
+ } = params;
714
+ const client = await createQStashClientWithToken(params);
640
715
  const requestHeaders = {};
641
716
  if (method) {
642
717
  requestHeaders["Upstash-Method"] = method;
@@ -687,10 +762,10 @@ var qstashTools = {
687
762
  }),
688
763
  qstash_logs_list: tool({
689
764
  description: `List QStash logs with optional filtering. Returns a paginated list of message logs without their bodies.`,
690
- inputSchema: z5.object({
691
- cursor: z5.string().optional().describe("Cursor for pagination"),
692
- messageId: z5.string().optional().describe("Filter logs by message ID"),
693
- state: z5.enum([
765
+ inputSchema: z6.object({
766
+ cursor: z6.string().optional().describe("Cursor for pagination"),
767
+ messageId: z6.string().optional().describe("Filter logs by message ID"),
768
+ state: z6.enum([
694
769
  "CREATED",
695
770
  "ACTIVE",
696
771
  "RETRY",
@@ -701,21 +776,27 @@ var qstashTools = {
701
776
  "CANCEL_REQUESTED",
702
777
  "CANCELLED"
703
778
  ]).optional().describe("Filter logs by state"),
704
- url: z5.string().optional().describe("Filter logs by URL"),
705
- topicName: z5.string().optional().describe("Filter logs by topic name"),
706
- scheduleId: z5.string().optional().describe("Filter logs by schedule ID"),
707
- queueName: z5.string().optional().describe("Filter logs by queue name"),
708
- fromDate: z5.number().optional().describe("Filter logs from date (Unix timestamp in milliseconds)"),
709
- toDate: z5.number().optional().describe("Filter logs to date (Unix timestamp in milliseconds)"),
710
- count: z5.number().max(1e3).optional().describe("Number of logs to return (max 1000)"),
711
- ...qstashCreds
779
+ url: z6.string().optional().describe("Filter logs by URL"),
780
+ topicName: z6.string().optional().describe("Filter logs by topic name"),
781
+ scheduleId: z6.string().optional().describe("Filter logs by schedule ID"),
782
+ queueName: z6.string().optional().describe("Filter logs by queue name"),
783
+ fromDate: z6.number().optional().describe("Filter logs from date (Unix timestamp in milliseconds)"),
784
+ toDate: z6.number().optional().describe("Filter logs to date (Unix timestamp in milliseconds)"),
785
+ count: z6.number().max(1e3).optional().describe("Number of logs to return (max 1000)"),
786
+ ...qstashCommon
712
787
  }),
713
788
  handler: async (params) => {
714
- const client = await createQStashClientWithToken(params.qstash_creds);
789
+ const {
790
+ region: _region,
791
+ local_mode_port: _local_mode_port,
792
+ qstash_creds: _qstash_creds,
793
+ ...query
794
+ } = params;
795
+ const client = await createQStashClientWithToken(params);
715
796
  const response = await client.get("v2/logs", {
716
797
  trimBody: 0,
717
798
  groupBy: "messageId",
718
- ...params
799
+ ...query
719
800
  });
720
801
  const firstMessageFields = Object.fromEntries(
721
802
  Object.entries(response.messages[0] ?? {}).filter(
@@ -738,12 +819,13 @@ var qstashTools = {
738
819
  }),
739
820
  qstash_logs_get: tool({
740
821
  description: `Get details of a single QStash log item by message ID without trimming the body.`,
741
- inputSchema: z5.object({
742
- messageId: z5.string().describe("The message ID to get details for"),
743
- ...qstashCreds
822
+ inputSchema: z6.object({
823
+ messageId: z6.string().describe("The message ID to get details for"),
824
+ ...qstashCommon
744
825
  }),
745
- handler: async ({ messageId, qstash_creds }) => {
746
- const client = await createQStashClientWithToken(qstash_creds);
826
+ handler: async (params) => {
827
+ const { messageId } = params;
828
+ const client = await createQStashClientWithToken(params);
747
829
  const response = await client.get("v2/logs", { messageId });
748
830
  if (response.messages.length === 0) {
749
831
  return "No log entry found for the specified message ID";
@@ -754,25 +836,31 @@ var qstashTools = {
754
836
  }),
755
837
  qstash_dlq_list: tool({
756
838
  description: `List messages in the QStash Dead Letter Queue (DLQ) with optional filtering.`,
757
- inputSchema: z5.object({
758
- cursor: z5.string().optional().describe("Cursor for pagination"),
759
- messageId: z5.string().optional().describe("Filter DLQ messages by message ID"),
760
- url: z5.string().optional().describe("Filter DLQ messages by URL"),
761
- topicName: z5.string().optional().describe("Filter DLQ messages by topic name"),
762
- scheduleId: z5.string().optional().describe("Filter DLQ messages by schedule ID"),
763
- queueName: z5.string().optional().describe("Filter DLQ messages by queue name"),
764
- fromDate: z5.number().optional().describe("Filter from date (Unix timestamp in milliseconds)"),
765
- toDate: z5.number().optional().describe("Filter to date (Unix timestamp in milliseconds)"),
766
- responseStatus: z5.number().optional().describe("Filter by HTTP response status code"),
767
- callerIp: z5.string().optional().describe("Filter by IP address of the publisher"),
768
- count: z5.number().max(100).optional().describe("Number of messages to return (max 100)"),
769
- ...qstashCreds
839
+ inputSchema: z6.object({
840
+ cursor: z6.string().optional().describe("Cursor for pagination"),
841
+ messageId: z6.string().optional().describe("Filter DLQ messages by message ID"),
842
+ url: z6.string().optional().describe("Filter DLQ messages by URL"),
843
+ topicName: z6.string().optional().describe("Filter DLQ messages by topic name"),
844
+ scheduleId: z6.string().optional().describe("Filter DLQ messages by schedule ID"),
845
+ queueName: z6.string().optional().describe("Filter DLQ messages by queue name"),
846
+ fromDate: z6.number().optional().describe("Filter from date (Unix timestamp in milliseconds)"),
847
+ toDate: z6.number().optional().describe("Filter to date (Unix timestamp in milliseconds)"),
848
+ responseStatus: z6.number().optional().describe("Filter by HTTP response status code"),
849
+ callerIp: z6.string().optional().describe("Filter by IP address of the publisher"),
850
+ count: z6.number().max(100).optional().describe("Number of messages to return (max 100)"),
851
+ ...qstashCommon
770
852
  }),
771
853
  handler: async (params) => {
772
- const client = await createQStashClientWithToken(params.qstash_creds);
854
+ const {
855
+ region: _region,
856
+ local_mode_port: _local_mode_port,
857
+ qstash_creds: _qstash_creds,
858
+ ...query
859
+ } = params;
860
+ const client = await createQStashClientWithToken(params);
773
861
  const response = await client.get("v2/dlq", {
774
862
  trimBody: 0,
775
- ...params
863
+ ...query
776
864
  });
777
865
  return [
778
866
  `Found ${response.messages.length} DLQ messages`,
@@ -783,59 +871,63 @@ var qstashTools = {
783
871
  }),
784
872
  qstash_dlq_get: tool({
785
873
  description: `Get details of a single DLQ message by DLQ ID.`,
786
- inputSchema: z5.object({
787
- dlqId: z5.string().describe("The DLQ ID of the message to retrieve"),
788
- ...qstashCreds
874
+ inputSchema: z6.object({
875
+ dlqId: z6.string().describe("The DLQ ID of the message to retrieve"),
876
+ ...qstashCommon
789
877
  }),
790
- handler: async ({ dlqId, qstash_creds }) => {
791
- const client = await createQStashClientWithToken(qstash_creds);
878
+ handler: async (params) => {
879
+ const { dlqId } = params;
880
+ const client = await createQStashClientWithToken(params);
792
881
  const message = await client.get(`v2/dlq/${dlqId}`);
793
882
  return [`DLQ message details for ID: ${dlqId}`, json(message)];
794
883
  }
795
884
  }),
796
885
  qstash_schedules_list: tool({
797
886
  description: `List all QStash schedules.`,
798
- handler: async ({ qstash_creds }) => {
799
- const client = await createQStashClientWithToken(qstash_creds);
887
+ inputSchema: z6.object({
888
+ ...qstashCommon
889
+ }),
890
+ handler: async (params) => {
891
+ const client = await createQStashClientWithToken(params);
800
892
  const schedules = await client.get("v2/schedules");
801
893
  return [`Found ${schedules.length} schedules`, json(schedules)];
802
894
  }
803
895
  }),
804
896
  qstash_schedules_manage: tool({
805
897
  description: `Create, update, or manage QStash schedules. This tool handles create, update (by providing scheduleId), pause, resume, and delete operations in one unified interface.`,
806
- inputSchema: z5.object({
807
- operation: z5.enum(["create", "update", "pause", "resume", "delete"]).describe("The operation to perform"),
808
- scheduleId: z5.string().optional().describe("Schedule ID (required for update, pause, resume, delete operations)"),
809
- destination: z5.string().optional().describe("Destination URL or topic name (required for create/update)"),
810
- cron: z5.string().optional().describe("Cron expression (required for create/update)"),
811
- method: z5.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional().describe("HTTP method (optional, defaults to POST)"),
812
- headers: z5.record(z5.string()).optional().describe("Request headers as key-value pairs"),
813
- body: z5.string().optional().describe("Request body"),
814
- delay: z5.string().optional().describe("Delay before message delivery (e.g., '10s', '5m', '1h')"),
815
- retries: z5.number().optional().describe("Number of retries on failure"),
816
- callback: z5.string().optional().describe("Callback URL for successful delivery"),
817
- failureCallback: z5.string().optional().describe("Callback URL for failed delivery"),
818
- timeout: z5.string().optional().describe("Request timeout (e.g., '30s')"),
819
- queueName: z5.string().optional().describe("Queue name to use"),
820
- ...qstashCreds
898
+ inputSchema: z6.object({
899
+ operation: z6.enum(["create", "update", "pause", "resume", "delete"]).describe("The operation to perform"),
900
+ scheduleId: z6.string().optional().describe("Schedule ID (required for update, pause, resume, delete operations)"),
901
+ destination: z6.string().optional().describe("Destination URL or topic name (required for create/update)"),
902
+ cron: z6.string().optional().describe("Cron expression (required for create/update)"),
903
+ method: z6.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional().describe("HTTP method (optional, defaults to POST)"),
904
+ headers: z6.record(z6.string()).optional().describe("Request headers as key-value pairs"),
905
+ body: z6.string().optional().describe("Request body"),
906
+ delay: z6.string().optional().describe("Delay before message delivery (e.g., '10s', '5m', '1h')"),
907
+ retries: z6.number().optional().describe("Number of retries on failure"),
908
+ callback: z6.string().optional().describe("Callback URL for successful delivery"),
909
+ failureCallback: z6.string().optional().describe("Callback URL for failed delivery"),
910
+ timeout: z6.string().optional().describe("Request timeout (e.g., '30s')"),
911
+ queueName: z6.string().optional().describe("Queue name to use"),
912
+ ...qstashCommon
821
913
  }),
822
- handler: async ({
823
- operation,
824
- scheduleId,
825
- destination,
826
- cron,
827
- method = "POST",
828
- headers,
829
- body,
830
- delay,
831
- retries,
832
- callback,
833
- failureCallback,
834
- timeout,
835
- queueName,
836
- qstash_creds
837
- }) => {
838
- const client = await createQStashClientWithToken(qstash_creds);
914
+ handler: async (params) => {
915
+ const {
916
+ operation,
917
+ scheduleId,
918
+ destination,
919
+ cron,
920
+ method = "POST",
921
+ headers,
922
+ body,
923
+ delay,
924
+ retries,
925
+ callback,
926
+ failureCallback,
927
+ timeout,
928
+ queueName
929
+ } = params;
930
+ const client = await createQStashClientWithToken(params);
839
931
  switch (operation) {
840
932
  case "create":
841
933
  case "update": {
@@ -915,25 +1007,31 @@ var qstashTools = {
915
1007
  };
916
1008
 
917
1009
  // src/tools/qstash/workflow.ts
918
- import { z as z6 } from "zod";
1010
+ import { z as z7 } from "zod";
919
1011
  var workflowTools = {
920
1012
  workflow_logs_list: tool({
921
1013
  description: `List Upstash Workflow logs with optional filtering. Returns grouped workflow runs with their execution details.`,
922
- inputSchema: z6.object({
923
- cursor: z6.string().optional().describe("Cursor for pagination"),
924
- workflowRunId: z6.string().optional().describe("Filter by specific workflow run ID"),
925
- count: z6.number().optional().describe("Number of workflow runs to return"),
926
- state: z6.enum(["RUN_STARTED", "RUN_SUCCESS", "RUN_FAILED", "RUN_CANCELED"]).optional().describe("Filter by workflow state"),
927
- workflowUrl: z6.string().optional().describe("Filter by workflow URL (exact match)"),
928
- workflowCreatedAt: z6.number().optional().describe("Filter by workflow creation timestamp (Unix timestamp)"),
929
- ...qstashCreds
1014
+ inputSchema: z7.object({
1015
+ cursor: z7.string().optional().describe("Cursor for pagination"),
1016
+ workflowRunId: z7.string().optional().describe("Filter by specific workflow run ID"),
1017
+ count: z7.number().optional().describe("Number of workflow runs to return"),
1018
+ state: z7.enum(["RUN_STARTED", "RUN_SUCCESS", "RUN_FAILED", "RUN_CANCELED"]).optional().describe("Filter by workflow state"),
1019
+ workflowUrl: z7.string().optional().describe("Filter by workflow URL (exact match)"),
1020
+ workflowCreatedAt: z7.number().optional().describe("Filter by workflow creation timestamp (Unix timestamp)"),
1021
+ ...qstashCommon
930
1022
  }),
931
1023
  handler: async (params) => {
932
- const client = await createQStashClientWithToken(params.qstash_creds);
1024
+ const {
1025
+ region: _region,
1026
+ local_mode_port: _local_mode_port,
1027
+ qstash_creds: _qstash_creds,
1028
+ ...query
1029
+ } = params;
1030
+ const client = await createQStashClientWithToken(params);
933
1031
  const response = await client.get("v2/workflows/events", {
934
1032
  trimBody: 0,
935
1033
  groupBy: "workflowRunId",
936
- ...params
1034
+ ...query
937
1035
  });
938
1036
  const cleaned = response.runs.map(
939
1037
  (run) => Object.fromEntries(Object.entries(run).filter(([key, _value]) => key !== "steps"))
@@ -952,16 +1050,20 @@ var workflowTools = {
952
1050
  description: `Get details of a single workflow run by workflow run ID. There
953
1051
  could be multiple workflow runs with the same workflow run ID, so you can
954
1052
  use the workflowCreatedAt to get the details of the specific workflow run.`,
955
- inputSchema: z6.object({
956
- workflowRunId: z6.string().describe("The workflow run ID to get details for"),
957
- workflowCreatedAt: z6.number().optional().describe("The workflow creation timestamp (Unix timestamp)"),
958
- ...qstashCreds
1053
+ inputSchema: z7.object({
1054
+ workflowRunId: z7.string().describe("The workflow run ID to get details for"),
1055
+ workflowCreatedAt: z7.number().optional().describe("The workflow creation timestamp (Unix timestamp)"),
1056
+ ...qstashCommon
959
1057
  }),
960
1058
  handler: async (params) => {
961
- const client = await createQStashClientWithToken(params.qstash_creds);
962
- const response = await client.get("v2/workflows/logs", {
963
- ...params
964
- });
1059
+ const {
1060
+ region: _region,
1061
+ local_mode_port: _local_mode_port,
1062
+ qstash_creds: _qstash_creds,
1063
+ ...query
1064
+ } = params;
1065
+ const client = await createQStashClientWithToken(params);
1066
+ const response = await client.get("v2/workflows/logs", query);
965
1067
  if (response.runs.length === 0) {
966
1068
  return "No workflow run found";
967
1069
  }
@@ -974,22 +1076,28 @@ var workflowTools = {
974
1076
  }),
975
1077
  workflow_dlq_list: tool({
976
1078
  description: `List failed workflow runs in the Dead Letter Queue (DLQ) with optional filtering.`,
977
- inputSchema: z6.object({
978
- cursor: z6.string().optional().describe("Cursor for pagination"),
979
- workflowRunId: z6.string().optional().describe("Filter by workflow run ID"),
980
- workflowUrl: z6.string().optional().describe("Filter by workflow URL"),
981
- fromDate: z6.number().optional().describe("Filter from date (Unix timestamp in milliseconds)"),
982
- toDate: z6.number().optional().describe("Filter to date (Unix timestamp in milliseconds)"),
983
- responseStatus: z6.number().optional().describe("Filter by HTTP response status code"),
984
- callerIP: z6.string().optional().describe("Filter by IP address of the caller"),
985
- failureCallbackState: z6.string().optional().describe("Filter by failure callback state"),
986
- count: z6.number().optional().describe("Number of DLQ messages to return"),
987
- ...qstashCreds
1079
+ inputSchema: z7.object({
1080
+ cursor: z7.string().optional().describe("Cursor for pagination"),
1081
+ workflowRunId: z7.string().optional().describe("Filter by workflow run ID"),
1082
+ workflowUrl: z7.string().optional().describe("Filter by workflow URL"),
1083
+ fromDate: z7.number().optional().describe("Filter from date (Unix timestamp in milliseconds)"),
1084
+ toDate: z7.number().optional().describe("Filter to date (Unix timestamp in milliseconds)"),
1085
+ responseStatus: z7.number().optional().describe("Filter by HTTP response status code"),
1086
+ callerIP: z7.string().optional().describe("Filter by IP address of the caller"),
1087
+ failureCallbackState: z7.string().optional().describe("Filter by failure callback state"),
1088
+ count: z7.number().optional().describe("Number of DLQ messages to return"),
1089
+ ...qstashCommon
988
1090
  }),
989
1091
  handler: async (params) => {
990
- const client = await createQStashClientWithToken(params.qstash_creds);
1092
+ const {
1093
+ region: _region,
1094
+ local_mode_port: _local_mode_port,
1095
+ qstash_creds: _qstash_creds,
1096
+ ...query
1097
+ } = params;
1098
+ const client = await createQStashClientWithToken(params);
991
1099
  const response = await client.get("v2/workflows/dlq", {
992
- ...params,
1100
+ ...query,
993
1101
  trimBody: 0
994
1102
  });
995
1103
  const cleaned = response.messages.map(
@@ -1007,27 +1115,28 @@ var workflowTools = {
1007
1115
  }),
1008
1116
  workflow_dlq_get: tool({
1009
1117
  description: `Get details of a single failed workflow run from the DLQ by DLQ ID.`,
1010
- inputSchema: z6.object({
1011
- dlqId: z6.string().describe("The DLQ ID of the failed workflow run to retrieve"),
1012
- ...qstashCreds
1118
+ inputSchema: z7.object({
1119
+ dlqId: z7.string().describe("The DLQ ID of the failed workflow run to retrieve"),
1120
+ ...qstashCommon
1013
1121
  }),
1014
1122
  handler: async (params) => {
1015
- const client = await createQStashClientWithToken(params.qstash_creds);
1123
+ const client = await createQStashClientWithToken(params);
1016
1124
  const message = await client.get(`v2/workflows/dlq/${params.dlqId}`);
1017
1125
  return [`Failed workflow run details for DLQ ID: ${params.dlqId}`, json(message)];
1018
1126
  }
1019
1127
  }),
1020
1128
  workflow_dlq_manage: tool({
1021
1129
  description: `Delete, restart, and resume failed workflow runs in the DLQ using only the DLQ ID.`,
1022
- inputSchema: z6.object({
1023
- dlqId: z6.string().describe("The DLQ ID of the failed workflow run"),
1024
- action: z6.enum(["delete", "restart", "resume"]).describe(
1130
+ inputSchema: z7.object({
1131
+ dlqId: z7.string().describe("The DLQ ID of the failed workflow run"),
1132
+ action: z7.enum(["delete", "restart", "resume"]).describe(
1025
1133
  "The action to perform: delete (remove from DLQ), restart (from beginning), or resume (from the failed step)"
1026
1134
  ),
1027
- ...qstashCreds
1135
+ ...qstashCommon
1028
1136
  }),
1029
- handler: async ({ dlqId, action, qstash_creds }) => {
1030
- const client = await createQStashClientWithToken(qstash_creds);
1137
+ handler: async (params) => {
1138
+ const { dlqId, action } = params;
1139
+ const client = await createQStashClientWithToken(params);
1031
1140
  switch (action) {
1032
1141
  case "delete": {
1033
1142
  await client.delete(`v2/workflows/dlq/delete/${dlqId}`);
@@ -1061,14 +1170,10 @@ var qstashAllTools = {
1061
1170
  };
1062
1171
 
1063
1172
  // src/tools/index.ts
1064
- var json = (json2) => typeof json2 === "string" ? json2 : JSON.stringify(json2, null, 2);
1065
1173
  var tools = {
1066
1174
  ...redisTools,
1067
1175
  ...qstashAllTools
1068
1176
  };
1069
- function tool(tool2) {
1070
- return tool2;
1071
- }
1072
1177
 
1073
1178
  // src/settings.ts
1074
1179
  var MAX_MESSAGE_LENGTH = 3e4;
@@ -1087,7 +1192,7 @@ function handlerResponseToCallResult(response) {
1087
1192
  }
1088
1193
 
1089
1194
  // src/server.ts
1090
- import z7 from "zod";
1195
+ import z8 from "zod";
1091
1196
  function createServerInstance() {
1092
1197
  const server = new McpServer(
1093
1198
  { name: "upstash", version: "0.1.0" },
@@ -1111,7 +1216,7 @@ function createServerInstance() {
1111
1216
  toolName,
1112
1217
  {
1113
1218
  description: tool2.description,
1114
- inputSchema: (tool2.inputSchema ?? z7.object({})).shape
1219
+ inputSchema: (tool2.inputSchema ?? z8.object({})).shape
1115
1220
  },
1116
1221
  // @ts-expect-error - Just ignore the types here
1117
1222
  async (args) => {
@@ -1171,10 +1276,13 @@ var argv = process.argv.slice(2);
1171
1276
  if (argv.length >= 3 && argv[0] === "run") {
1172
1277
  argv = ["--email", argv[1], "--api-key", argv[2], ...argv.slice(3)];
1173
1278
  }
1174
- var program = new Command().option("--transport <stdio|http>", "transport type", "stdio").option("--port <number>", "port for HTTP transport", "3000").option("--email <email>", "Upstash email").option("--api-key <key>", "Upstash API key").option("--debug", "Enable debug mode").allowUnknownOption();
1279
+ var program = new Command().option("--transport <stdio|http>", "transport type", "stdio").option("--port <number>", "port for HTTP transport", "3000").option("--email <email>", "Upstash email").option("--api-key <key>", "Upstash API key").option("--debug", "Enable debug mode").option("--disable-telemetry", "Disable telemetry headers sent to Upstash APIs").allowUnknownOption();
1175
1280
  program.parse(argv, { from: "user" });
1176
1281
  var cliOptions = program.opts();
1177
1282
  var DEBUG = cliOptions.debug ?? false;
1283
+ if (DEBUG) {
1284
+ initDebugLog(new URL("../", import.meta.url).pathname);
1285
+ }
1178
1286
  var allowedTransports = ["stdio", "http"];
1179
1287
  if (!allowedTransports.includes(cliOptions.transport)) {
1180
1288
  console.error(
@@ -1203,6 +1311,7 @@ async function main() {
1203
1311
  }
1204
1312
  config.email = email;
1205
1313
  config.apiKey = apiKey;
1314
+ config.disableTelemetry = cliOptions.disableTelemetry ?? false;
1206
1315
  await testConnection();
1207
1316
  const transportType = TRANSPORT_TYPE;
1208
1317
  if (transportType === "http") {