@upstash/mcp-server 0.2.2-rc.1 → 0.2.3-rc.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/README.md +285 -116
- package/dist/index.js +758 -210
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9,7 +9,22 @@ import { createServer } from "http";
|
|
|
9
9
|
// src/server.ts
|
|
10
10
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
11
11
|
|
|
12
|
+
// src/config.ts
|
|
13
|
+
var config = {
|
|
14
|
+
apiKey: "",
|
|
15
|
+
email: "",
|
|
16
|
+
boxApiKey: "",
|
|
17
|
+
disableTelemetry: false,
|
|
18
|
+
readonly: false
|
|
19
|
+
};
|
|
20
|
+
|
|
12
21
|
// src/log.ts
|
|
22
|
+
import { appendFileSync } from "fs";
|
|
23
|
+
import path from "path";
|
|
24
|
+
var debugLogPath = null;
|
|
25
|
+
function initDebugLog(projectRoot) {
|
|
26
|
+
debugLogPath = path.resolve(projectRoot, "upstash-debug.log");
|
|
27
|
+
}
|
|
13
28
|
function log(...args) {
|
|
14
29
|
const msg = `[DEBUG ${(/* @__PURE__ */ new Date()).toISOString()}] ${args.map((arg) => typeof arg === "string" ? arg : JSON.stringify(arg)).join(" ")}
|
|
15
30
|
`;
|
|
@@ -17,13 +32,28 @@ function log(...args) {
|
|
|
17
32
|
logs.push(msg);
|
|
18
33
|
}
|
|
19
34
|
process.stderr.write(msg);
|
|
35
|
+
if (debugLogPath) {
|
|
36
|
+
try {
|
|
37
|
+
appendFileSync(debugLogPath, msg);
|
|
38
|
+
} catch {
|
|
39
|
+
}
|
|
40
|
+
}
|
|
20
41
|
}
|
|
21
42
|
var logsStore = /* @__PURE__ */ new Map();
|
|
22
43
|
|
|
23
44
|
// src/tools/utils.ts
|
|
24
45
|
import { z } from "zod";
|
|
46
|
+
|
|
47
|
+
// src/tools/helpers.ts
|
|
48
|
+
var json = (value) => typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
49
|
+
function tool(t) {
|
|
50
|
+
return t;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// src/tools/utils.ts
|
|
25
54
|
var utilTools = {
|
|
26
55
|
util_timestamps_to_date: tool({
|
|
56
|
+
readonly: true,
|
|
27
57
|
description: `Use this tool to convert a timestamp to a human-readable date`,
|
|
28
58
|
inputSchema: z.object({
|
|
29
59
|
timestamps: z.array(z.number()).describe("Array of timestamps to convert")
|
|
@@ -33,6 +63,7 @@ var utilTools = {
|
|
|
33
63
|
}
|
|
34
64
|
}),
|
|
35
65
|
util_dates_to_timestamps: tool({
|
|
66
|
+
readonly: true,
|
|
36
67
|
description: `Use this tool to convert an array of ISO 8601 dates to an array of timestamps`,
|
|
37
68
|
inputSchema: z.object({
|
|
38
69
|
dates: z.array(z.string()).describe("Array of dates to convert")
|
|
@@ -46,12 +77,6 @@ var utilTools = {
|
|
|
46
77
|
// src/tools/redis/backup.ts
|
|
47
78
|
import { z as z2 } from "zod";
|
|
48
79
|
|
|
49
|
-
// src/config.ts
|
|
50
|
-
var config = {
|
|
51
|
-
apiKey: "",
|
|
52
|
-
email: ""
|
|
53
|
-
};
|
|
54
|
-
|
|
55
80
|
// src/middlewares.ts
|
|
56
81
|
var formatTimestamps = (obj) => {
|
|
57
82
|
if (!obj || typeof obj !== "object") {
|
|
@@ -91,27 +116,56 @@ var applyMiddlewares = async (req, func) => {
|
|
|
91
116
|
return next();
|
|
92
117
|
};
|
|
93
118
|
|
|
119
|
+
// src/version.ts
|
|
120
|
+
var VERSION = "0.2.3-rc.2";
|
|
121
|
+
|
|
122
|
+
// src/telemetry.ts
|
|
123
|
+
function getRuntime() {
|
|
124
|
+
var _a, _b, _c;
|
|
125
|
+
if (globalThis.Bun !== void 0) {
|
|
126
|
+
return `bun@${globalThis.Bun.version}`;
|
|
127
|
+
}
|
|
128
|
+
if (globalThis.Deno !== void 0) {
|
|
129
|
+
const denoVersion = (_b = (_a = globalThis.Deno) == null ? void 0 : _a.version) == null ? void 0 : _b.deno;
|
|
130
|
+
return denoVersion ? `deno@${denoVersion}` : "deno";
|
|
131
|
+
}
|
|
132
|
+
if (typeof process !== "undefined" && ((_c = process.versions) == null ? void 0 : _c.node)) {
|
|
133
|
+
return `node@${process.versions.node}`;
|
|
134
|
+
}
|
|
135
|
+
return "unknown";
|
|
136
|
+
}
|
|
137
|
+
function getPlatform() {
|
|
138
|
+
if (typeof process !== "undefined" && process.platform) {
|
|
139
|
+
return process.platform;
|
|
140
|
+
}
|
|
141
|
+
return "unknown";
|
|
142
|
+
}
|
|
143
|
+
var telemetry = {
|
|
144
|
+
runtime: getRuntime(),
|
|
145
|
+
platform: getPlatform(),
|
|
146
|
+
sdk: `@upstash/mcp-server@${VERSION}`
|
|
147
|
+
};
|
|
148
|
+
|
|
94
149
|
// src/http.ts
|
|
95
|
-
import fetch from "node-fetch";
|
|
96
150
|
var HttpClient = class {
|
|
97
151
|
constructor(config2) {
|
|
98
152
|
this.baseUrl = config2.baseUrl.replace(/\/$/, "");
|
|
99
153
|
this.qstashToken = config2.qstashToken;
|
|
100
154
|
}
|
|
101
|
-
async get(
|
|
102
|
-
return this.requestWithMiddleware({ method: "GET", path, query });
|
|
155
|
+
async get(path2, query) {
|
|
156
|
+
return this.requestWithMiddleware({ method: "GET", path: path2, query });
|
|
103
157
|
}
|
|
104
|
-
async post(
|
|
105
|
-
return this.requestWithMiddleware({ method: "POST", path, body, headers });
|
|
158
|
+
async post(path2, body, headers) {
|
|
159
|
+
return this.requestWithMiddleware({ method: "POST", path: path2, body, headers });
|
|
106
160
|
}
|
|
107
|
-
async put(
|
|
108
|
-
return this.requestWithMiddleware({ method: "PUT", path, body, headers });
|
|
161
|
+
async put(path2, body, headers) {
|
|
162
|
+
return this.requestWithMiddleware({ method: "PUT", path: path2, body, headers });
|
|
109
163
|
}
|
|
110
|
-
async patch(
|
|
111
|
-
return this.requestWithMiddleware({ method: "PATCH", path, body });
|
|
164
|
+
async patch(path2, body) {
|
|
165
|
+
return this.requestWithMiddleware({ method: "PATCH", path: path2, body });
|
|
112
166
|
}
|
|
113
|
-
async delete(
|
|
114
|
-
return this.requestWithMiddleware({ method: "DELETE", path, body });
|
|
167
|
+
async delete(path2, body) {
|
|
168
|
+
return this.requestWithMiddleware({ method: "DELETE", path: path2, body });
|
|
115
169
|
}
|
|
116
170
|
async requestWithMiddleware(req) {
|
|
117
171
|
const res = await applyMiddlewares(req, async (req2) => {
|
|
@@ -145,11 +199,17 @@ var HttpClient = class {
|
|
|
145
199
|
const token = [config.email, config.apiKey].join(":");
|
|
146
200
|
authHeader = `Basic ${Buffer.from(token).toString("base64")}`;
|
|
147
201
|
}
|
|
202
|
+
const telemetryHeaders = config.disableTelemetry ? {} : {
|
|
203
|
+
"Upstash-Telemetry-Runtime": telemetry.runtime,
|
|
204
|
+
"Upstash-Telemetry-Platform": telemetry.platform,
|
|
205
|
+
"Upstash-Telemetry-Sdk": telemetry.sdk
|
|
206
|
+
};
|
|
148
207
|
const init = {
|
|
149
208
|
method: req.method,
|
|
150
209
|
headers: {
|
|
151
210
|
"Content-Type": "application/json",
|
|
152
211
|
Authorization: authHeader,
|
|
212
|
+
...telemetryHeaders,
|
|
153
213
|
...req.headers
|
|
154
214
|
}
|
|
155
215
|
};
|
|
@@ -240,6 +300,7 @@ var redisBackupTools = {
|
|
|
240
300
|
}
|
|
241
301
|
}),
|
|
242
302
|
redis_database_list_backups: tool({
|
|
303
|
+
readonly: true,
|
|
243
304
|
// TODO: Add explanation for fields
|
|
244
305
|
// TODO: Is this in bytes?
|
|
245
306
|
description: `List all backups of a specific Upstash redis database.`,
|
|
@@ -269,9 +330,9 @@ var redisBackupTools = {
|
|
|
269
330
|
|
|
270
331
|
// src/tools/redis/command.ts
|
|
271
332
|
import { z as z3 } from "zod";
|
|
272
|
-
import fetch2 from "node-fetch";
|
|
273
333
|
var redisCommandTools = {
|
|
274
334
|
redis_database_run_redis_commands: tool({
|
|
335
|
+
readonly: true,
|
|
275
336
|
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.
|
|
276
337
|
NOTE: For discovery, use SCAN over KEYS. Use TYPE to get the type of a key.
|
|
277
338
|
NOTE: SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
|
|
@@ -300,7 +361,7 @@ NOTE: Multiple commands will be executed as a pipeline for better performance.`,
|
|
|
300
361
|
log("Fetching database details for database_id:", database_id);
|
|
301
362
|
const db = await http.get(["v2/redis/database", database_id]);
|
|
302
363
|
restUrl = "https://" + db.endpoint;
|
|
303
|
-
restToken = db.rest_token;
|
|
364
|
+
restToken = config.readonly ? db.read_only_rest_token : db.rest_token;
|
|
304
365
|
}
|
|
305
366
|
if (!restUrl || !restToken) {
|
|
306
367
|
throw new Error("Could not determine REST URL and token for the database");
|
|
@@ -308,7 +369,7 @@ NOTE: Multiple commands will be executed as a pipeline for better performance.`,
|
|
|
308
369
|
const isSingleCommand = commands.length === 1;
|
|
309
370
|
const url = isSingleCommand ? restUrl : restUrl + "/pipeline";
|
|
310
371
|
const body = isSingleCommand ? JSON.stringify(commands[0]) : JSON.stringify(commands);
|
|
311
|
-
const req = await
|
|
372
|
+
const req = await fetch(url, {
|
|
312
373
|
method: "POST",
|
|
313
374
|
body,
|
|
314
375
|
headers: {
|
|
@@ -393,6 +454,7 @@ NOTE: Ask user for the region and name of the database.${GENERIC_DATABASE_NOTES}
|
|
|
393
454
|
}
|
|
394
455
|
}),
|
|
395
456
|
redis_database_list_databases: tool({
|
|
457
|
+
readonly: true,
|
|
396
458
|
description: `List all Upstash redis databases. Only their names and ids.${GENERIC_DATABASE_NOTES}`,
|
|
397
459
|
handler: async () => {
|
|
398
460
|
const dbs = await http.get("v2/redis/databases");
|
|
@@ -419,6 +481,7 @@ NOTE: Ask user for the region and name of the database.${GENERIC_DATABASE_NOTES}
|
|
|
419
481
|
}
|
|
420
482
|
}),
|
|
421
483
|
redis_database_get_details: tool({
|
|
484
|
+
readonly: true,
|
|
422
485
|
description: `Get further details of a specific Upstash redis database. Includes all details of the database including usage statistics.
|
|
423
486
|
db_disk_threshold: Total disk usage limit.
|
|
424
487
|
db_memory_threshold: Maximum memory usage.
|
|
@@ -461,6 +524,7 @@ ${GENERIC_DATABASE_NOTES}
|
|
|
461
524
|
}
|
|
462
525
|
}),
|
|
463
526
|
redis_database_get_statistics: tool({
|
|
527
|
+
readonly: true,
|
|
464
528
|
description: `Get comprehensive usage statistics of an Upstash redis database. Returns both:
|
|
465
529
|
1. PRECISE 5-day usage: Exact command count and bandwidth usage over the last 5 days
|
|
466
530
|
2. SAMPLED period stats: Sampled statistics over a specified period (1h, 3h, 12h, 1d, 3d, 7d) for performance monitoring
|
|
@@ -525,6 +589,7 @@ var parseUsageData = (data) => {
|
|
|
525
589
|
if (!Array.isArray(data)) return "INVALID DATA";
|
|
526
590
|
if (data.length === 0 || data.length === 1) return "NO DATA";
|
|
527
591
|
const filteredData = data.filter((d) => d.x && d.y);
|
|
592
|
+
if (filteredData.length === 0) return "NO DATA";
|
|
528
593
|
return {
|
|
529
594
|
start: filteredData[0].x,
|
|
530
595
|
// last one can be null, so use the second last
|
|
@@ -543,57 +608,81 @@ var redisTools = {
|
|
|
543
608
|
};
|
|
544
609
|
|
|
545
610
|
// src/tools/qstash/qstash.ts
|
|
546
|
-
import { z as
|
|
611
|
+
import { z as z6 } from "zod";
|
|
547
612
|
|
|
548
613
|
// src/tools/qstash/utils.ts
|
|
549
|
-
var
|
|
550
|
-
var
|
|
551
|
-
|
|
614
|
+
var LOCAL_QSTASH_TOKEN = "eyJVc2VySUQiOiJkZWZhdWx0VXNlciIsIlBhc3N3b3JkIjoiZGVmYXVsdFBhc3N3b3JkIn0=";
|
|
615
|
+
var REGION_URLS = {
|
|
616
|
+
eu: "https://qstash-eu-central-1.upstash.io",
|
|
617
|
+
us: "https://qstash-us-east-1.upstash.io"
|
|
618
|
+
};
|
|
619
|
+
var REGION_API_NAMES = {
|
|
620
|
+
eu: "eu-central-1",
|
|
621
|
+
us: "us-east-1"
|
|
622
|
+
};
|
|
623
|
+
var cachedUsers = null;
|
|
624
|
+
var cacheExpiry = 0;
|
|
625
|
+
async function getQStashCredentials(region) {
|
|
552
626
|
const now = Date.now();
|
|
553
|
-
if (
|
|
554
|
-
|
|
627
|
+
if (!cachedUsers || now >= cacheExpiry) {
|
|
628
|
+
try {
|
|
629
|
+
cachedUsers = await http.get("v2/qstash/users");
|
|
630
|
+
cacheExpiry = now + 60 * 1e3;
|
|
631
|
+
} catch (error) {
|
|
632
|
+
throw new Error(
|
|
633
|
+
`Failed to get QStash credentials: ${error instanceof Error ? error.message : String(error)}`
|
|
634
|
+
);
|
|
635
|
+
}
|
|
555
636
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
637
|
+
const apiRegion = REGION_API_NAMES[region] ?? REGION_API_NAMES["eu"];
|
|
638
|
+
const match = cachedUsers.find((u) => u.region === apiRegion);
|
|
639
|
+
if (!match) {
|
|
640
|
+
throw new Error(`No QStash user found for region '${region}' (${apiRegion})`);
|
|
641
|
+
}
|
|
642
|
+
return { token: match.token };
|
|
643
|
+
}
|
|
644
|
+
async function createQStashClientWithToken(options) {
|
|
645
|
+
if (config.readonly) {
|
|
562
646
|
throw new Error(
|
|
563
|
-
|
|
647
|
+
"QStash is not available in readonly mode yet. This feature will be implemented in the near future."
|
|
564
648
|
);
|
|
565
649
|
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
creds.token = await getQStashToken();
|
|
650
|
+
const { qstash_creds, region, local_mode_port } = options;
|
|
651
|
+
if (qstash_creds) {
|
|
652
|
+
return createQStashClient({ url: qstash_creds.url, token: qstash_creds.token });
|
|
570
653
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
654
|
+
if (region === "local") {
|
|
655
|
+
return createQStashClient({
|
|
656
|
+
url: `http://localhost:${local_mode_port}`,
|
|
657
|
+
token: LOCAL_QSTASH_TOKEN
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
const effectiveRegion = region && REGION_URLS[region] ? region : "eu";
|
|
661
|
+
const fetched = await getQStashCredentials(effectiveRegion);
|
|
662
|
+
return createQStashClient({ url: REGION_URLS[effectiveRegion], token: fetched.token });
|
|
575
663
|
}
|
|
576
664
|
|
|
577
|
-
// src/tools/qstash/
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
// ),
|
|
665
|
+
// src/tools/qstash/common.ts
|
|
666
|
+
import { z as z5 } from "zod";
|
|
667
|
+
var qstashCommon = {
|
|
668
|
+
region: z5.enum(["eu", "us", "local"]).default("eu").describe("QStash region to use. To use local mode, pick `local`"),
|
|
669
|
+
local_mode_port: z5.number().default(8080).describe(
|
|
670
|
+
"Only provide when using local mode and if default does not work, the port is usually in the .env file"
|
|
671
|
+
),
|
|
672
|
+
qstash_creds: z5.object({
|
|
673
|
+
url: z5.string(),
|
|
674
|
+
token: z5.string()
|
|
675
|
+
}).optional().describe("Custom qstash credentials, overrides `region`")
|
|
589
676
|
};
|
|
677
|
+
|
|
678
|
+
// src/tools/qstash/qstash.ts
|
|
590
679
|
var qstashTools = {
|
|
591
680
|
qstash_get_user_token: tool({
|
|
592
681
|
description: `Get the QSTASH_TOKEN and QSTASH_URL of the current user. This
|
|
593
682
|
is not needed for the mcp tools since the token is automatically fetched from
|
|
594
683
|
the Upstash API for them.`,
|
|
595
684
|
handler: async () => {
|
|
596
|
-
const user = await http.get("qstash/user");
|
|
685
|
+
const user = await http.get("v2/qstash/user");
|
|
597
686
|
return [json(user)];
|
|
598
687
|
}
|
|
599
688
|
}),
|
|
@@ -601,42 +690,42 @@ var qstashTools = {
|
|
|
601
690
|
description: `Publish a message to a destination URL using QStash. This
|
|
602
691
|
sends an HTTP request to the specified destination via QStash's message
|
|
603
692
|
queue. This can also be used to trigger a upstash workflow run.`,
|
|
604
|
-
inputSchema:
|
|
605
|
-
destination:
|
|
606
|
-
body:
|
|
607
|
-
method:
|
|
608
|
-
delay:
|
|
609
|
-
retries:
|
|
610
|
-
callback:
|
|
611
|
-
failureCallback:
|
|
612
|
-
timeout:
|
|
613
|
-
queueName:
|
|
693
|
+
inputSchema: z6.object({
|
|
694
|
+
destination: z6.string().describe("The destination URL to send the message to (e.g., 'https://example.com')"),
|
|
695
|
+
body: z6.string().optional().describe("Request body (JSON string or plain text)"),
|
|
696
|
+
method: z6.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional().describe("HTTP method (optional, defaults to POST)").default("POST"),
|
|
697
|
+
delay: z6.string().optional().describe("Delay before message delivery (e.g., '10s', '5m', '1h')"),
|
|
698
|
+
retries: z6.number().optional().describe("Number of retries on failure, default is 3"),
|
|
699
|
+
callback: z6.string().optional().describe("Callback URL that will be called when the message is successfully delivered"),
|
|
700
|
+
failureCallback: z6.string().optional().describe("Callback URL that will be called when the message is failed to deliver"),
|
|
701
|
+
timeout: z6.string().optional().describe("Request timeout (e.g., '30s', '1h')"),
|
|
702
|
+
queueName: z6.string().optional().describe(
|
|
614
703
|
"Queue name to use, you have to first create the queue in upstash. Prefer the flow control key instead"
|
|
615
704
|
),
|
|
616
|
-
flow_control:
|
|
617
|
-
key:
|
|
618
|
-
parallelism:
|
|
619
|
-
rate:
|
|
620
|
-
period:
|
|
705
|
+
flow_control: z6.object({
|
|
706
|
+
key: z6.string().describe("Unique identifier for grouping messages under same flow control rules"),
|
|
707
|
+
parallelism: z6.number().optional().describe("Max concurrent active calls (default: unlimited)"),
|
|
708
|
+
rate: z6.number().optional().describe("Max calls per period (default: unlimited)"),
|
|
709
|
+
period: z6.string().optional().describe("Time window for rate limit (e.g., '1s', '1m', '1h', default: '1s')")
|
|
621
710
|
}).optional().describe("Flow control for rate limiting and parallelism management"),
|
|
622
|
-
extraHeaders:
|
|
623
|
-
...
|
|
711
|
+
extraHeaders: z6.record(z6.string()).optional().describe("Extra headers to add to the request"),
|
|
712
|
+
...qstashCommon
|
|
624
713
|
}),
|
|
625
|
-
handler: async ({
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
const client = await createQStashClientWithToken(
|
|
714
|
+
handler: async (params) => {
|
|
715
|
+
const {
|
|
716
|
+
destination,
|
|
717
|
+
body,
|
|
718
|
+
method,
|
|
719
|
+
extraHeaders,
|
|
720
|
+
delay,
|
|
721
|
+
retries,
|
|
722
|
+
callback,
|
|
723
|
+
failureCallback,
|
|
724
|
+
timeout,
|
|
725
|
+
queueName,
|
|
726
|
+
flow_control
|
|
727
|
+
} = params;
|
|
728
|
+
const client = await createQStashClientWithToken(params);
|
|
640
729
|
const requestHeaders = {};
|
|
641
730
|
if (method) {
|
|
642
731
|
requestHeaders["Upstash-Method"] = method;
|
|
@@ -687,10 +776,10 @@ var qstashTools = {
|
|
|
687
776
|
}),
|
|
688
777
|
qstash_logs_list: tool({
|
|
689
778
|
description: `List QStash logs with optional filtering. Returns a paginated list of message logs without their bodies.`,
|
|
690
|
-
inputSchema:
|
|
691
|
-
cursor:
|
|
692
|
-
messageId:
|
|
693
|
-
state:
|
|
779
|
+
inputSchema: z6.object({
|
|
780
|
+
cursor: z6.string().optional().describe("Cursor for pagination"),
|
|
781
|
+
messageId: z6.string().optional().describe("Filter logs by message ID"),
|
|
782
|
+
state: z6.enum([
|
|
694
783
|
"CREATED",
|
|
695
784
|
"ACTIVE",
|
|
696
785
|
"RETRY",
|
|
@@ -701,21 +790,27 @@ var qstashTools = {
|
|
|
701
790
|
"CANCEL_REQUESTED",
|
|
702
791
|
"CANCELLED"
|
|
703
792
|
]).optional().describe("Filter logs by state"),
|
|
704
|
-
url:
|
|
705
|
-
topicName:
|
|
706
|
-
scheduleId:
|
|
707
|
-
queueName:
|
|
708
|
-
fromDate:
|
|
709
|
-
toDate:
|
|
710
|
-
count:
|
|
711
|
-
...
|
|
793
|
+
url: z6.string().optional().describe("Filter logs by URL"),
|
|
794
|
+
topicName: z6.string().optional().describe("Filter logs by topic name"),
|
|
795
|
+
scheduleId: z6.string().optional().describe("Filter logs by schedule ID"),
|
|
796
|
+
queueName: z6.string().optional().describe("Filter logs by queue name"),
|
|
797
|
+
fromDate: z6.number().optional().describe("Filter logs from date (Unix timestamp in milliseconds)"),
|
|
798
|
+
toDate: z6.number().optional().describe("Filter logs to date (Unix timestamp in milliseconds)"),
|
|
799
|
+
count: z6.number().max(1e3).optional().describe("Number of logs to return (max 1000)"),
|
|
800
|
+
...qstashCommon
|
|
712
801
|
}),
|
|
713
802
|
handler: async (params) => {
|
|
714
|
-
const
|
|
803
|
+
const {
|
|
804
|
+
region: _region,
|
|
805
|
+
local_mode_port: _local_mode_port,
|
|
806
|
+
qstash_creds: _qstash_creds,
|
|
807
|
+
...query
|
|
808
|
+
} = params;
|
|
809
|
+
const client = await createQStashClientWithToken(params);
|
|
715
810
|
const response = await client.get("v2/logs", {
|
|
716
811
|
trimBody: 0,
|
|
717
812
|
groupBy: "messageId",
|
|
718
|
-
...
|
|
813
|
+
...query
|
|
719
814
|
});
|
|
720
815
|
const firstMessageFields = Object.fromEntries(
|
|
721
816
|
Object.entries(response.messages[0] ?? {}).filter(
|
|
@@ -738,12 +833,13 @@ var qstashTools = {
|
|
|
738
833
|
}),
|
|
739
834
|
qstash_logs_get: tool({
|
|
740
835
|
description: `Get details of a single QStash log item by message ID without trimming the body.`,
|
|
741
|
-
inputSchema:
|
|
742
|
-
messageId:
|
|
743
|
-
...
|
|
836
|
+
inputSchema: z6.object({
|
|
837
|
+
messageId: z6.string().describe("The message ID to get details for"),
|
|
838
|
+
...qstashCommon
|
|
744
839
|
}),
|
|
745
|
-
handler: async (
|
|
746
|
-
const
|
|
840
|
+
handler: async (params) => {
|
|
841
|
+
const { messageId } = params;
|
|
842
|
+
const client = await createQStashClientWithToken(params);
|
|
747
843
|
const response = await client.get("v2/logs", { messageId });
|
|
748
844
|
if (response.messages.length === 0) {
|
|
749
845
|
return "No log entry found for the specified message ID";
|
|
@@ -754,25 +850,31 @@ var qstashTools = {
|
|
|
754
850
|
}),
|
|
755
851
|
qstash_dlq_list: tool({
|
|
756
852
|
description: `List messages in the QStash Dead Letter Queue (DLQ) with optional filtering.`,
|
|
757
|
-
inputSchema:
|
|
758
|
-
cursor:
|
|
759
|
-
messageId:
|
|
760
|
-
url:
|
|
761
|
-
topicName:
|
|
762
|
-
scheduleId:
|
|
763
|
-
queueName:
|
|
764
|
-
fromDate:
|
|
765
|
-
toDate:
|
|
766
|
-
responseStatus:
|
|
767
|
-
callerIp:
|
|
768
|
-
count:
|
|
769
|
-
...
|
|
853
|
+
inputSchema: z6.object({
|
|
854
|
+
cursor: z6.string().optional().describe("Cursor for pagination"),
|
|
855
|
+
messageId: z6.string().optional().describe("Filter DLQ messages by message ID"),
|
|
856
|
+
url: z6.string().optional().describe("Filter DLQ messages by URL"),
|
|
857
|
+
topicName: z6.string().optional().describe("Filter DLQ messages by topic name"),
|
|
858
|
+
scheduleId: z6.string().optional().describe("Filter DLQ messages by schedule ID"),
|
|
859
|
+
queueName: z6.string().optional().describe("Filter DLQ messages by queue name"),
|
|
860
|
+
fromDate: z6.number().optional().describe("Filter from date (Unix timestamp in milliseconds)"),
|
|
861
|
+
toDate: z6.number().optional().describe("Filter to date (Unix timestamp in milliseconds)"),
|
|
862
|
+
responseStatus: z6.number().optional().describe("Filter by HTTP response status code"),
|
|
863
|
+
callerIp: z6.string().optional().describe("Filter by IP address of the publisher"),
|
|
864
|
+
count: z6.number().max(100).optional().describe("Number of messages to return (max 100)"),
|
|
865
|
+
...qstashCommon
|
|
770
866
|
}),
|
|
771
867
|
handler: async (params) => {
|
|
772
|
-
const
|
|
868
|
+
const {
|
|
869
|
+
region: _region,
|
|
870
|
+
local_mode_port: _local_mode_port,
|
|
871
|
+
qstash_creds: _qstash_creds,
|
|
872
|
+
...query
|
|
873
|
+
} = params;
|
|
874
|
+
const client = await createQStashClientWithToken(params);
|
|
773
875
|
const response = await client.get("v2/dlq", {
|
|
774
876
|
trimBody: 0,
|
|
775
|
-
...
|
|
877
|
+
...query
|
|
776
878
|
});
|
|
777
879
|
return [
|
|
778
880
|
`Found ${response.messages.length} DLQ messages`,
|
|
@@ -783,59 +885,63 @@ var qstashTools = {
|
|
|
783
885
|
}),
|
|
784
886
|
qstash_dlq_get: tool({
|
|
785
887
|
description: `Get details of a single DLQ message by DLQ ID.`,
|
|
786
|
-
inputSchema:
|
|
787
|
-
dlqId:
|
|
788
|
-
...
|
|
888
|
+
inputSchema: z6.object({
|
|
889
|
+
dlqId: z6.string().describe("The DLQ ID of the message to retrieve"),
|
|
890
|
+
...qstashCommon
|
|
789
891
|
}),
|
|
790
|
-
handler: async (
|
|
791
|
-
const
|
|
892
|
+
handler: async (params) => {
|
|
893
|
+
const { dlqId } = params;
|
|
894
|
+
const client = await createQStashClientWithToken(params);
|
|
792
895
|
const message = await client.get(`v2/dlq/${dlqId}`);
|
|
793
896
|
return [`DLQ message details for ID: ${dlqId}`, json(message)];
|
|
794
897
|
}
|
|
795
898
|
}),
|
|
796
899
|
qstash_schedules_list: tool({
|
|
797
900
|
description: `List all QStash schedules.`,
|
|
798
|
-
|
|
799
|
-
|
|
901
|
+
inputSchema: z6.object({
|
|
902
|
+
...qstashCommon
|
|
903
|
+
}),
|
|
904
|
+
handler: async (params) => {
|
|
905
|
+
const client = await createQStashClientWithToken(params);
|
|
800
906
|
const schedules = await client.get("v2/schedules");
|
|
801
907
|
return [`Found ${schedules.length} schedules`, json(schedules)];
|
|
802
908
|
}
|
|
803
909
|
}),
|
|
804
910
|
qstash_schedules_manage: tool({
|
|
805
911
|
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:
|
|
807
|
-
operation:
|
|
808
|
-
scheduleId:
|
|
809
|
-
destination:
|
|
810
|
-
cron:
|
|
811
|
-
method:
|
|
812
|
-
headers:
|
|
813
|
-
body:
|
|
814
|
-
delay:
|
|
815
|
-
retries:
|
|
816
|
-
callback:
|
|
817
|
-
failureCallback:
|
|
818
|
-
timeout:
|
|
819
|
-
queueName:
|
|
820
|
-
...
|
|
912
|
+
inputSchema: z6.object({
|
|
913
|
+
operation: z6.enum(["create", "update", "pause", "resume", "delete"]).describe("The operation to perform"),
|
|
914
|
+
scheduleId: z6.string().optional().describe("Schedule ID (required for update, pause, resume, delete operations)"),
|
|
915
|
+
destination: z6.string().optional().describe("Destination URL or topic name (required for create/update)"),
|
|
916
|
+
cron: z6.string().optional().describe("Cron expression (required for create/update)"),
|
|
917
|
+
method: z6.enum(["GET", "POST", "PUT", "DELETE", "PATCH"]).optional().describe("HTTP method (optional, defaults to POST)"),
|
|
918
|
+
headers: z6.record(z6.string()).optional().describe("Request headers as key-value pairs"),
|
|
919
|
+
body: z6.string().optional().describe("Request body"),
|
|
920
|
+
delay: z6.string().optional().describe("Delay before message delivery (e.g., '10s', '5m', '1h')"),
|
|
921
|
+
retries: z6.number().optional().describe("Number of retries on failure"),
|
|
922
|
+
callback: z6.string().optional().describe("Callback URL for successful delivery"),
|
|
923
|
+
failureCallback: z6.string().optional().describe("Callback URL for failed delivery"),
|
|
924
|
+
timeout: z6.string().optional().describe("Request timeout (e.g., '30s')"),
|
|
925
|
+
queueName: z6.string().optional().describe("Queue name to use"),
|
|
926
|
+
...qstashCommon
|
|
821
927
|
}),
|
|
822
|
-
handler: async ({
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
const client = await createQStashClientWithToken(
|
|
928
|
+
handler: async (params) => {
|
|
929
|
+
const {
|
|
930
|
+
operation,
|
|
931
|
+
scheduleId,
|
|
932
|
+
destination,
|
|
933
|
+
cron,
|
|
934
|
+
method = "POST",
|
|
935
|
+
headers,
|
|
936
|
+
body,
|
|
937
|
+
delay,
|
|
938
|
+
retries,
|
|
939
|
+
callback,
|
|
940
|
+
failureCallback,
|
|
941
|
+
timeout,
|
|
942
|
+
queueName
|
|
943
|
+
} = params;
|
|
944
|
+
const client = await createQStashClientWithToken(params);
|
|
839
945
|
switch (operation) {
|
|
840
946
|
case "create":
|
|
841
947
|
case "update": {
|
|
@@ -915,25 +1021,31 @@ var qstashTools = {
|
|
|
915
1021
|
};
|
|
916
1022
|
|
|
917
1023
|
// src/tools/qstash/workflow.ts
|
|
918
|
-
import { z as
|
|
1024
|
+
import { z as z7 } from "zod";
|
|
919
1025
|
var workflowTools = {
|
|
920
1026
|
workflow_logs_list: tool({
|
|
921
1027
|
description: `List Upstash Workflow logs with optional filtering. Returns grouped workflow runs with their execution details.`,
|
|
922
|
-
inputSchema:
|
|
923
|
-
cursor:
|
|
924
|
-
workflowRunId:
|
|
925
|
-
count:
|
|
926
|
-
state:
|
|
927
|
-
workflowUrl:
|
|
928
|
-
workflowCreatedAt:
|
|
929
|
-
...
|
|
1028
|
+
inputSchema: z7.object({
|
|
1029
|
+
cursor: z7.string().optional().describe("Cursor for pagination"),
|
|
1030
|
+
workflowRunId: z7.string().optional().describe("Filter by specific workflow run ID"),
|
|
1031
|
+
count: z7.number().optional().describe("Number of workflow runs to return"),
|
|
1032
|
+
state: z7.enum(["RUN_STARTED", "RUN_SUCCESS", "RUN_FAILED", "RUN_CANCELED"]).optional().describe("Filter by workflow state"),
|
|
1033
|
+
workflowUrl: z7.string().optional().describe("Filter by workflow URL (exact match)"),
|
|
1034
|
+
workflowCreatedAt: z7.number().optional().describe("Filter by workflow creation timestamp (Unix timestamp)"),
|
|
1035
|
+
...qstashCommon
|
|
930
1036
|
}),
|
|
931
1037
|
handler: async (params) => {
|
|
932
|
-
const
|
|
1038
|
+
const {
|
|
1039
|
+
region: _region,
|
|
1040
|
+
local_mode_port: _local_mode_port,
|
|
1041
|
+
qstash_creds: _qstash_creds,
|
|
1042
|
+
...query
|
|
1043
|
+
} = params;
|
|
1044
|
+
const client = await createQStashClientWithToken(params);
|
|
933
1045
|
const response = await client.get("v2/workflows/events", {
|
|
934
1046
|
trimBody: 0,
|
|
935
1047
|
groupBy: "workflowRunId",
|
|
936
|
-
...
|
|
1048
|
+
...query
|
|
937
1049
|
});
|
|
938
1050
|
const cleaned = response.runs.map(
|
|
939
1051
|
(run) => Object.fromEntries(Object.entries(run).filter(([key, _value]) => key !== "steps"))
|
|
@@ -952,16 +1064,20 @@ var workflowTools = {
|
|
|
952
1064
|
description: `Get details of a single workflow run by workflow run ID. There
|
|
953
1065
|
could be multiple workflow runs with the same workflow run ID, so you can
|
|
954
1066
|
use the workflowCreatedAt to get the details of the specific workflow run.`,
|
|
955
|
-
inputSchema:
|
|
956
|
-
workflowRunId:
|
|
957
|
-
workflowCreatedAt:
|
|
958
|
-
...
|
|
1067
|
+
inputSchema: z7.object({
|
|
1068
|
+
workflowRunId: z7.string().describe("The workflow run ID to get details for"),
|
|
1069
|
+
workflowCreatedAt: z7.number().optional().describe("The workflow creation timestamp (Unix timestamp)"),
|
|
1070
|
+
...qstashCommon
|
|
959
1071
|
}),
|
|
960
1072
|
handler: async (params) => {
|
|
961
|
-
const
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1073
|
+
const {
|
|
1074
|
+
region: _region,
|
|
1075
|
+
local_mode_port: _local_mode_port,
|
|
1076
|
+
qstash_creds: _qstash_creds,
|
|
1077
|
+
...query
|
|
1078
|
+
} = params;
|
|
1079
|
+
const client = await createQStashClientWithToken(params);
|
|
1080
|
+
const response = await client.get("v2/workflows/logs", query);
|
|
965
1081
|
if (response.runs.length === 0) {
|
|
966
1082
|
return "No workflow run found";
|
|
967
1083
|
}
|
|
@@ -974,22 +1090,28 @@ var workflowTools = {
|
|
|
974
1090
|
}),
|
|
975
1091
|
workflow_dlq_list: tool({
|
|
976
1092
|
description: `List failed workflow runs in the Dead Letter Queue (DLQ) with optional filtering.`,
|
|
977
|
-
inputSchema:
|
|
978
|
-
cursor:
|
|
979
|
-
workflowRunId:
|
|
980
|
-
workflowUrl:
|
|
981
|
-
fromDate:
|
|
982
|
-
toDate:
|
|
983
|
-
responseStatus:
|
|
984
|
-
callerIP:
|
|
985
|
-
failureCallbackState:
|
|
986
|
-
count:
|
|
987
|
-
...
|
|
1093
|
+
inputSchema: z7.object({
|
|
1094
|
+
cursor: z7.string().optional().describe("Cursor for pagination"),
|
|
1095
|
+
workflowRunId: z7.string().optional().describe("Filter by workflow run ID"),
|
|
1096
|
+
workflowUrl: z7.string().optional().describe("Filter by workflow URL"),
|
|
1097
|
+
fromDate: z7.number().optional().describe("Filter from date (Unix timestamp in milliseconds)"),
|
|
1098
|
+
toDate: z7.number().optional().describe("Filter to date (Unix timestamp in milliseconds)"),
|
|
1099
|
+
responseStatus: z7.number().optional().describe("Filter by HTTP response status code"),
|
|
1100
|
+
callerIP: z7.string().optional().describe("Filter by IP address of the caller"),
|
|
1101
|
+
failureCallbackState: z7.string().optional().describe("Filter by failure callback state"),
|
|
1102
|
+
count: z7.number().optional().describe("Number of DLQ messages to return"),
|
|
1103
|
+
...qstashCommon
|
|
988
1104
|
}),
|
|
989
1105
|
handler: async (params) => {
|
|
990
|
-
const
|
|
1106
|
+
const {
|
|
1107
|
+
region: _region,
|
|
1108
|
+
local_mode_port: _local_mode_port,
|
|
1109
|
+
qstash_creds: _qstash_creds,
|
|
1110
|
+
...query
|
|
1111
|
+
} = params;
|
|
1112
|
+
const client = await createQStashClientWithToken(params);
|
|
991
1113
|
const response = await client.get("v2/workflows/dlq", {
|
|
992
|
-
...
|
|
1114
|
+
...query,
|
|
993
1115
|
trimBody: 0
|
|
994
1116
|
});
|
|
995
1117
|
const cleaned = response.messages.map(
|
|
@@ -1007,27 +1129,28 @@ var workflowTools = {
|
|
|
1007
1129
|
}),
|
|
1008
1130
|
workflow_dlq_get: tool({
|
|
1009
1131
|
description: `Get details of a single failed workflow run from the DLQ by DLQ ID.`,
|
|
1010
|
-
inputSchema:
|
|
1011
|
-
dlqId:
|
|
1012
|
-
...
|
|
1132
|
+
inputSchema: z7.object({
|
|
1133
|
+
dlqId: z7.string().describe("The DLQ ID of the failed workflow run to retrieve"),
|
|
1134
|
+
...qstashCommon
|
|
1013
1135
|
}),
|
|
1014
1136
|
handler: async (params) => {
|
|
1015
|
-
const client = await createQStashClientWithToken(params
|
|
1137
|
+
const client = await createQStashClientWithToken(params);
|
|
1016
1138
|
const message = await client.get(`v2/workflows/dlq/${params.dlqId}`);
|
|
1017
1139
|
return [`Failed workflow run details for DLQ ID: ${params.dlqId}`, json(message)];
|
|
1018
1140
|
}
|
|
1019
1141
|
}),
|
|
1020
1142
|
workflow_dlq_manage: tool({
|
|
1021
1143
|
description: `Delete, restart, and resume failed workflow runs in the DLQ using only the DLQ ID.`,
|
|
1022
|
-
inputSchema:
|
|
1023
|
-
dlqId:
|
|
1024
|
-
action:
|
|
1144
|
+
inputSchema: z7.object({
|
|
1145
|
+
dlqId: z7.string().describe("The DLQ ID of the failed workflow run"),
|
|
1146
|
+
action: z7.enum(["delete", "restart", "resume"]).describe(
|
|
1025
1147
|
"The action to perform: delete (remove from DLQ), restart (from beginning), or resume (from the failed step)"
|
|
1026
1148
|
),
|
|
1027
|
-
...
|
|
1149
|
+
...qstashCommon
|
|
1028
1150
|
}),
|
|
1029
|
-
handler: async (
|
|
1030
|
-
const
|
|
1151
|
+
handler: async (params) => {
|
|
1152
|
+
const { dlqId, action } = params;
|
|
1153
|
+
const client = await createQStashClientWithToken(params);
|
|
1031
1154
|
switch (action) {
|
|
1032
1155
|
case "delete": {
|
|
1033
1156
|
await client.delete(`v2/workflows/dlq/delete/${dlqId}`);
|
|
@@ -1060,15 +1183,425 @@ var qstashAllTools = {
|
|
|
1060
1183
|
...workflowTools
|
|
1061
1184
|
};
|
|
1062
1185
|
|
|
1186
|
+
// src/tools/box/manage.ts
|
|
1187
|
+
import { z as z9 } from "zod";
|
|
1188
|
+
|
|
1189
|
+
// src/tools/box/common.ts
|
|
1190
|
+
import { z as z8 } from "zod";
|
|
1191
|
+
var BOX_BASE_URL = "https://us-east-1.box.upstash.com";
|
|
1192
|
+
function buildBoxCommon() {
|
|
1193
|
+
const hasConfigKey = Boolean(config.boxApiKey);
|
|
1194
|
+
return {
|
|
1195
|
+
box_api_key: hasConfigKey ? z8.string().optional().describe(
|
|
1196
|
+
"NOTE: The api key is already pre-configured at server startup; only pass this to override the configured key."
|
|
1197
|
+
) : z8.string().describe(
|
|
1198
|
+
"Box API key (starts with 'box_'). Check the project's .env file for BOX_API_KEY, or ask the user for it."
|
|
1199
|
+
)
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// src/tools/box/utils.ts
|
|
1204
|
+
function getBoxClient(params) {
|
|
1205
|
+
const apiKey = params.box_api_key || config.boxApiKey;
|
|
1206
|
+
if (!apiKey) {
|
|
1207
|
+
throw new Error(
|
|
1208
|
+
"No Box API key available. Pass box_api_key as a tool argument, or configure the server with --box-api-key / UPSTASH_BOX_API_KEY env var."
|
|
1209
|
+
);
|
|
1210
|
+
}
|
|
1211
|
+
return new HttpClient({
|
|
1212
|
+
baseUrl: BOX_BASE_URL,
|
|
1213
|
+
qstashToken: apiKey
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// src/tools/box/manage.ts
|
|
1218
|
+
var boxManageTool = {
|
|
1219
|
+
box_manage: tool({
|
|
1220
|
+
description: `Manage Upstash Box containers. Supports creating, listing, getting, deleting, pausing, resuming, and forking boxes. Boxes are secure cloud containers with built-in AI agent capabilities.`,
|
|
1221
|
+
get inputSchema() {
|
|
1222
|
+
return z9.object({
|
|
1223
|
+
action: z9.enum(["create", "list", "get", "delete", "pause", "resume", "fork"]).describe("The action to perform"),
|
|
1224
|
+
box_id: z9.string().optional().describe("Box ID (required for get, delete, pause, resume, fork)"),
|
|
1225
|
+
// Create-specific fields
|
|
1226
|
+
name: z9.string().optional().describe("Display name for the box"),
|
|
1227
|
+
model: z9.string().optional().describe(
|
|
1228
|
+
"LLM model to use (e.g. 'claude/sonnet_4_6', 'openai/o4-mini'). Required for create"
|
|
1229
|
+
),
|
|
1230
|
+
agent: z9.enum(["claude-code", "codex", "opencode"]).optional().default("claude-code").describe("Agent type (default: claude-code)"),
|
|
1231
|
+
runtime: z9.string().optional().default("node").describe("Runtime environment (e.g. 'node', 'python')"),
|
|
1232
|
+
agent_api_key: z9.string().optional().describe("API key for the AI agent provider. Empty uses managed key"),
|
|
1233
|
+
env_vars: z9.record(z9.string()).optional().describe("Environment variables to set in the box"),
|
|
1234
|
+
clone_repo: z9.string().optional().describe("Git repository URL to clone into the box"),
|
|
1235
|
+
clone_token: z9.string().optional().describe("Token for cloning private repositories"),
|
|
1236
|
+
ephemeral: z9.boolean().optional().describe("If true, box auto-deletes after TTL expires"),
|
|
1237
|
+
ttl: z9.number().optional().describe("Time-to-live in seconds for ephemeral boxes (max 259200 = 3 days)"),
|
|
1238
|
+
// List-specific fields
|
|
1239
|
+
status: z9.enum(["active", "deleted"]).optional().describe("Filter for list action: 'active' (default) or 'deleted'"),
|
|
1240
|
+
...buildBoxCommon()
|
|
1241
|
+
});
|
|
1242
|
+
},
|
|
1243
|
+
handler: async (params) => {
|
|
1244
|
+
const { action, box_id } = params;
|
|
1245
|
+
const client = getBoxClient(params);
|
|
1246
|
+
switch (action) {
|
|
1247
|
+
case "create": {
|
|
1248
|
+
if (!params.model) {
|
|
1249
|
+
throw new Error("model is required for create action");
|
|
1250
|
+
}
|
|
1251
|
+
const body = {
|
|
1252
|
+
model: params.model
|
|
1253
|
+
};
|
|
1254
|
+
if (params.name) body.name = params.name;
|
|
1255
|
+
if (params.agent) body.agent = params.agent;
|
|
1256
|
+
if (params.runtime) body.runtime = params.runtime;
|
|
1257
|
+
if (params.agent_api_key) body.agent_api_key = params.agent_api_key;
|
|
1258
|
+
if (params.env_vars) body.env_vars = params.env_vars;
|
|
1259
|
+
if (params.clone_repo) body.clone_repo = params.clone_repo;
|
|
1260
|
+
if (params.clone_token) body.clone_token = params.clone_token;
|
|
1261
|
+
if (params.ephemeral !== void 0) body.ephemeral = params.ephemeral;
|
|
1262
|
+
if (params.ttl !== void 0) body.ttl = params.ttl;
|
|
1263
|
+
const box = await client.post("v2/box", body);
|
|
1264
|
+
return [
|
|
1265
|
+
`Box created successfully (status: ${box.status})`,
|
|
1266
|
+
`Box ID: ${box.id}`,
|
|
1267
|
+
json(box)
|
|
1268
|
+
];
|
|
1269
|
+
}
|
|
1270
|
+
case "list": {
|
|
1271
|
+
const query = {};
|
|
1272
|
+
if (params.status === "deleted") query.status = "deleted";
|
|
1273
|
+
const boxes = await client.get("v2/box", query);
|
|
1274
|
+
return [`Found ${boxes.length} boxes`, json(boxes)];
|
|
1275
|
+
}
|
|
1276
|
+
case "get": {
|
|
1277
|
+
if (!box_id) throw new Error("box_id is required for get action");
|
|
1278
|
+
const box = await client.get(`v2/box/${box_id}`);
|
|
1279
|
+
return [`Box ${box_id} (status: ${box.status})`, json(box)];
|
|
1280
|
+
}
|
|
1281
|
+
case "delete": {
|
|
1282
|
+
if (!box_id) throw new Error("box_id is required for delete action");
|
|
1283
|
+
await client.delete(`v2/box/${box_id}`);
|
|
1284
|
+
return `Box ${box_id} deleted successfully`;
|
|
1285
|
+
}
|
|
1286
|
+
case "pause": {
|
|
1287
|
+
if (!box_id) throw new Error("box_id is required for pause action");
|
|
1288
|
+
await client.post(`v2/box/${box_id}/pause`);
|
|
1289
|
+
return `Box ${box_id} paused successfully`;
|
|
1290
|
+
}
|
|
1291
|
+
case "resume": {
|
|
1292
|
+
if (!box_id) throw new Error("box_id is required for resume action");
|
|
1293
|
+
await client.post(`v2/box/${box_id}/resume`);
|
|
1294
|
+
return `Box ${box_id} resumed successfully`;
|
|
1295
|
+
}
|
|
1296
|
+
case "fork": {
|
|
1297
|
+
if (!box_id) throw new Error("box_id is required for fork action");
|
|
1298
|
+
const forked = await client.post(`v2/box/${box_id}/fork`);
|
|
1299
|
+
return [`Box forked successfully`, `New Box ID: ${forked.id}`, json(forked)];
|
|
1300
|
+
}
|
|
1301
|
+
default: {
|
|
1302
|
+
throw new Error(`Unknown action: ${action}`);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
})
|
|
1307
|
+
};
|
|
1308
|
+
|
|
1309
|
+
// src/tools/box/exec.ts
|
|
1310
|
+
import { z as z10 } from "zod";
|
|
1311
|
+
var boxExecTool = {
|
|
1312
|
+
box_exec: tool({
|
|
1313
|
+
description: `Execute a shell command inside an Upstash Box container. Use this for file operations, git commands, package installs, or any shell operation inside the box.`,
|
|
1314
|
+
get inputSchema() {
|
|
1315
|
+
return z10.object({
|
|
1316
|
+
box_id: z10.string().describe("The box ID to execute the command in"),
|
|
1317
|
+
command: z10.array(z10.string()).describe("Command and arguments as an array (e.g. ['bash', '-c', 'ls -la'])"),
|
|
1318
|
+
folder: z10.string().optional().describe("Working directory inside the box"),
|
|
1319
|
+
async: z10.boolean().optional().describe("If true, return immediately without waiting for completion"),
|
|
1320
|
+
...buildBoxCommon()
|
|
1321
|
+
});
|
|
1322
|
+
},
|
|
1323
|
+
handler: async (params) => {
|
|
1324
|
+
const { box_id, command, folder, async: isAsync } = params;
|
|
1325
|
+
const client = getBoxClient(params);
|
|
1326
|
+
const body = { command };
|
|
1327
|
+
if (folder) body.folder = folder;
|
|
1328
|
+
if (isAsync) body.async = isAsync;
|
|
1329
|
+
const response = await client.post(`v2/box/${box_id}/exec`, body);
|
|
1330
|
+
if (response.exit_code !== 0) {
|
|
1331
|
+
return [
|
|
1332
|
+
`Command failed with exit code ${response.exit_code}`,
|
|
1333
|
+
response.output ? `stdout: ${response.output}` : "",
|
|
1334
|
+
response.error ? `stderr: ${response.error}` : ""
|
|
1335
|
+
].filter(Boolean);
|
|
1336
|
+
}
|
|
1337
|
+
return [
|
|
1338
|
+
`Command executed successfully (exit code: ${response.exit_code})`,
|
|
1339
|
+
response.output || "(no output)"
|
|
1340
|
+
];
|
|
1341
|
+
}
|
|
1342
|
+
})
|
|
1343
|
+
};
|
|
1344
|
+
|
|
1345
|
+
// src/tools/box/agent-run.ts
|
|
1346
|
+
import { z as z11 } from "zod";
|
|
1347
|
+
var boxAgentRunTool = {
|
|
1348
|
+
box_agent_run: tool({
|
|
1349
|
+
description: `Run an AI agent prompt inside an Upstash Box. The agent has access to shell, filesystem, and git inside the box. It reasons, executes commands, and iterates until the task is complete. This is a synchronous call that may take a while depending on the complexity of the prompt.`,
|
|
1350
|
+
get inputSchema() {
|
|
1351
|
+
return z11.object({
|
|
1352
|
+
box_id: z11.string().describe("The box ID to run the agent in"),
|
|
1353
|
+
prompt: z11.string().describe("The natural-language prompt for the agent to execute"),
|
|
1354
|
+
model: z11.string().optional().describe("Override the box's default LLM model for this run"),
|
|
1355
|
+
folder: z11.string().optional().describe("Working directory inside the box for the agent"),
|
|
1356
|
+
...buildBoxCommon()
|
|
1357
|
+
});
|
|
1358
|
+
},
|
|
1359
|
+
handler: async (params) => {
|
|
1360
|
+
const { box_id, prompt, model, folder } = params;
|
|
1361
|
+
const client = getBoxClient(params);
|
|
1362
|
+
const body = { prompt };
|
|
1363
|
+
if (model) body.model = model;
|
|
1364
|
+
if (folder) body.folder = folder;
|
|
1365
|
+
const response = await client.post(`v2/box/${box_id}/run`, body);
|
|
1366
|
+
const result = [`Agent run completed`];
|
|
1367
|
+
if (response.run_id) {
|
|
1368
|
+
result.push(`Run ID: ${response.run_id}`);
|
|
1369
|
+
}
|
|
1370
|
+
result.push(response.output || "(no output)");
|
|
1371
|
+
if (response.metadata) {
|
|
1372
|
+
result.push(
|
|
1373
|
+
`Tokens: ${response.metadata.input_tokens ?? 0} in / ${response.metadata.output_tokens ?? 0} out` + (response.metadata.cost_usd ? ` ($${response.metadata.cost_usd.toFixed(4)})` : "")
|
|
1374
|
+
);
|
|
1375
|
+
}
|
|
1376
|
+
return result;
|
|
1377
|
+
}
|
|
1378
|
+
})
|
|
1379
|
+
};
|
|
1380
|
+
|
|
1381
|
+
// src/tools/box/logs.ts
|
|
1382
|
+
import { z as z12 } from "zod";
|
|
1383
|
+
var boxLogsTool = {
|
|
1384
|
+
box_logs: tool({
|
|
1385
|
+
description: `Get logs from an Upstash Box container. Useful for debugging what happened inside the box. Returns timestamped log entries from the system, user, and agent sources.`,
|
|
1386
|
+
get inputSchema() {
|
|
1387
|
+
return z12.object({
|
|
1388
|
+
box_id: z12.string().describe("The box ID to get logs for"),
|
|
1389
|
+
offset: z12.number().optional().default(0).describe("Starting position for log entries (default: 0)"),
|
|
1390
|
+
limit: z12.number().max(1e3).optional().default(100).describe("Maximum number of log entries to return (max 1000, default: 100)"),
|
|
1391
|
+
...buildBoxCommon()
|
|
1392
|
+
});
|
|
1393
|
+
},
|
|
1394
|
+
handler: async (params) => {
|
|
1395
|
+
const { box_id, offset, limit } = params;
|
|
1396
|
+
const client = getBoxClient(params);
|
|
1397
|
+
const response = await client.get(`v2/box/${box_id}/logs`, {
|
|
1398
|
+
offset,
|
|
1399
|
+
limit
|
|
1400
|
+
});
|
|
1401
|
+
const logs = response.logs ?? [];
|
|
1402
|
+
if (logs.length === 0) {
|
|
1403
|
+
return "No logs found for this box";
|
|
1404
|
+
}
|
|
1405
|
+
return [`Found ${logs.length} log entries`, json(logs)];
|
|
1406
|
+
}
|
|
1407
|
+
})
|
|
1408
|
+
};
|
|
1409
|
+
|
|
1410
|
+
// src/tools/box/runs.ts
|
|
1411
|
+
import { z as z13 } from "zod";
|
|
1412
|
+
var boxRunsTool = {
|
|
1413
|
+
box_runs: tool({
|
|
1414
|
+
description: `List, get details, or cancel runs (execution history) for an Upstash Box. Useful for debugging past agent runs and shell executions, checking their status, output, token usage, and costs.`,
|
|
1415
|
+
get inputSchema() {
|
|
1416
|
+
return z13.object({
|
|
1417
|
+
action: z13.enum(["list", "get", "cancel"]).describe("The action to perform"),
|
|
1418
|
+
box_id: z13.string().describe("The box ID"),
|
|
1419
|
+
run_id: z13.string().optional().describe("Run ID (required for get and cancel actions)"),
|
|
1420
|
+
...buildBoxCommon()
|
|
1421
|
+
});
|
|
1422
|
+
},
|
|
1423
|
+
handler: async (params) => {
|
|
1424
|
+
const { action, box_id, run_id } = params;
|
|
1425
|
+
const client = getBoxClient(params);
|
|
1426
|
+
switch (action) {
|
|
1427
|
+
case "list": {
|
|
1428
|
+
const response = await client.get(`v2/box/${box_id}/runs`);
|
|
1429
|
+
const runs = response.runs ?? [];
|
|
1430
|
+
return [`Found ${runs.length} runs`, json(runs)];
|
|
1431
|
+
}
|
|
1432
|
+
case "get": {
|
|
1433
|
+
if (!run_id) throw new Error("run_id is required for get action");
|
|
1434
|
+
const run = await client.get(`v2/box/${box_id}/runs/${run_id}`);
|
|
1435
|
+
return [`Run ${run_id} (status: ${run.status})`, json(run)];
|
|
1436
|
+
}
|
|
1437
|
+
case "cancel": {
|
|
1438
|
+
if (!run_id) throw new Error("run_id is required for cancel action");
|
|
1439
|
+
await client.post(`v2/box/${box_id}/runs/${run_id}/cancel`);
|
|
1440
|
+
return `Run ${run_id} cancelled successfully`;
|
|
1441
|
+
}
|
|
1442
|
+
default: {
|
|
1443
|
+
throw new Error(`Unknown action: ${action}`);
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
})
|
|
1448
|
+
};
|
|
1449
|
+
|
|
1450
|
+
// src/tools/box/preview.ts
|
|
1451
|
+
import { z as z14 } from "zod";
|
|
1452
|
+
var boxPreviewTool = {
|
|
1453
|
+
box_preview: tool({
|
|
1454
|
+
description: `Manage preview URLs for web applications running inside an Upstash Box. Create public URLs to access services running on specific ports, list existing previews, or delete them.`,
|
|
1455
|
+
get inputSchema() {
|
|
1456
|
+
return z14.object({
|
|
1457
|
+
action: z14.enum(["create", "list", "delete"]).describe("The action to perform"),
|
|
1458
|
+
box_id: z14.string().describe("The box ID"),
|
|
1459
|
+
port: z14.number().min(1).max(65535).optional().describe("Port number (required for create and delete)"),
|
|
1460
|
+
basic_auth: z14.boolean().optional().describe("Enable basic auth on the preview URL (create only)"),
|
|
1461
|
+
bearer_token: z14.boolean().optional().describe("Enable bearer token auth on the preview URL (create only)"),
|
|
1462
|
+
...buildBoxCommon()
|
|
1463
|
+
});
|
|
1464
|
+
},
|
|
1465
|
+
handler: async (params) => {
|
|
1466
|
+
const { action, box_id, port, basic_auth, bearer_token } = params;
|
|
1467
|
+
const client = getBoxClient(params);
|
|
1468
|
+
switch (action) {
|
|
1469
|
+
case "create": {
|
|
1470
|
+
if (!port) throw new Error("port is required for create action");
|
|
1471
|
+
const body = { port };
|
|
1472
|
+
if (basic_auth) body.basic_auth = basic_auth;
|
|
1473
|
+
if (bearer_token) body.bearer_token = bearer_token;
|
|
1474
|
+
const response = await client.post(
|
|
1475
|
+
`v2/box/${box_id}/preview`,
|
|
1476
|
+
body
|
|
1477
|
+
);
|
|
1478
|
+
const result = [
|
|
1479
|
+
`Preview URL created: ${response.url}`,
|
|
1480
|
+
`Port: ${response.port}`
|
|
1481
|
+
];
|
|
1482
|
+
if (response.username) result.push(`Username: ${response.username}`);
|
|
1483
|
+
if (response.password) result.push(`Password: ${response.password}`);
|
|
1484
|
+
if (response.token) result.push(`Token: ${response.token}`);
|
|
1485
|
+
return result;
|
|
1486
|
+
}
|
|
1487
|
+
case "list": {
|
|
1488
|
+
const response = await client.get(`v2/box/${box_id}/preview`);
|
|
1489
|
+
const previews = response.previews ?? [];
|
|
1490
|
+
return [`Found ${previews.length} preview URLs`, json(previews)];
|
|
1491
|
+
}
|
|
1492
|
+
case "delete": {
|
|
1493
|
+
if (!port) throw new Error("port is required for delete action");
|
|
1494
|
+
await client.delete(`v2/box/${box_id}/preview/${port}`);
|
|
1495
|
+
return `Preview for port ${port} deleted successfully`;
|
|
1496
|
+
}
|
|
1497
|
+
default: {
|
|
1498
|
+
throw new Error(`Unknown action: ${action}`);
|
|
1499
|
+
}
|
|
1500
|
+
}
|
|
1501
|
+
}
|
|
1502
|
+
})
|
|
1503
|
+
};
|
|
1504
|
+
|
|
1505
|
+
// src/tools/box/snapshots.ts
|
|
1506
|
+
import { z as z15 } from "zod";
|
|
1507
|
+
var boxSnapshotsTool = {
|
|
1508
|
+
box_snapshots: tool({
|
|
1509
|
+
description: `Manage Upstash Box snapshots. Create full filesystem snapshots of a box, list snapshots, delete them, or restore a box from a snapshot.`,
|
|
1510
|
+
get inputSchema() {
|
|
1511
|
+
return z15.object({
|
|
1512
|
+
action: z15.enum(["create", "list", "list_all", "delete", "restore"]).describe(
|
|
1513
|
+
"The action to perform. 'list' lists snapshots for a specific box, 'list_all' lists all your snapshots"
|
|
1514
|
+
),
|
|
1515
|
+
box_id: z15.string().optional().describe("Box ID (required for create, list, delete)"),
|
|
1516
|
+
snapshot_id: z15.string().optional().describe("Snapshot ID (required for delete and restore)"),
|
|
1517
|
+
// Create-specific
|
|
1518
|
+
name: z15.string().optional().describe("Name for the snapshot (auto-generated if empty)"),
|
|
1519
|
+
// Restore-specific
|
|
1520
|
+
model: z15.string().optional().describe("LLM model for the restored box (required for restore)"),
|
|
1521
|
+
runtime: z15.string().optional().describe("Override the snapshot's runtime for the restored box"),
|
|
1522
|
+
env_vars: z15.record(z15.string()).optional().describe("Environment variables for the restored box"),
|
|
1523
|
+
ephemeral: z15.boolean().optional().describe("Create the restored box as ephemeral"),
|
|
1524
|
+
ttl: z15.number().optional().describe("TTL in seconds for the restored ephemeral box (max 259200)"),
|
|
1525
|
+
...buildBoxCommon()
|
|
1526
|
+
});
|
|
1527
|
+
},
|
|
1528
|
+
handler: async (params) => {
|
|
1529
|
+
const { action, box_id, snapshot_id } = params;
|
|
1530
|
+
const client = getBoxClient(params);
|
|
1531
|
+
switch (action) {
|
|
1532
|
+
case "create": {
|
|
1533
|
+
if (!box_id) throw new Error("box_id is required for create action");
|
|
1534
|
+
const body = {};
|
|
1535
|
+
if (params.name) body.name = params.name;
|
|
1536
|
+
const snapshot = await client.post(`v2/box/${box_id}/snapshots`, body);
|
|
1537
|
+
return [
|
|
1538
|
+
`Snapshot created (status: ${snapshot.status})`,
|
|
1539
|
+
`Snapshot ID: ${snapshot.id}`,
|
|
1540
|
+
json(snapshot)
|
|
1541
|
+
];
|
|
1542
|
+
}
|
|
1543
|
+
case "list": {
|
|
1544
|
+
if (!box_id) throw new Error("box_id is required for list action");
|
|
1545
|
+
const response = await client.get(
|
|
1546
|
+
`v2/box/${box_id}/snapshots`
|
|
1547
|
+
);
|
|
1548
|
+
const snapshots = response.snapshots ?? [];
|
|
1549
|
+
return [`Found ${snapshots.length} snapshots for box ${box_id}`, json(snapshots)];
|
|
1550
|
+
}
|
|
1551
|
+
case "list_all": {
|
|
1552
|
+
const response = await client.get("v2/box/snapshots");
|
|
1553
|
+
const snapshots = response.snapshots ?? [];
|
|
1554
|
+
return [`Found ${snapshots.length} snapshots total`, json(snapshots)];
|
|
1555
|
+
}
|
|
1556
|
+
case "delete": {
|
|
1557
|
+
if (!box_id) throw new Error("box_id is required for delete action");
|
|
1558
|
+
if (!snapshot_id) throw new Error("snapshot_id is required for delete action");
|
|
1559
|
+
await client.delete(`v2/box/${box_id}/snapshots/${snapshot_id}`);
|
|
1560
|
+
return `Snapshot ${snapshot_id} deleted successfully`;
|
|
1561
|
+
}
|
|
1562
|
+
case "restore": {
|
|
1563
|
+
if (!snapshot_id) throw new Error("snapshot_id is required for restore action");
|
|
1564
|
+
if (!params.model) throw new Error("model is required for restore action");
|
|
1565
|
+
const body = {
|
|
1566
|
+
snapshot_id,
|
|
1567
|
+
model: params.model
|
|
1568
|
+
};
|
|
1569
|
+
if (params.runtime) body.runtime = params.runtime;
|
|
1570
|
+
if (params.env_vars) body.env_vars = params.env_vars;
|
|
1571
|
+
if (params.ephemeral !== void 0) body.ephemeral = params.ephemeral;
|
|
1572
|
+
if (params.ttl !== void 0) body.ttl = params.ttl;
|
|
1573
|
+
const box = await client.post("v2/box/from-snapshot", body);
|
|
1574
|
+
return [
|
|
1575
|
+
`Box restored from snapshot (status: ${box.status})`,
|
|
1576
|
+
`New Box ID: ${box.id}`,
|
|
1577
|
+
json(box)
|
|
1578
|
+
];
|
|
1579
|
+
}
|
|
1580
|
+
default: {
|
|
1581
|
+
throw new Error(`Unknown action: ${action}`);
|
|
1582
|
+
}
|
|
1583
|
+
}
|
|
1584
|
+
}
|
|
1585
|
+
})
|
|
1586
|
+
};
|
|
1587
|
+
|
|
1588
|
+
// src/tools/box/index.ts
|
|
1589
|
+
var boxTools = {
|
|
1590
|
+
...boxManageTool,
|
|
1591
|
+
...boxExecTool,
|
|
1592
|
+
...boxAgentRunTool,
|
|
1593
|
+
...boxLogsTool,
|
|
1594
|
+
...boxRunsTool,
|
|
1595
|
+
...boxPreviewTool,
|
|
1596
|
+
...boxSnapshotsTool
|
|
1597
|
+
};
|
|
1598
|
+
|
|
1063
1599
|
// src/tools/index.ts
|
|
1064
|
-
var json = (json2) => typeof json2 === "string" ? json2 : JSON.stringify(json2, null, 2);
|
|
1065
1600
|
var tools = {
|
|
1066
1601
|
...redisTools,
|
|
1067
|
-
...qstashAllTools
|
|
1602
|
+
...qstashAllTools,
|
|
1603
|
+
...boxTools
|
|
1068
1604
|
};
|
|
1069
|
-
function tool(tool2) {
|
|
1070
|
-
return tool2;
|
|
1071
|
-
}
|
|
1072
1605
|
|
|
1073
1606
|
// src/settings.ts
|
|
1074
1607
|
var MAX_MESSAGE_LENGTH = 3e4;
|
|
@@ -1087,7 +1620,7 @@ function handlerResponseToCallResult(response) {
|
|
|
1087
1620
|
}
|
|
1088
1621
|
|
|
1089
1622
|
// src/server.ts
|
|
1090
|
-
import
|
|
1623
|
+
import z16 from "zod";
|
|
1091
1624
|
function createServerInstance() {
|
|
1092
1625
|
const server = new McpServer(
|
|
1093
1626
|
{ name: "upstash", version: "0.1.0" },
|
|
@@ -1098,7 +1631,8 @@ function createServerInstance() {
|
|
|
1098
1631
|
}
|
|
1099
1632
|
}
|
|
1100
1633
|
);
|
|
1101
|
-
const
|
|
1634
|
+
const filteredTools = config.readonly ? Object.fromEntries(Object.entries(tools).filter(([_, tool2]) => tool2.readonly)) : tools;
|
|
1635
|
+
const toolsList = Object.entries(filteredTools).map(([name, tool2]) => ({
|
|
1102
1636
|
name,
|
|
1103
1637
|
description: tool2.description,
|
|
1104
1638
|
inputSchema: tool2.inputSchema,
|
|
@@ -1111,7 +1645,7 @@ function createServerInstance() {
|
|
|
1111
1645
|
toolName,
|
|
1112
1646
|
{
|
|
1113
1647
|
description: tool2.description,
|
|
1114
|
-
inputSchema: (tool2.inputSchema ??
|
|
1648
|
+
inputSchema: (tool2.inputSchema ?? z16.object({})).shape
|
|
1115
1649
|
},
|
|
1116
1650
|
// @ts-expect-error - Just ignore the types here
|
|
1117
1651
|
async (args) => {
|
|
@@ -1148,6 +1682,7 @@ Stack trace: ${error instanceof Error ? error.stack : "No stack trace available"
|
|
|
1148
1682
|
}
|
|
1149
1683
|
|
|
1150
1684
|
// src/test-connection.ts
|
|
1685
|
+
var READONLY_ERROR = "Readonly API key";
|
|
1151
1686
|
async function testConnection() {
|
|
1152
1687
|
log("\u{1F9EA} Testing connection to Upstash API");
|
|
1153
1688
|
let dbs;
|
|
@@ -1162,6 +1697,14 @@ async function testConnection() {
|
|
|
1162
1697
|
}
|
|
1163
1698
|
if (!Array.isArray(dbs))
|
|
1164
1699
|
throw new Error("Invalid response from Upstash API. Check your API key and email.");
|
|
1700
|
+
try {
|
|
1701
|
+
await http.delete("v2/redis/database/readonly-check-nonexistent");
|
|
1702
|
+
} catch (error) {
|
|
1703
|
+
if (error instanceof Error && error.message.includes(READONLY_ERROR)) {
|
|
1704
|
+
config.readonly = true;
|
|
1705
|
+
log("\u{1F512} Readonly API key detected. Write operations will be disabled.");
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1165
1708
|
log("\u2705 Connection to Upstash API is successful");
|
|
1166
1709
|
}
|
|
1167
1710
|
|
|
@@ -1171,10 +1714,13 @@ var argv = process.argv.slice(2);
|
|
|
1171
1714
|
if (argv.length >= 3 && argv[0] === "run") {
|
|
1172
1715
|
argv = ["--email", argv[1], "--api-key", argv[2], ...argv.slice(3)];
|
|
1173
1716
|
}
|
|
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();
|
|
1717
|
+
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("--box-api-key <key>", "Upstash Box API key (optional)").option("--debug", "Enable debug mode").option("--disable-telemetry", "Disable telemetry headers sent to Upstash APIs").allowUnknownOption();
|
|
1175
1718
|
program.parse(argv, { from: "user" });
|
|
1176
1719
|
var cliOptions = program.opts();
|
|
1177
1720
|
var DEBUG = cliOptions.debug ?? false;
|
|
1721
|
+
if (DEBUG) {
|
|
1722
|
+
initDebugLog(new URL("../", import.meta.url).pathname);
|
|
1723
|
+
}
|
|
1178
1724
|
var allowedTransports = ["stdio", "http"];
|
|
1179
1725
|
if (!allowedTransports.includes(cliOptions.transport)) {
|
|
1180
1726
|
console.error(
|
|
@@ -1203,6 +1749,8 @@ async function main() {
|
|
|
1203
1749
|
}
|
|
1204
1750
|
config.email = email;
|
|
1205
1751
|
config.apiKey = apiKey;
|
|
1752
|
+
config.boxApiKey = cliOptions.boxApiKey || process.env.UPSTASH_BOX_API_KEY || "";
|
|
1753
|
+
config.disableTelemetry = cliOptions.disableTelemetry ?? false;
|
|
1206
1754
|
await testConnection();
|
|
1207
1755
|
const transportType = TRANSPORT_TYPE;
|
|
1208
1756
|
if (transportType === "http") {
|