@upstash/mcp-server 0.0.7

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Upstash, Inc.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,118 @@
1
+ # Upstash MCP Server
2
+
3
+ [![smithery badge](https://smithery.ai/badge/@upstash/mcp-server)](https://smithery.ai/server/@upstash/mcp-server)
4
+
5
+ Model Context Protocol (MCP) is a [new, standardized protocol](https://modelcontextprotocol.io/introduction) for managing context between large language models (LLMs) and external systems. In this repository, we provide an installer as well as an MCP Server for [Upstash Developer API's](https://upstash.com/docs/devops/developer-api).
6
+
7
+ This lets you use Claude Desktop, or any MCP Client, to use natural language to accomplish things on your Upstash account, e.g.:
8
+
9
+ - "Create a new Redis database in us-east-1"
10
+ - "List all databases"
11
+ - "See keys starting with "user:" in users-db"
12
+ - "Create a backup"
13
+ - "Give me the spikes in throughput for the last 7 days"
14
+
15
+ # Usage
16
+
17
+ ## Requirements
18
+
19
+ - Node.js >= v18.0.0
20
+ - Claude Desktop
21
+ - [Upstash API key](https://upstash.com/docs/devops/developer-api) - You can create one from [here](https://console.upstash.com/account/api).
22
+
23
+ ## How to use locally
24
+
25
+ ### Installing via Smithery
26
+
27
+ To install Upstash for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@upstash/mcp-server):
28
+
29
+ ```bash
30
+ npx -y @smithery/cli install @upstash/mcp-server --client claude
31
+ ```
32
+
33
+ ### Installing manually
34
+
35
+ 1. Run `npx @upstash/mcp-server init <UPSTASH_EMAIL> <UPSTASH_API_KEY>`
36
+ 2. Restart Claude Desktop
37
+ 3. You should now be able to use Upstash commands in Claude Desktop
38
+
39
+ See the [troubleshooting guide](https://modelcontextprotocol.io/quickstart#troubleshooting) in the MCP documentation. You can also reach out to us at [Discord](https://discord.com/invite/w9SenAtbme).
40
+
41
+ > NOTE: If you are using a node version manager like nvm or fnm, please check [this issue](https://github.com/modelcontextprotocol/servers/issues/64#issuecomment-2530337743). You should change the `node` command in the MCP config to the absolute path of the node binary.
42
+
43
+ ## Tools
44
+
45
+ ### Redis
46
+
47
+ - `redis_database_create_backup`
48
+ - `redis_database_create_new`
49
+ - `redis_database_delete`
50
+ - `redis_database_delete_backup`
51
+ - `redis_database_get_details`
52
+ - `redis_database_list_backups`
53
+ - `redis_database_list_databases`
54
+ - `redis_database_reset_password`
55
+ - `redis_database_restore_backup`
56
+ - `redis_database_run_multiple_redis_commands`
57
+ - `redis_database_run_single_redis_command`
58
+ - `redis_database_set_daily_backup`
59
+ - `redis_database_update_regions`
60
+ - `redis_database_get_usage_last_5_days`
61
+ - `redis_database_get_stats`
62
+
63
+ ## Development
64
+
65
+ Clone the project and run:
66
+
67
+ ```bash
68
+ bun install
69
+ npm run watch
70
+ ```
71
+
72
+ This will continuously build the project and watch for changes.
73
+
74
+ For testing, you can create a `.env` file in the same directory as the project with the following content:
75
+
76
+ ```bash
77
+ UPSTASH_EMAIL=<UPSTASH_EMAIL>
78
+ UPSTASH_API_KEY=<UPSTASH_API_KEY>
79
+ ```
80
+
81
+ This will be used for setting the Claude config and running mcp inspector.
82
+
83
+ ### Testing with Claude Desktop
84
+
85
+ To install the Claude Desktop config for local development, run the following command:
86
+
87
+ ```bash
88
+ npm run setup
89
+ ```
90
+
91
+ This will add an `upstash` entry to your MCP config file that points to the local build of the package.
92
+
93
+ ```json
94
+ {
95
+ "upstash": {
96
+ "command": "node",
97
+ "args": ["<path-to-repo>/dist/index.js", "run", "<UPSTASH_EMAIL>", "<UPSTASH_API_KEY>"]
98
+ }
99
+ }
100
+ ```
101
+
102
+ > NOTE: The same issue with node version manager applies here. Please look at the note in the usage section if you are using a node version manager.
103
+
104
+ You can now use Claude Desktop to run Upstash commands.
105
+
106
+ To view the logs from the MCP Server in real time, run the following command:
107
+
108
+ ```bash
109
+ npm run logs
110
+ ```
111
+
112
+ ### Testing with MCP Inspector
113
+
114
+ You can also use the MCP Inspector to test the tools.
115
+
116
+ ```bash
117
+ npm run inspector
118
+ ```
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,674 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+
6
+ // src/init.ts
7
+ import path from "path";
8
+ import os from "os";
9
+ import fs from "fs";
10
+ import chalk from "chalk";
11
+
12
+ // src/log.ts
13
+ function log(...args) {
14
+ const msg = `[DEBUG ${(/* @__PURE__ */ new Date()).toISOString()}] ${args.map((arg) => typeof arg === "string" ? arg : JSON.stringify(arg)).join(" ")}
15
+ `;
16
+ process.stderr.write(msg);
17
+ }
18
+
19
+ // src/config.ts
20
+ var config = {
21
+ apiKey: "",
22
+ email: ""
23
+ };
24
+
25
+ // src/init.ts
26
+ var claudeConfigPath = path.join(
27
+ os.homedir(),
28
+ "Library",
29
+ "Application Support",
30
+ "Claude",
31
+ "claude_desktop_config.json"
32
+ );
33
+ var UPSTASH_MCP_KEY = "upstash";
34
+ async function init({ executablePath }) {
35
+ const isLocal = executablePath.includes("dist/index.js");
36
+ const upstashConfig = isLocal ? {
37
+ command: "node",
38
+ args: [executablePath, "run", config.email, config.apiKey]
39
+ } : {
40
+ command: "npx",
41
+ args: ["-y", "@upstash/mcp-server", "run", config.email, config.apiKey]
42
+ };
43
+ const configDir = path.dirname(claudeConfigPath);
44
+ if (!fs.existsSync(configDir)) {
45
+ log(chalk.blue("Creating Claude config directory..."));
46
+ fs.mkdirSync(configDir, { recursive: true });
47
+ }
48
+ const existingConfig = fs.existsSync(claudeConfigPath) ? JSON.parse(fs.readFileSync(claudeConfigPath, "utf8")) : { mcpServers: {} };
49
+ if (UPSTASH_MCP_KEY in ((existingConfig == null ? void 0 : existingConfig.mcpServers) || {})) {
50
+ log(chalk.yellow("Upstash entry already exists. Overriding it."));
51
+ }
52
+ if (isLocal) {
53
+ log(
54
+ chalk.yellow(
55
+ "Local executable detected. Using 'node' and absolute path instead of 'npx' for development."
56
+ )
57
+ );
58
+ }
59
+ const newConfig = {
60
+ ...existingConfig,
61
+ mcpServers: {
62
+ ...existingConfig.mcpServers,
63
+ [UPSTASH_MCP_KEY]: upstashConfig
64
+ }
65
+ };
66
+ fs.writeFileSync(claudeConfigPath, JSON.stringify(newConfig, null, 2));
67
+ log(
68
+ chalk.blue(
69
+ "\n" + JSON.stringify(
70
+ {
71
+ [UPSTASH_MCP_KEY]: upstashConfig
72
+ },
73
+ null,
74
+ 2
75
+ ).replaceAll(config.apiKey, "********")
76
+ )
77
+ );
78
+ log(chalk.green(`Config written to: "${claudeConfigPath.replace(os.homedir(), "~")}"`));
79
+ }
80
+
81
+ // src/server.ts
82
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
83
+ import { ListToolsRequestSchema, CallToolRequestSchema } from "@modelcontextprotocol/sdk/types.js";
84
+
85
+ // src/tools/utils.ts
86
+ import { z } from "zod";
87
+ var utilTools = {
88
+ timestamps_to_date: tool({
89
+ description: `Use this tool to convert a timestamp to a human-readable date`,
90
+ inputSchema: z.object({
91
+ timestamps: z.array(z.number()).describe("Array of timestamps to convert")
92
+ }),
93
+ handler: async ({ timestamps }) => {
94
+ return timestamps.map((timestamp) => new Date(timestamp).toUTCString());
95
+ }
96
+ })
97
+ };
98
+
99
+ // src/tools/redis/backup.ts
100
+ import { z as z2 } from "zod";
101
+
102
+ // src/middlewares.ts
103
+ var formatCreationTime = (obj) => {
104
+ if (obj && typeof obj === "object" && "creation_time" in obj && typeof obj.creation_time === "number" && obj.creation_time > 0) {
105
+ obj.creation_time = new Date(obj.creation_time * 1e3).toLocaleString();
106
+ }
107
+ };
108
+ var middlewares = [
109
+ // Middleware to format creation_time field to human readable format
110
+ async (req, next) => {
111
+ const res = await next();
112
+ if (Array.isArray(res)) {
113
+ for (const element of res) {
114
+ formatCreationTime(element);
115
+ }
116
+ } else {
117
+ formatCreationTime(res);
118
+ }
119
+ return res;
120
+ }
121
+ ];
122
+ var applyMiddlewares = async (req, func) => {
123
+ let next = async () => func(req);
124
+ for (const middleware of middlewares.reverse()) {
125
+ const prevNext = next;
126
+ next = async () => middleware(req, prevNext);
127
+ }
128
+ return next();
129
+ };
130
+
131
+ // src/http.ts
132
+ import fetch from "node-fetch";
133
+ var HttpClient = class {
134
+ constructor(config2) {
135
+ this.baseUrl = config2.baseUrl.replace(/\/$/, "");
136
+ }
137
+ async get(path2) {
138
+ return this.requestWithMiddleware({ method: "GET", path: path2 });
139
+ }
140
+ async post(path2, body) {
141
+ return this.requestWithMiddleware({ method: "POST", path: path2, body });
142
+ }
143
+ async patch(path2, body) {
144
+ return this.requestWithMiddleware({ method: "PATCH", path: path2, body });
145
+ }
146
+ async delete(path2) {
147
+ return this.requestWithMiddleware({ method: "DELETE", path: path2 });
148
+ }
149
+ async requestWithMiddleware(req) {
150
+ const res = await applyMiddlewares(req, async (req2) => {
151
+ return this.request(req2);
152
+ });
153
+ return res;
154
+ }
155
+ async request(req) {
156
+ if (!req.path) {
157
+ req.path = [];
158
+ } else if (typeof req.path === "string") {
159
+ req.path = [req.path];
160
+ }
161
+ const url = [this.baseUrl, ...req.path].join("/");
162
+ const token = [config.email, config.apiKey].join(":");
163
+ const init2 = {
164
+ method: req.method,
165
+ headers: {
166
+ "Content-Type": "application/json",
167
+ Authorization: `Basic ${Buffer.from(token).toString("base64")}`
168
+ }
169
+ };
170
+ if (req.method !== "GET") {
171
+ init2.body = JSON.stringify(req.body);
172
+ }
173
+ log("Sending request", {
174
+ url,
175
+ ...init2,
176
+ headers: { ...init2.headers, Authorization: "***" }
177
+ });
178
+ const res = await fetch(url, init2);
179
+ if (!res.ok) {
180
+ throw new Error(`Request failed (${res.status} ${res.statusText}): ${await res.text()}`);
181
+ }
182
+ return await res.json();
183
+ }
184
+ };
185
+ var http = new HttpClient({ baseUrl: "https://api.upstash.com" });
186
+
187
+ // src/tools/redis/backup.ts
188
+ var redisBackupTools = {
189
+ redis_database_create_backup: tool({
190
+ description: `Create a backup of a specific Upstash redis database.
191
+ NOTE: Ask user to choose a name for the backup`,
192
+ inputSchema: z2.object({
193
+ database_id: z2.string().describe("The ID of the database to create a backup for."),
194
+ backup_name: z2.string().describe("A name for the backup.")
195
+ }),
196
+ handler: async ({ database_id, backup_name }) => {
197
+ await http.post(["v2/redis/create-backup", database_id], {
198
+ name: backup_name
199
+ });
200
+ return "OK";
201
+ }
202
+ }),
203
+ redis_database_delete_backup: tool({
204
+ description: `Delete a backup of a specific Upstash redis database.`,
205
+ inputSchema: z2.object({
206
+ database_id: z2.string().describe("The ID of the database to delete a backup from."),
207
+ backup_id: z2.string().describe("The ID of the backup to delete.")
208
+ }),
209
+ handler: async ({ database_id, backup_id }) => {
210
+ await http.delete(["v2/redis/delete-backup", database_id, backup_id]);
211
+ return "OK";
212
+ }
213
+ }),
214
+ redis_database_restore_backup: tool({
215
+ description: `Restore a backup of a specific Upstash redis database. A backup can only be restored to the same database it was created from.`,
216
+ inputSchema: z2.object({
217
+ database_id: z2.string().describe("The ID of the database to restore a backup to."),
218
+ backup_id: z2.string().describe("The ID of the backup to restore.")
219
+ }),
220
+ handler: async ({ database_id, backup_id }) => {
221
+ await http.post(["v2/redis/restore-backup", database_id], {
222
+ backup_id
223
+ });
224
+ return "OK";
225
+ }
226
+ }),
227
+ redis_database_list_backups: tool({
228
+ // TODO: Add explanation for fields
229
+ // TODO: Is this in bytes?
230
+ description: `List all backups of a specific Upstash redis database.`,
231
+ inputSchema: z2.object({
232
+ database_id: z2.string().describe("The ID of the database to list backups for.")
233
+ }),
234
+ handler: async ({ database_id }) => {
235
+ const backups = await http.get(["v2/redis/list-backup", database_id]);
236
+ return json(backups);
237
+ }
238
+ }),
239
+ redis_database_set_daily_backup: tool({
240
+ description: `Enable or disable daily backups for a specific Upstash redis database.`,
241
+ inputSchema: z2.object({
242
+ database_id: z2.string().describe("The ID of the database to enable or disable daily backups for."),
243
+ enable: z2.boolean().describe("Whether to enable or disable daily backups.")
244
+ }),
245
+ handler: async ({ database_id, enable }) => {
246
+ await http.patch([
247
+ `v2/redis/${enable ? "enable-dailybackup" : "disable-dailybackup"}`,
248
+ database_id
249
+ ]);
250
+ return "OK";
251
+ }
252
+ })
253
+ };
254
+
255
+ // src/tools/redis/command.ts
256
+ import { z as z3 } from "zod";
257
+ import fetch2 from "node-fetch";
258
+ var redisCommandTools = {
259
+ redis_database_run_single_redis_command: tool({
260
+ description: `Run a single Redis command on a specific Upstash redis database.
261
+ NOTE: For discovery, use SCAN over KEYS. Use TYPE to get the type of a key.
262
+ NOTE: SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`,
263
+ inputSchema: z3.object({
264
+ database_rest_url: z3.string().describe("The REST URL of the database. Example: https://***.upstash.io"),
265
+ database_rest_token: z3.string().describe("The REST token of the database."),
266
+ command: z3.array(z3.string()).describe("The Redis command to run. Example: ['SET', 'foo', 'bar', 'EX', 100]")
267
+ }),
268
+ handler: async ({ database_rest_url, database_rest_token, command }) => {
269
+ const req = await fetch2(database_rest_url, {
270
+ method: "POST",
271
+ body: JSON.stringify(command),
272
+ headers: {
273
+ "Content-Type": "application/json",
274
+ Authorization: `Bearer ${database_rest_token}`
275
+ }
276
+ });
277
+ const result = await req.json();
278
+ log("command result:", result);
279
+ if ("error" in result) {
280
+ throw new Error("Redis error: " + result.error);
281
+ }
282
+ const isScanCommand = command[0].toLocaleLowerCase().includes("scan");
283
+ const messages = [json(result)];
284
+ if (isScanCommand)
285
+ messages.push(`NOTE: Use the returned cursor to get the next set of keys.
286
+ NOTE: The result might be too large to be returned. If applicable, stop after the second SCAN command and ask the user if they want to continue.`);
287
+ return messages;
288
+ }
289
+ }),
290
+ redis_database_run_multiple_redis_commands: tool({
291
+ description: `Run multiple Redis commands on a specific Upstash redis database`,
292
+ inputSchema: z3.object({
293
+ database_rest_url: z3.string().describe("The REST URL of the database. Example: https://***.upstash.io"),
294
+ database_rest_token: z3.string().describe("The REST token of the database."),
295
+ commands: z3.array(z3.array(z3.string())).describe("The Redis commands to run. Example: [['SET', 'foo', 'bar'], ['GET', 'foo']]")
296
+ }),
297
+ handler: async ({ database_rest_url, database_rest_token, commands }) => {
298
+ const req = await fetch2(database_rest_url + "/pipeline", {
299
+ method: "POST",
300
+ body: JSON.stringify(commands),
301
+ headers: {
302
+ "Content-Type": "application/json",
303
+ Authorization: `Bearer ${database_rest_token}`
304
+ }
305
+ });
306
+ const result = await req.json();
307
+ log("commands result:", result);
308
+ if (result.some((r) => "error" in r)) {
309
+ throw new Error("Some commands in the pipeline resulted in an error:\n" + json(result));
310
+ }
311
+ return json(result);
312
+ }
313
+ })
314
+ };
315
+
316
+ // src/tools/redis/db.ts
317
+ import { z as z4 } from "zod";
318
+
319
+ // src/utils.ts
320
+ function pruneFalsy(obj) {
321
+ return Object.fromEntries(Object.entries(obj).filter(([, value]) => value));
322
+ }
323
+
324
+ // src/tools/redis/db.ts
325
+ var readRegionSchema = z4.union([
326
+ z4.literal("us-east-1"),
327
+ z4.literal("us-west-1"),
328
+ z4.literal("us-west-2"),
329
+ z4.literal("eu-west-1"),
330
+ z4.literal("eu-central-1"),
331
+ z4.literal("ap-southeast-1"),
332
+ z4.literal("ap-southeast-2"),
333
+ z4.literal("sa-east-1")
334
+ ]);
335
+ var GENERIC_DATABASE_NOTES = "\nNOTE: Don't show the database ID from the response to the user unless explicitly asked or needed.\n";
336
+ var redisDbOpsTools = {
337
+ redis_database_create_new: tool({
338
+ description: `Create a new Upstash redis database.
339
+ NOTE: Ask user for the region and name of the database.${GENERIC_DATABASE_NOTES}`,
340
+ inputSchema: z4.object({
341
+ name: z4.string().describe("Name of the database."),
342
+ primary_region: readRegionSchema.describe(`Primary Region of the Global Database.`),
343
+ read_regions: z4.array(readRegionSchema).optional().describe(`Array of read regions of the db`)
344
+ }),
345
+ handler: async ({ name, primary_region, read_regions }) => {
346
+ const newDb = await http.post("v2/redis/database", {
347
+ name,
348
+ region: "global",
349
+ primary_region,
350
+ read_regions
351
+ });
352
+ return [
353
+ json(newDb),
354
+ `Upstash console url: https://console.upstash.com/redis/${newDb.database_id}`
355
+ ];
356
+ }
357
+ }),
358
+ redis_database_delete: tool({
359
+ description: `Delete an Upstash redis database.`,
360
+ inputSchema: z4.object({
361
+ database_id: z4.string().describe("The ID of the database to delete.")
362
+ }),
363
+ handler: async ({ database_id }) => {
364
+ await http.delete(["v2/redis/database", database_id]);
365
+ return "Database deleted successfully.";
366
+ }
367
+ }),
368
+ redis_database_list_databases: tool({
369
+ description: `List all Upstash redis databases. Only their names and ids.${GENERIC_DATABASE_NOTES}`,
370
+ handler: async () => {
371
+ const dbs = await http.get("v2/redis/databases");
372
+ const messages = [
373
+ json(
374
+ dbs.map((db) => {
375
+ const result = {
376
+ database_id: db.database_id,
377
+ database_name: db.database_name,
378
+ state: db.state === "active" ? void 0 : db.state
379
+ };
380
+ return pruneFalsy(result);
381
+ })
382
+ )
383
+ ];
384
+ if (dbs.length > 2)
385
+ messages.push(
386
+ `NOTE: If the user did not specify a database name for the next command, ask them to choose a database from the list.`
387
+ );
388
+ messages.push(
389
+ "NOTE: If the user wants to see dbs in another team, mention that they need to create a new management api key for that team and initialize MCP server with the newly created key."
390
+ );
391
+ return messages;
392
+ }
393
+ }),
394
+ redis_database_get_details: tool({
395
+ description: `Get further details of a specific Upstash redis database. Includes all details of the database including usage statistics.
396
+ db_disk_threshold: Total disk usage limit.
397
+ db_memory_threshold: Maximum memory usage.
398
+ db_daily_bandwidth_limit: Maximum daily network bandwidth usage.
399
+ db_request_limit: Total number of commands allowed.
400
+ All sizes are in bytes
401
+ ${GENERIC_DATABASE_NOTES}
402
+ `,
403
+ inputSchema: z4.object({
404
+ database_id: z4.string().describe("The ID of the database to get details for.")
405
+ }),
406
+ handler: async ({ database_id }) => {
407
+ const db = await http.get(["v2/redis/database", database_id]);
408
+ return json(db);
409
+ }
410
+ }),
411
+ redis_database_update_regions: tool({
412
+ description: `Update the read regions of an Upstash redis database.`,
413
+ inputSchema: z4.object({
414
+ id: z4.string().describe("The ID of your database."),
415
+ read_regions: z4.array(readRegionSchema).describe(
416
+ "Array of the new read regions of the database. This will replace the old regions array. Available regions: us-east-1, us-west-1, us-west-2, eu-west-1, eu-central-1, ap-southeast-1, ap-southeast-2, sa-east-1"
417
+ )
418
+ }),
419
+ handler: async ({ id, read_regions }) => {
420
+ const updatedDb = await http.post(["v2/redis/update-regions", id], {
421
+ read_regions
422
+ });
423
+ return json(updatedDb);
424
+ }
425
+ }),
426
+ redis_database_reset_password: tool({
427
+ description: `Reset the password of an Upstash redis database.`,
428
+ inputSchema: z4.object({
429
+ id: z4.string().describe("The ID of your database.")
430
+ }),
431
+ handler: async ({ id }) => {
432
+ const updatedDb = await http.post(["v2/redis/reset-password", id], {});
433
+ return json(updatedDb);
434
+ }
435
+ }),
436
+ redis_database_get_usage_last_5_days: tool({
437
+ description: `Get PRECISE command count and bandwidth usage statistics of an Upstash redis database over the last 5 days. This is a precise stat, not an average.
438
+ NOTE: Ask user first if they want to see stats for each database seperately or just for one.`,
439
+ inputSchema: z4.object({
440
+ id: z4.string().describe("The ID of your database.")
441
+ }),
442
+ handler: async ({ id }) => {
443
+ const stats = await http.get(["v2/redis/stats", `${id}?period=3h`]);
444
+ return [
445
+ json({
446
+ days: stats.days,
447
+ command_usage: stats.dailyrequests,
448
+ bandwidth_usage: stats.bandwidths
449
+ }),
450
+ `NOTE: Times are calculated according to UTC+0`
451
+ ];
452
+ }
453
+ }),
454
+ redis_database_get_stats: tool({
455
+ description: `Get SAMPLED usage statistics of an Upstash redis database over a period of time (1h, 3h, 12h, 1d, 3d, 7d). Use this to check for peak usages and latency problems.
456
+ Includes: read_latency_mean, write_latency_mean, keyspace, throughput (cmds/sec), diskusage
457
+ NOTE: If the user does not specify which stat to get, use throughput as default.`,
458
+ inputSchema: z4.object({
459
+ id: z4.string().describe("The ID of your database."),
460
+ period: z4.union([
461
+ z4.literal("1h"),
462
+ z4.literal("3h"),
463
+ z4.literal("12h"),
464
+ z4.literal("1d"),
465
+ z4.literal("3d"),
466
+ z4.literal("7d")
467
+ ]).describe("The period of the stats."),
468
+ type: z4.union([
469
+ z4.literal("read_latency_mean"),
470
+ z4.literal("write_latency_mean"),
471
+ z4.literal("keyspace").describe("Number of keys in db"),
472
+ z4.literal("throughput").describe("commands per second (sampled), calculate area for estimated count"),
473
+ z4.literal("diskusage").describe("Current disk usage in bytes")
474
+ ]).describe("The type of stat to get")
475
+ }),
476
+ handler: async ({ id, period, type }) => {
477
+ const stats = await http.get([
478
+ "v2/redis/stats",
479
+ `${id}?period=${period}`
480
+ ]);
481
+ const stat = stats[type];
482
+ if (!Array.isArray(stat))
483
+ throw new Error(
484
+ `Invalid key provided: ${type}. Valid keys are: ${Object.keys(stats).join(", ")}`
485
+ );
486
+ return [
487
+ JSON.stringify(parseUsageData(stat)),
488
+ `NOTE: Use the timestamps_to_date tool to parse timestamps if needed`,
489
+ `NOTE: Don't try to plot multiple stats in the same chart`
490
+ ];
491
+ }
492
+ })
493
+ };
494
+ var parseUsageData = (data) => {
495
+ var _a;
496
+ if (!data) return "NO DATA";
497
+ if (!Array.isArray(data)) return "INVALID DATA";
498
+ if (data.length === 0 || data.length === 1) return "NO DATA";
499
+ const filteredData = data.filter((d) => d.x && d.y);
500
+ return {
501
+ start: filteredData[0].x,
502
+ // last one can be null, so use the second last
503
+ // eslint-disable-next-line unicorn/prefer-at
504
+ end: (_a = filteredData[filteredData.length - 1]) == null ? void 0 : _a.x,
505
+ data: data.map((d) => [new Date(d.x).getTime(), d.y])
506
+ };
507
+ };
508
+
509
+ // src/tools/redis/index.ts
510
+ var redisTools = {
511
+ ...redisDbOpsTools,
512
+ ...redisBackupTools,
513
+ ...redisCommandTools,
514
+ ...utilTools
515
+ };
516
+
517
+ // src/tools/index.ts
518
+ var json = (json2) => typeof json2 === "string" ? json2 : JSON.stringify(json2, null, 2);
519
+ var tools = {
520
+ ...redisTools
521
+ };
522
+ function tool(tool2) {
523
+ return tool2;
524
+ }
525
+
526
+ // src/tool.ts
527
+ import { z as z5 } from "zod";
528
+ import { zodToJsonSchema } from "zod-to-json-schema";
529
+
530
+ // src/settings.ts
531
+ var MAX_MESSAGE_LENGTH = 8e3;
532
+
533
+ // src/tool.ts
534
+ function handlerResponseToCallResult(response) {
535
+ if (typeof response === "string" || Array.isArray(response)) {
536
+ const array = Array.isArray(response) ? response : [response];
537
+ const truncatedArray = array.map(
538
+ (item) => item.length > MAX_MESSAGE_LENGTH ? `${item.slice(0, MAX_MESSAGE_LENGTH)}... (MESSAGE TRUNCATED, MENTION THIS TO USER)` : item
539
+ );
540
+ return {
541
+ content: truncatedArray.map((text) => ({ type: "text", text }))
542
+ };
543
+ } else return response;
544
+ }
545
+ function convertToJsonSchema(schema) {
546
+ const jsonSchema = zodToJsonSchema(schema);
547
+ delete jsonSchema.$schema;
548
+ const removeAdditionalProperties = (schema2) => {
549
+ if (schema2.type !== "object") return;
550
+ delete schema2.additionalProperties;
551
+ for (const value of Object.values(schema2.properties)) {
552
+ removeAdditionalProperties(value);
553
+ }
554
+ };
555
+ removeAdditionalProperties(jsonSchema);
556
+ return jsonSchema;
557
+ }
558
+ function convertToTools(tools2) {
559
+ return Object.entries(tools2).map(([name, tool2]) => ({
560
+ name,
561
+ description: tool2.description,
562
+ inputSchema: convertToJsonSchema(tool2.inputSchema ?? z5.object({}))
563
+ }));
564
+ }
565
+
566
+ // src/server.ts
567
+ var server = new Server(
568
+ { name: "upstash", version: "0.1.0" },
569
+ { capabilities: { tools: {} } }
570
+ );
571
+ var toolsList = convertToTools(tools);
572
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
573
+ log("Received list tools request", toolsList);
574
+ return { tools: toolsList };
575
+ });
576
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
577
+ const toolName = request.params.name;
578
+ log("< received tool call:", toolName, request.params.arguments);
579
+ try {
580
+ if (toolName in tools) {
581
+ const tool2 = tools[toolName];
582
+ const result = await tool2.handler(request.params.arguments);
583
+ const response = handlerResponseToCallResult(result);
584
+ log("> tool result:", response.content.map((item) => item.text).join("\n"));
585
+ return response;
586
+ }
587
+ throw new Error(`Unknown tool: ${toolName}`);
588
+ } catch (error) {
589
+ const msg = error instanceof Error ? error.message : String(error);
590
+ log("> error in tool call:", msg);
591
+ return {
592
+ content: [
593
+ {
594
+ type: "text",
595
+ text: `Error: ${msg}`
596
+ }
597
+ ],
598
+ isError: true
599
+ };
600
+ }
601
+ });
602
+
603
+ // src/index.ts
604
+ import "dotenv/config";
605
+
606
+ // src/test-connection.ts
607
+ async function testConnection() {
608
+ log("\u{1F9EA} Testing connection to Upstash API");
609
+ const res = await http.get("v2/teams");
610
+ if (!Array.isArray(res))
611
+ throw new Error("Invalid response from Upstash API. Check your API key and email.");
612
+ log("\u2705 Connection to Upstash API is successful");
613
+ }
614
+
615
+ // src/index.ts
616
+ process.on("uncaughtException", (error) => {
617
+ log("Uncaught exception:", error.name, error.message, error.stack);
618
+ });
619
+ process.on("unhandledRejection", (error) => {
620
+ if (error instanceof Error) log("Unhandled rejection:", error.name, error.message, error.stack);
621
+ else log("Unhandled rejection:", error);
622
+ });
623
+ var envApiKey = process.env.UPSTASH_API_KEY;
624
+ var envEmail = process.env.UPSTASH_EMAIL;
625
+ var USAGE_GENERAL = `Usage: npx @upstash/mcp-server (init|run) <UPSTASH_EMAIL> <UPSTASH_API_KEY>`;
626
+ var USAGE_RUN = `Usage: npx @upstash/mcp-server run <UPSTASH_EMAIL> <UPSTASH_API_KEY>`;
627
+ var USAGE_INIT = `Usage: npx @upstash/mcp-server init <UPSTASH_EMAIL> <UPSTASH_API_KEY>`;
628
+ function parseArguments() {
629
+ const [cmd, ...args] = process.argv.slice(2);
630
+ if (!cmd) throw new Error(USAGE_GENERAL);
631
+ else if (cmd === "init") {
632
+ const [email, apiKey, ...rest] = args;
633
+ const finalApiKey = apiKey || envApiKey;
634
+ const finalEmail = email || envEmail;
635
+ if (rest.length > 0) throw new Error(`Too many arguments. ${USAGE_INIT}`);
636
+ if (!finalApiKey) throw new Error(`Missing API key. ${USAGE_INIT}`);
637
+ if (!finalEmail) throw new Error(`Missing email. ${USAGE_INIT}`);
638
+ config.apiKey = finalApiKey;
639
+ config.email = finalEmail;
640
+ testConnection();
641
+ init({
642
+ executablePath: process.argv[1]
643
+ });
644
+ } else if (cmd === "run") {
645
+ const [email, apiKey, ...rest] = args;
646
+ const finalApiKey = apiKey || envApiKey;
647
+ const finalEmail = email || envEmail;
648
+ if (!finalApiKey) throw new Error(`Missing API key. ${USAGE_RUN}`);
649
+ if (!finalEmail) throw new Error(`Missing email. ${USAGE_RUN}`);
650
+ if (rest.length > 0) throw new Error(`Too many arguments. ${USAGE_RUN}`);
651
+ log("Starting MCP server");
652
+ config.apiKey = finalApiKey;
653
+ config.email = finalEmail;
654
+ testConnection();
655
+ main().catch((error) => {
656
+ console.error("Server error:", error);
657
+ process.exit(1);
658
+ });
659
+ } else {
660
+ throw new Error(`Unknown command: ${cmd}. Expected 'init' or 'run'. ${USAGE_GENERAL}`);
661
+ }
662
+ }
663
+ async function main() {
664
+ const transport = new StdioServerTransport();
665
+ await server.connect(transport);
666
+ }
667
+ try {
668
+ parseArguments();
669
+ } catch (error) {
670
+ if (!(error instanceof Error)) throw error;
671
+ console.error(error.message);
672
+ process.exit(1);
673
+ }
674
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/init.ts","../src/log.ts","../src/config.ts","../src/server.ts","../src/tools/utils.ts","../src/tools/redis/backup.ts","../src/middlewares.ts","../src/http.ts","../src/tools/redis/command.ts","../src/tools/redis/db.ts","../src/utils.ts","../src/tools/redis/index.ts","../src/tools/index.ts","../src/tool.ts","../src/settings.ts","../src/test-connection.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { init } from \"./init\";\nimport { log } from \"./log\";\nimport { server } from \"./server\";\nimport { config } from \"./config\";\nimport \"dotenv/config\";\nimport { testConnection } from \"./test-connection\";\n\nprocess.on(\"uncaughtException\", (error) => {\n log(\"Uncaught exception:\", error.name, error.message, error.stack);\n});\n\nprocess.on(\"unhandledRejection\", (error) => {\n if (error instanceof Error) log(\"Unhandled rejection:\", error.name, error.message, error.stack);\n else log(\"Unhandled rejection:\", error);\n});\n\nconst envApiKey = process.env.UPSTASH_API_KEY;\nconst envEmail = process.env.UPSTASH_EMAIL;\n\nconst USAGE_GENERAL = `Usage: npx @upstash/mcp-server (init|run) <UPSTASH_EMAIL> <UPSTASH_API_KEY>`;\nconst USAGE_RUN = `Usage: npx @upstash/mcp-server run <UPSTASH_EMAIL> <UPSTASH_API_KEY>`;\nconst USAGE_INIT = `Usage: npx @upstash/mcp-server init <UPSTASH_EMAIL> <UPSTASH_API_KEY>`;\n\nfunction parseArguments() {\n const [cmd, ...args] = process.argv.slice(2);\n if (!cmd) throw new Error(USAGE_GENERAL);\n else if (cmd === \"init\") {\n const [email, apiKey, ...rest] = args;\n const finalApiKey = apiKey || envApiKey;\n const finalEmail = email || envEmail;\n\n if (rest.length > 0) throw new Error(`Too many arguments. ${USAGE_INIT}`);\n if (!finalApiKey) throw new Error(`Missing API key. ${USAGE_INIT}`);\n if (!finalEmail) throw new Error(`Missing email. ${USAGE_INIT}`);\n\n config.apiKey = finalApiKey;\n config.email = finalEmail;\n\n testConnection();\n\n init({\n executablePath: process.argv[1],\n });\n } else if (cmd === \"run\") {\n const [email, apiKey, ...rest] = args;\n const finalApiKey = apiKey || envApiKey;\n const finalEmail = email || envEmail;\n\n if (!finalApiKey) throw new Error(`Missing API key. ${USAGE_RUN}`);\n if (!finalEmail) throw new Error(`Missing email. ${USAGE_RUN}`);\n if (rest.length > 0) throw new Error(`Too many arguments. ${USAGE_RUN}`);\n log(\"Starting MCP server\");\n\n config.apiKey = finalApiKey;\n config.email = finalEmail;\n\n testConnection();\n\n // Start the server\n main().catch((error) => {\n console.error(\"Server error:\", error);\n process.exit(1);\n });\n } else {\n throw new Error(`Unknown command: ${cmd}. Expected 'init' or 'run'. ${USAGE_GENERAL}`);\n }\n}\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\ntry {\n parseArguments();\n} catch (error) {\n if (!(error instanceof Error)) throw error;\n console.error(error.message);\n process.exit(1);\n}\n","/* eslint-disable unicorn/prefer-node-protocol */\nimport path from \"path\";\nimport os from \"os\";\nimport fs from \"fs\";\nimport chalk from \"chalk\";\n\nimport { log } from \"./log\";\nimport { config } from \"./config\";\n\nconst claudeConfigPath = path.join(\n os.homedir(),\n \"Library\",\n \"Application Support\",\n \"Claude\",\n \"claude_desktop_config.json\"\n);\n\nconst UPSTASH_MCP_KEY = \"upstash\";\n\nexport async function init({ executablePath }: { executablePath: string }) {\n const isLocal = executablePath.includes(\"dist/index.js\");\n const upstashConfig = isLocal\n ? {\n command: \"node\",\n args: [executablePath, \"run\", config.email, config.apiKey],\n }\n : {\n command: \"npx\",\n args: [\"-y\", \"@upstash/mcp-server\", \"run\", config.email, config.apiKey],\n };\n\n const configDir = path.dirname(claudeConfigPath);\n if (!fs.existsSync(configDir)) {\n log(chalk.blue(\"Creating Claude config directory...\"));\n fs.mkdirSync(configDir, { recursive: true });\n }\n\n const existingConfig = fs.existsSync(claudeConfigPath)\n ? (JSON.parse(fs.readFileSync(claudeConfigPath, \"utf8\")) as {\n mcpServers?: Record<string, unknown>;\n })\n : { mcpServers: {} };\n\n if (UPSTASH_MCP_KEY in (existingConfig?.mcpServers || {})) {\n log(chalk.yellow(\"Upstash entry already exists. Overriding it.\"));\n }\n\n if (isLocal) {\n log(\n chalk.yellow(\n \"Local executable detected. Using 'node' and absolute path instead of 'npx' for development.\"\n )\n );\n }\n\n const newConfig = {\n ...existingConfig,\n mcpServers: {\n ...existingConfig.mcpServers,\n [UPSTASH_MCP_KEY]: upstashConfig,\n },\n };\n\n fs.writeFileSync(claudeConfigPath, JSON.stringify(newConfig, null, 2));\n\n log(\n chalk.blue(\n \"\\n\" +\n JSON.stringify(\n {\n [UPSTASH_MCP_KEY]: upstashConfig,\n },\n null,\n 2\n ).replaceAll(config.apiKey, \"********\")\n )\n );\n log(chalk.green(`Config written to: \"${claudeConfigPath.replace(os.homedir(), \"~\")}\"`));\n}\n","export function log(...args: unknown[]) {\n const msg = `[DEBUG ${new Date().toISOString()}] ${args.map((arg) => (typeof arg === \"string\" ? arg : JSON.stringify(arg))).join(\" \")}\\n`;\n process.stderr.write(msg);\n}\n","export const config = {\n apiKey: \"\",\n email: \"\",\n};\n","import { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { ListToolsRequestSchema, CallToolRequestSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport { log } from \"./log\";\nimport { tools } from \"./tools\";\nimport { convertToTools, handlerResponseToCallResult } from \"./tool\";\n\nexport const server = new Server(\n { name: \"upstash\", version: \"0.1.0\" },\n { capabilities: { tools: {} } }\n);\n\nconst toolsList = convertToTools(tools);\n\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n log(\"Received list tools request\", toolsList);\n return { tools: toolsList };\n});\n\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const toolName = request.params.name;\n log(\"< received tool call:\", toolName, request.params.arguments);\n try {\n if (toolName in tools) {\n const tool = tools[toolName];\n const result = await tool.handler(request.params.arguments);\n\n const response = handlerResponseToCallResult(result);\n log(\"> tool result:\", response.content.map((item) => item.text).join(\"\\n\"));\n\n return response;\n }\n throw new Error(`Unknown tool: ${toolName}`);\n } catch (error) {\n const msg = error instanceof Error ? error.message : String(error);\n log(\"> error in tool call:\", msg);\n return {\n content: [\n {\n type: \"text\",\n text: `Error: ${msg}`,\n },\n ],\n isError: true,\n };\n }\n});\n","import { z } from \"zod\";\nimport { tool } from \".\";\n\nexport const utilTools = {\n timestamps_to_date: tool({\n description: `Use this tool to convert a timestamp to a human-readable date`,\n inputSchema: z.object({\n timestamps: z.array(z.number()).describe(\"Array of timestamps to convert\"),\n }),\n handler: async ({ timestamps }) => {\n return timestamps.map((timestamp) => new Date(timestamp).toUTCString());\n },\n }),\n}","import { z } from \"zod\";\nimport { json, tool } from \"..\";\nimport { http } from \"../../http\";\nimport type { RedisBackup } from \"./types\";\n\nexport const redisBackupTools = {\n redis_database_create_backup: tool({\n description: `Create a backup of a specific Upstash redis database.\nNOTE: Ask user to choose a name for the backup`,\n inputSchema: z.object({\n database_id: z.string().describe(\"The ID of the database to create a backup for.\"),\n backup_name: z.string().describe(\"A name for the backup.\"),\n }),\n handler: async ({ database_id, backup_name }) => {\n await http.post([\"v2/redis/create-backup\", database_id], {\n name: backup_name,\n });\n\n return \"OK\";\n },\n }),\n\n redis_database_delete_backup: tool({\n description: `Delete a backup of a specific Upstash redis database.`,\n inputSchema: z.object({\n database_id: z.string().describe(\"The ID of the database to delete a backup from.\"),\n backup_id: z.string().describe(\"The ID of the backup to delete.\"),\n }),\n handler: async ({ database_id, backup_id }) => {\n await http.delete([\"v2/redis/delete-backup\", database_id, backup_id]);\n\n return \"OK\";\n },\n }),\n\n redis_database_restore_backup: tool({\n description: `Restore a backup of a specific Upstash redis database. A backup can only be restored to the same database it was created from.`,\n inputSchema: z.object({\n database_id: z.string().describe(\"The ID of the database to restore a backup to.\"),\n backup_id: z.string().describe(\"The ID of the backup to restore.\"),\n }),\n handler: async ({ database_id, backup_id }) => {\n await http.post([\"v2/redis/restore-backup\", database_id], {\n backup_id,\n });\n\n return \"OK\";\n },\n }),\n\n redis_database_list_backups: tool({\n // TODO: Add explanation for fields\n // TODO: Is this in bytes?\n description: `List all backups of a specific Upstash redis database.`,\n inputSchema: z.object({\n database_id: z.string().describe(\"The ID of the database to list backups for.\"),\n }),\n handler: async ({ database_id }) => {\n const backups = await http.get<RedisBackup[]>([\"v2/redis/list-backup\", database_id]);\n\n return json(backups);\n },\n }),\n\n redis_database_set_daily_backup: tool({\n description: `Enable or disable daily backups for a specific Upstash redis database.`,\n inputSchema: z.object({\n database_id: z\n .string()\n .describe(\"The ID of the database to enable or disable daily backups for.\"),\n enable: z.boolean().describe(\"Whether to enable or disable daily backups.\"),\n }),\n handler: async ({ database_id, enable }) => {\n await http.patch([\n `v2/redis/${enable ? \"enable-dailybackup\" : \"disable-dailybackup\"}`,\n database_id,\n ]);\n\n return \"OK\";\n },\n }),\n};\n","import type { UpstashRequest } from \"./http\";\n\ntype Middleware = (req: UpstashRequest, next: () => Promise<unknown>) => Promise<unknown>;\n\nconst formatCreationTime = (obj: unknown) => {\n if (\n obj &&\n typeof obj === \"object\" &&\n \"creation_time\" in obj &&\n typeof obj.creation_time === \"number\" &&\n obj.creation_time > 0\n ) {\n obj.creation_time = new Date(obj.creation_time * 1000).toLocaleString();\n }\n};\n\nconst middlewares: Middleware[] = [\n // Middleware to format creation_time field to human readable format\n async (req, next) => {\n const res = await next();\n if (Array.isArray(res)) {\n for (const element of res) {\n formatCreationTime(element);\n }\n } else {\n formatCreationTime(res);\n }\n return res;\n },\n];\n\nexport const applyMiddlewares = async (\n req: UpstashRequest,\n func: (req: UpstashRequest) => Promise<unknown>\n) => {\n let next = async () => func(req);\n for (const middleware of middlewares.reverse()) {\n const prevNext = next;\n next = async () => middleware(req, prevNext);\n }\n return next();\n};\n","import { config } from \"./config\";\nimport { log } from \"./log\";\nimport { applyMiddlewares } from \"./middlewares\";\nimport type { RequestInit } from \"node-fetch\";\nimport fetch from \"node-fetch\";\n\nexport type UpstashRequest = {\n method: string;\n path?: string[] | string;\n /**\n * Request body will be serialized to json\n */\n body?: unknown;\n};\n\ntype HttpClientConfig = {\n baseUrl: string;\n};\n\nclass HttpClient {\n private readonly baseUrl: string;\n\n public constructor(config: HttpClientConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, \"\");\n }\n\n public async get<TResponse>(path: string[] | string): Promise<TResponse> {\n return this.requestWithMiddleware<TResponse>({ method: \"GET\", path });\n }\n\n public async post<TResponse>(path: string[] | string, body: unknown): Promise<TResponse> {\n return this.requestWithMiddleware<TResponse>({ method: \"POST\", path, body });\n }\n\n public async patch<TResponse>(path: string[] | string, body?: unknown): Promise<TResponse> {\n return this.requestWithMiddleware<TResponse>({ method: \"PATCH\", path, body });\n }\n\n public async delete<TResponse>(path: string[] | string): Promise<TResponse> {\n return this.requestWithMiddleware<TResponse>({ method: \"DELETE\", path });\n }\n\n private async requestWithMiddleware<TResponse>(req: UpstashRequest): Promise<TResponse> {\n const res = await applyMiddlewares(req, async (req) => {\n return this.request<TResponse>(req);\n });\n\n return res as TResponse;\n }\n\n private async request<TResponse>(req: UpstashRequest): Promise<TResponse> {\n if (!req.path) {\n req.path = [];\n } else if (typeof req.path === \"string\") {\n req.path = [req.path];\n }\n\n const url = [this.baseUrl, ...req.path].join(\"/\");\n const token = [config.email, config.apiKey].join(\":\");\n\n const init: RequestInit = {\n method: req.method,\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Basic ${Buffer.from(token).toString(\"base64\")}`,\n },\n };\n\n if (req.method !== \"GET\") {\n init.body = JSON.stringify(req.body);\n }\n\n log(\"Sending request\", {\n url,\n ...init,\n headers: { ...init.headers, Authorization: \"***\" },\n });\n\n // fetch is defined by isomorphic fetch\n const res = await fetch(url, init);\n if (!res.ok) {\n throw new Error(`Request failed (${res.status} ${res.statusText}): ${await res.text()}`);\n }\n return (await res.json()) as TResponse;\n }\n}\n\nexport const http = new HttpClient({ baseUrl: \"https://api.upstash.com\" });\n","import { z } from \"zod\";\nimport { json, tool } from \"..\";\nimport { log } from \"../../log\";\nimport fetch from \"node-fetch\";\n\ntype RedisCommandResult =\n | {\n result: unknown;\n }\n | {\n error: string;\n };\n\nexport const redisCommandTools = {\n redis_database_run_single_redis_command: tool({\n description: `Run a single Redis command on a specific Upstash redis database.\nNOTE: For discovery, use SCAN over KEYS. Use TYPE to get the type of a key.\nNOTE: SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]`,\n inputSchema: z.object({\n database_rest_url: z\n .string()\n .describe(\"The REST URL of the database. Example: https://***.upstash.io\"),\n database_rest_token: z.string().describe(\"The REST token of the database.\"),\n command: z\n .array(z.string())\n .describe(\"The Redis command to run. Example: ['SET', 'foo', 'bar', 'EX', 100]\"),\n }),\n\n handler: async ({ database_rest_url, database_rest_token, command }) => {\n const req = await fetch(database_rest_url, {\n method: \"POST\",\n body: JSON.stringify(command),\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${database_rest_token}`,\n },\n });\n\n const result = (await req.json()) as RedisCommandResult;\n\n log(\"command result:\", result);\n\n if (\"error\" in result) {\n throw new Error(\"Redis error: \" + result.error);\n }\n\n const isScanCommand = command[0].toLocaleLowerCase().includes(\"scan\");\n const messages = [json(result)];\n\n if (isScanCommand)\n messages.push(`NOTE: Use the returned cursor to get the next set of keys.\nNOTE: The result might be too large to be returned. If applicable, stop after the second SCAN command and ask the user if they want to continue.`);\n\n return messages;\n },\n }),\n\n redis_database_run_multiple_redis_commands: tool({\n description: `Run multiple Redis commands on a specific Upstash redis database`,\n inputSchema: z.object({\n database_rest_url: z\n .string()\n .describe(\"The REST URL of the database. Example: https://***.upstash.io\"),\n database_rest_token: z.string().describe(\"The REST token of the database.\"),\n commands: z\n .array(z.array(z.string()))\n .describe(\"The Redis commands to run. Example: [['SET', 'foo', 'bar'], ['GET', 'foo']]\"),\n }),\n\n handler: async ({ database_rest_url, database_rest_token, commands }) => {\n const req = await fetch(database_rest_url + \"/pipeline\", {\n method: \"POST\",\n body: JSON.stringify(commands),\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${database_rest_token}`,\n },\n });\n\n const result = (await req.json()) as RedisCommandResult[];\n\n log(\"commands result:\", result);\n\n if (result.some((r) => \"error\" in r)) {\n throw new Error(\"Some commands in the pipeline resulted in an error:\\n\" + json(result));\n }\n\n return json(result);\n },\n }),\n};\n","import { z } from \"zod\";\nimport { json, tool } from \"..\";\nimport { http } from \"../../http\";\nimport type { RedisDatabase, RedisUsageResponse, UsageData } from \"./types\";\nimport { pruneFalsy } from \"../../utils\";\n\nconst readRegionSchema = z.union([\n z.literal(\"us-east-1\"),\n z.literal(\"us-west-1\"),\n z.literal(\"us-west-2\"),\n z.literal(\"eu-west-1\"),\n z.literal(\"eu-central-1\"),\n z.literal(\"ap-southeast-1\"),\n z.literal(\"ap-southeast-2\"),\n z.literal(\"sa-east-1\"),\n]);\n\nconst GENERIC_DATABASE_NOTES =\n \"\\nNOTE: Don't show the database ID from the response to the user unless explicitly asked or needed.\\n\";\n\nexport const redisDbOpsTools = {\n redis_database_create_new: tool({\n description: `Create a new Upstash redis database. \nNOTE: Ask user for the region and name of the database.${GENERIC_DATABASE_NOTES}`,\n inputSchema: z.object({\n name: z.string().describe(\"Name of the database.\"),\n primary_region: readRegionSchema.describe(`Primary Region of the Global Database.`),\n read_regions: z\n .array(readRegionSchema)\n .optional()\n .describe(`Array of read regions of the db`),\n }),\n handler: async ({ name, primary_region, read_regions }) => {\n const newDb = await http.post<RedisDatabase>(\"v2/redis/database\", {\n name,\n region: \"global\",\n primary_region,\n read_regions,\n });\n\n return [\n json(newDb),\n `Upstash console url: https://console.upstash.com/redis/${newDb.database_id}`,\n ];\n },\n }),\n\n redis_database_delete: tool({\n description: `Delete an Upstash redis database.`,\n inputSchema: z.object({\n database_id: z.string().describe(\"The ID of the database to delete.\"),\n }),\n handler: async ({ database_id }) => {\n await http.delete([\"v2/redis/database\", database_id]);\n\n return \"Database deleted successfully.\";\n },\n }),\n\n redis_database_list_databases: tool({\n description: `List all Upstash redis databases. Only their names and ids.${GENERIC_DATABASE_NOTES}`,\n handler: async () => {\n const dbs = await http.get<RedisDatabase[]>(\"v2/redis/databases\");\n\n const messages = [\n json(\n dbs.map((db) => {\n const result = {\n database_id: db.database_id,\n database_name: db.database_name,\n state: db.state === \"active\" ? undefined : db.state,\n };\n return pruneFalsy(result);\n })\n ),\n ];\n\n if (dbs.length > 2)\n messages.push(\n `NOTE: If the user did not specify a database name for the next command, ask them to choose a database from the list.`\n );\n messages.push(\n \"NOTE: If the user wants to see dbs in another team, mention that they need to create a new management api key for that team and initialize MCP server with the newly created key.\"\n );\n\n return messages;\n },\n }),\n\n redis_database_get_details: tool({\n description: `Get further details of a specific Upstash redis database. Includes all details of the database including usage statistics.\ndb_disk_threshold: Total disk usage limit.\ndb_memory_threshold: Maximum memory usage.\ndb_daily_bandwidth_limit: Maximum daily network bandwidth usage.\ndb_request_limit: Total number of commands allowed.\nAll sizes are in bytes\n${GENERIC_DATABASE_NOTES}\n `,\n inputSchema: z.object({\n database_id: z.string().describe(\"The ID of the database to get details for.\"),\n }),\n handler: async ({ database_id }) => {\n const db = await http.get<RedisDatabase>([\"v2/redis/database\", database_id]);\n\n return json(db);\n },\n }),\n\n redis_database_update_regions: tool({\n description: `Update the read regions of an Upstash redis database.`,\n inputSchema: z.object({\n id: z.string().describe(\"The ID of your database.\"),\n read_regions: z\n .array(readRegionSchema)\n .describe(\n \"Array of the new read regions of the database. This will replace the old regions array. Available regions: us-east-1, us-west-1, us-west-2, eu-west-1, eu-central-1, ap-southeast-1, ap-southeast-2, sa-east-1\"\n ),\n }),\n handler: async ({ id, read_regions }) => {\n const updatedDb = await http.post<RedisDatabase>([\"v2/redis/update-regions\", id], {\n read_regions,\n });\n\n return json(updatedDb);\n },\n }),\n\n redis_database_reset_password: tool({\n description: `Reset the password of an Upstash redis database.`,\n inputSchema: z.object({\n id: z.string().describe(\"The ID of your database.\"),\n }),\n handler: async ({ id }) => {\n const updatedDb = await http.post<RedisDatabase>([\"v2/redis/reset-password\", id], {});\n\n return json(updatedDb);\n },\n }),\n\n redis_database_get_usage_last_5_days: tool({\n description: `Get PRECISE command count and bandwidth usage statistics of an Upstash redis database over the last 5 days. This is a precise stat, not an average.\nNOTE: Ask user first if they want to see stats for each database seperately or just for one.`,\n inputSchema: z.object({\n id: z.string().describe(\"The ID of your database.\"),\n }),\n handler: async ({ id }) => {\n const stats = await http.get<RedisUsageResponse>([\"v2/redis/stats\", `${id}?period=3h`]);\n\n return [\n json({\n days: stats.days,\n command_usage: stats.dailyrequests,\n bandwidth_usage: stats.bandwidths,\n }),\n `NOTE: Times are calculated according to UTC+0`,\n ];\n },\n }),\n\n redis_database_get_stats: tool({\n description: `Get SAMPLED usage statistics of an Upstash redis database over a period of time (1h, 3h, 12h, 1d, 3d, 7d). Use this to check for peak usages and latency problems.\nIncludes: read_latency_mean, write_latency_mean, keyspace, throughput (cmds/sec), diskusage\nNOTE: If the user does not specify which stat to get, use throughput as default.`,\n inputSchema: z.object({\n id: z.string().describe(\"The ID of your database.\"),\n period: z\n .union([\n z.literal(\"1h\"),\n z.literal(\"3h\"),\n z.literal(\"12h\"),\n z.literal(\"1d\"),\n z.literal(\"3d\"),\n z.literal(\"7d\"),\n ])\n .describe(\"The period of the stats.\"),\n type: z\n .union([\n z.literal(\"read_latency_mean\"),\n z.literal(\"write_latency_mean\"),\n z.literal(\"keyspace\").describe(\"Number of keys in db\"),\n z\n .literal(\"throughput\")\n .describe(\"commands per second (sampled), calculate area for estimated count\"),\n z.literal(\"diskusage\").describe(\"Current disk usage in bytes\"),\n ])\n .describe(\"The type of stat to get\"),\n }),\n handler: async ({ id, period, type }) => {\n const stats = await http.get<RedisUsageResponse>([\n \"v2/redis/stats\",\n `${id}?period=${period}`,\n ]);\n\n const stat = stats[type];\n\n if (!Array.isArray(stat))\n throw new Error(\n `Invalid key provided: ${type}. Valid keys are: ${Object.keys(stats).join(\", \")}`\n );\n\n return [\n JSON.stringify(parseUsageData(stat)),\n `NOTE: Use the timestamps_to_date tool to parse timestamps if needed`,\n `NOTE: Don't try to plot multiple stats in the same chart`,\n ];\n },\n }),\n};\n\nconst parseUsageData = (data: UsageData) => {\n if (!data) return \"NO DATA\";\n if (!Array.isArray(data)) return \"INVALID DATA\";\n if (data.length === 0 || data.length === 1) return \"NO DATA\";\n const filteredData = data.filter((d) => d.x && d.y);\n return {\n start: filteredData[0].x,\n // last one can be null, so use the second last\n // eslint-disable-next-line unicorn/prefer-at\n end: filteredData[filteredData.length - 1]?.x,\n data: data.map((d) => [new Date(d.x).getTime(), d.y]),\n };\n};\n","export function pruneFalsy(obj: Record<string, any>) {\n return Object.fromEntries(Object.entries(obj).filter(([, value]) => value));\n}\n","import { utilTools } from \"../utils\";\nimport { redisBackupTools } from \"./backup\";\nimport { redisCommandTools } from \"./command\";\nimport { redisDbOpsTools } from \"./db\";\n\nexport const redisTools = {\n ...redisDbOpsTools,\n ...redisBackupTools,\n ...redisCommandTools,\n ...utilTools,\n};\n","import type { ZodSchema } from \"zod\";\nimport type { CustomTool } from \"../tool\";\nimport { redisTools } from \"./redis\";\n\nexport const json = (json: unknown) =>\n typeof json === \"string\" ? json : JSON.stringify(json, null, 2);\n\nexport const tools = {\n ...redisTools,\n} as unknown as Record<string, CustomTool>;\n\n// Only used for type inference\nexport function tool<TSchema extends ZodSchema>(tool: CustomTool<TSchema>) {\n return tool;\n}\n","import type { CallToolResultSchema } from \"@modelcontextprotocol/sdk/types.js\";\nimport type { ZodSchema } from \"zod\";\nimport { z } from \"zod\";\nimport { zodToJsonSchema } from \"zod-to-json-schema\";\nimport { MAX_MESSAGE_LENGTH } from \"./settings\";\n\ntype HandlerResponse = string | string[] | z.infer<typeof CallToolResultSchema>;\n\nexport type CustomTool<TSchema extends ZodSchema = ZodSchema> = {\n description: string;\n\n /**\n * Zod schema for the input of the tool.\n */\n inputSchema?: TSchema;\n\n /**\n * The handler function for the tool.\n * @param input Parsed input according to the input schema.\n * @returns\n * If result is a string, it will be displayed as a single text block.\n * If result is an array of strings, each string will be displayed as a separate text block.\n * You can also return a CallToolResult object to display more complex content.\n */\n handler: (input: z.infer<TSchema>) => Promise<HandlerResponse>;\n};\n\nexport function handlerResponseToCallResult(\n response: HandlerResponse\n): z.infer<typeof CallToolResultSchema> {\n if (typeof response === \"string\" || Array.isArray(response)) {\n const array = Array.isArray(response) ? response : [response];\n\n // Truncate messages that are too long\n const truncatedArray = array.map((item) =>\n item.length > MAX_MESSAGE_LENGTH\n ? `${item.slice(0, MAX_MESSAGE_LENGTH)}... (MESSAGE TRUNCATED, MENTION THIS TO USER)`\n : item\n );\n\n return {\n content: truncatedArray.map((text) => ({ type: \"text\", text })),\n };\n } else return response;\n}\n\nfunction convertToJsonSchema(schema: ZodSchema) {\n const jsonSchema = zodToJsonSchema(schema);\n delete jsonSchema.$schema;\n\n // Remove additionalProperties field from all objects, as it's not needed\n const removeAdditionalProperties = (schema: any) => {\n if (schema.type !== \"object\") return;\n\n delete schema.additionalProperties;\n for (const value of Object.values(schema.properties)) {\n removeAdditionalProperties(value);\n }\n };\n\n removeAdditionalProperties(jsonSchema);\n\n return jsonSchema;\n}\n\nexport function convertToTools(tools: Record<string, CustomTool>) {\n return Object.entries(tools).map(([name, tool]) => ({\n name,\n description: tool.description,\n inputSchema: convertToJsonSchema(tool.inputSchema ?? z.object({})),\n }));\n}\n","export const MAX_MESSAGE_LENGTH = 8000;\n","import { http } from \"./http\";\nimport { log } from \"./log\";\n\nexport async function testConnection() {\n log(\"🧪 Testing connection to Upstash API\");\n // TODO: Test the connection to the Upstash API here\n // to check if the token is valid\n const res = await http.get<[]>(\"v2/teams\");\n\n if (!Array.isArray(res))\n throw new Error(\"Invalid response from Upstash API. Check your API key and email.\");\n\n log(\"✅ Connection to Upstash API is successful\");\n}\n"],"mappings":";;;AAEA,SAAS,4BAA4B;;;ACDrC,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,OAAO,QAAQ;AACf,OAAO,WAAW;;;ACJX,SAAS,OAAO,MAAiB;AACtC,QAAM,MAAM,WAAU,oBAAI,KAAK,GAAE,YAAY,CAAC,KAAK,KAAK,IAAI,CAAC,QAAS,OAAO,QAAQ,WAAW,MAAM,KAAK,UAAU,GAAG,CAAE,EAAE,KAAK,GAAG,CAAC;AAAA;AACrI,UAAQ,OAAO,MAAM,GAAG;AAC1B;;;ACHO,IAAM,SAAS;AAAA,EACpB,QAAQ;AAAA,EACR,OAAO;AACT;;;AFMA,IAAM,mBAAmB,KAAK;AAAA,EAC5B,GAAG,QAAQ;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,kBAAkB;AAExB,eAAsB,KAAK,EAAE,eAAe,GAA+B;AACzE,QAAM,UAAU,eAAe,SAAS,eAAe;AACvD,QAAM,gBAAgB,UAClB;AAAA,IACE,SAAS;AAAA,IACT,MAAM,CAAC,gBAAgB,OAAO,OAAO,OAAO,OAAO,MAAM;AAAA,EAC3D,IACA;AAAA,IACE,SAAS;AAAA,IACT,MAAM,CAAC,MAAM,uBAAuB,OAAO,OAAO,OAAO,OAAO,MAAM;AAAA,EACxE;AAEJ,QAAM,YAAY,KAAK,QAAQ,gBAAgB;AAC/C,MAAI,CAAC,GAAG,WAAW,SAAS,GAAG;AAC7B,QAAI,MAAM,KAAK,qCAAqC,CAAC;AACrD,OAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AAAA,EAC7C;AAEA,QAAM,iBAAiB,GAAG,WAAW,gBAAgB,IAChD,KAAK,MAAM,GAAG,aAAa,kBAAkB,MAAM,CAAC,IAGrD,EAAE,YAAY,CAAC,EAAE;AAErB,MAAI,qBAAoB,iDAAgB,eAAc,CAAC,IAAI;AACzD,QAAI,MAAM,OAAO,8CAA8C,CAAC;AAAA,EAClE;AAEA,MAAI,SAAS;AACX;AAAA,MACE,MAAM;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY;AAAA,IAChB,GAAG;AAAA,IACH,YAAY;AAAA,MACV,GAAG,eAAe;AAAA,MAClB,CAAC,eAAe,GAAG;AAAA,IACrB;AAAA,EACF;AAEA,KAAG,cAAc,kBAAkB,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC;AAErE;AAAA,IACE,MAAM;AAAA,MACJ,OACE,KAAK;AAAA,QACH;AAAA,UACE,CAAC,eAAe,GAAG;AAAA,QACrB;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,WAAW,OAAO,QAAQ,UAAU;AAAA,IAC1C;AAAA,EACF;AACA,MAAI,MAAM,MAAM,uBAAuB,iBAAiB,QAAQ,GAAG,QAAQ,GAAG,GAAG,CAAC,GAAG,CAAC;AACxF;;;AG9EA,SAAS,cAAc;AACvB,SAAS,wBAAwB,6BAA6B;;;ACD9D,SAAS,SAAS;AAGX,IAAM,YAAY;AAAA,EACvB,oBAAoB,KAAK;AAAA,IACvB,aAAa;AAAA,IACb,aAAa,EAAE,OAAO;AAAA,MACpB,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,gCAAgC;AAAA,IAC3E,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,WAAW,MAAM;AACjC,aAAO,WAAW,IAAI,CAAC,cAAc,IAAI,KAAK,SAAS,EAAE,YAAY,CAAC;AAAA,IACxE;AAAA,EACF,CAAC;AACH;;;ACbA,SAAS,KAAAA,UAAS;;;ACIlB,IAAM,qBAAqB,CAAC,QAAiB;AAC3C,MACE,OACA,OAAO,QAAQ,YACf,mBAAmB,OACnB,OAAO,IAAI,kBAAkB,YAC7B,IAAI,gBAAgB,GACpB;AACA,QAAI,gBAAgB,IAAI,KAAK,IAAI,gBAAgB,GAAI,EAAE,eAAe;AAAA,EACxE;AACF;AAEA,IAAM,cAA4B;AAAA;AAAA,EAEhC,OAAO,KAAK,SAAS;AACnB,UAAM,MAAM,MAAM,KAAK;AACvB,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,iBAAW,WAAW,KAAK;AACzB,2BAAmB,OAAO;AAAA,MAC5B;AAAA,IACF,OAAO;AACL,yBAAmB,GAAG;AAAA,IACxB;AACA,WAAO;AAAA,EACT;AACF;AAEO,IAAM,mBAAmB,OAC9B,KACA,SACG;AACH,MAAI,OAAO,YAAY,KAAK,GAAG;AAC/B,aAAW,cAAc,YAAY,QAAQ,GAAG;AAC9C,UAAM,WAAW;AACjB,WAAO,YAAY,WAAW,KAAK,QAAQ;AAAA,EAC7C;AACA,SAAO,KAAK;AACd;;;ACrCA,OAAO,WAAW;AAelB,IAAM,aAAN,MAAiB;AAAA,EAGR,YAAYC,SAA0B;AAC3C,SAAK,UAAUA,QAAO,QAAQ,QAAQ,OAAO,EAAE;AAAA,EACjD;AAAA,EAEA,MAAa,IAAeC,OAA6C;AACvE,WAAO,KAAK,sBAAiC,EAAE,QAAQ,OAAO,MAAAA,MAAK,CAAC;AAAA,EACtE;AAAA,EAEA,MAAa,KAAgBA,OAAyB,MAAmC;AACvF,WAAO,KAAK,sBAAiC,EAAE,QAAQ,QAAQ,MAAAA,OAAM,KAAK,CAAC;AAAA,EAC7E;AAAA,EAEA,MAAa,MAAiBA,OAAyB,MAAoC;AACzF,WAAO,KAAK,sBAAiC,EAAE,QAAQ,SAAS,MAAAA,OAAM,KAAK,CAAC;AAAA,EAC9E;AAAA,EAEA,MAAa,OAAkBA,OAA6C;AAC1E,WAAO,KAAK,sBAAiC,EAAE,QAAQ,UAAU,MAAAA,MAAK,CAAC;AAAA,EACzE;AAAA,EAEA,MAAc,sBAAiC,KAAyC;AACtF,UAAM,MAAM,MAAM,iBAAiB,KAAK,OAAOC,SAAQ;AACrD,aAAO,KAAK,QAAmBA,IAAG;AAAA,IACpC,CAAC;AAED,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,QAAmB,KAAyC;AACxE,QAAI,CAAC,IAAI,MAAM;AACb,UAAI,OAAO,CAAC;AAAA,IACd,WAAW,OAAO,IAAI,SAAS,UAAU;AACvC,UAAI,OAAO,CAAC,IAAI,IAAI;AAAA,IACtB;AAEA,UAAM,MAAM,CAAC,KAAK,SAAS,GAAG,IAAI,IAAI,EAAE,KAAK,GAAG;AAChD,UAAM,QAAQ,CAAC,OAAO,OAAO,OAAO,MAAM,EAAE,KAAK,GAAG;AAEpD,UAAMC,QAAoB;AAAA,MACxB,QAAQ,IAAI;AAAA,MACZ,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,eAAe,SAAS,OAAO,KAAK,KAAK,EAAE,SAAS,QAAQ,CAAC;AAAA,MAC/D;AAAA,IACF;AAEA,QAAI,IAAI,WAAW,OAAO;AACxB,MAAAA,MAAK,OAAO,KAAK,UAAU,IAAI,IAAI;AAAA,IACrC;AAEA,QAAI,mBAAmB;AAAA,MACrB;AAAA,MACA,GAAGA;AAAA,MACH,SAAS,EAAE,GAAGA,MAAK,SAAS,eAAe,MAAM;AAAA,IACnD,CAAC;AAGD,UAAM,MAAM,MAAM,MAAM,KAAKA,KAAI;AACjC,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,IAAI,MAAM,mBAAmB,IAAI,MAAM,IAAI,IAAI,UAAU,MAAM,MAAM,IAAI,KAAK,CAAC,EAAE;AAAA,IACzF;AACA,WAAQ,MAAM,IAAI,KAAK;AAAA,EACzB;AACF;AAEO,IAAM,OAAO,IAAI,WAAW,EAAE,SAAS,0BAA0B,CAAC;;;AFlFlE,IAAM,mBAAmB;AAAA,EAC9B,8BAA8B,KAAK;AAAA,IACjC,aAAa;AAAA;AAAA,IAEb,aAAaC,GAAE,OAAO;AAAA,MACpB,aAAaA,GAAE,OAAO,EAAE,SAAS,gDAAgD;AAAA,MACjF,aAAaA,GAAE,OAAO,EAAE,SAAS,wBAAwB;AAAA,IAC3D,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,aAAa,YAAY,MAAM;AAC/C,YAAM,KAAK,KAAK,CAAC,0BAA0B,WAAW,GAAG;AAAA,QACvD,MAAM;AAAA,MACR,CAAC;AAED,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAAA,EAED,8BAA8B,KAAK;AAAA,IACjC,aAAa;AAAA,IACb,aAAaA,GAAE,OAAO;AAAA,MACpB,aAAaA,GAAE,OAAO,EAAE,SAAS,iDAAiD;AAAA,MAClF,WAAWA,GAAE,OAAO,EAAE,SAAS,iCAAiC;AAAA,IAClE,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,aAAa,UAAU,MAAM;AAC7C,YAAM,KAAK,OAAO,CAAC,0BAA0B,aAAa,SAAS,CAAC;AAEpE,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAAA,EAED,+BAA+B,KAAK;AAAA,IAClC,aAAa;AAAA,IACb,aAAaA,GAAE,OAAO;AAAA,MACpB,aAAaA,GAAE,OAAO,EAAE,SAAS,gDAAgD;AAAA,MACjF,WAAWA,GAAE,OAAO,EAAE,SAAS,kCAAkC;AAAA,IACnE,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,aAAa,UAAU,MAAM;AAC7C,YAAM,KAAK,KAAK,CAAC,2BAA2B,WAAW,GAAG;AAAA,QACxD;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAAA,EAED,6BAA6B,KAAK;AAAA;AAAA;AAAA,IAGhC,aAAa;AAAA,IACb,aAAaA,GAAE,OAAO;AAAA,MACpB,aAAaA,GAAE,OAAO,EAAE,SAAS,6CAA6C;AAAA,IAChF,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,YAAY,MAAM;AAClC,YAAM,UAAU,MAAM,KAAK,IAAmB,CAAC,wBAAwB,WAAW,CAAC;AAEnF,aAAO,KAAK,OAAO;AAAA,IACrB;AAAA,EACF,CAAC;AAAA,EAED,iCAAiC,KAAK;AAAA,IACpC,aAAa;AAAA,IACb,aAAaA,GAAE,OAAO;AAAA,MACpB,aAAaA,GACV,OAAO,EACP,SAAS,gEAAgE;AAAA,MAC5E,QAAQA,GAAE,QAAQ,EAAE,SAAS,6CAA6C;AAAA,IAC5E,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,aAAa,OAAO,MAAM;AAC1C,YAAM,KAAK,MAAM;AAAA,QACf,YAAY,SAAS,uBAAuB,qBAAqB;AAAA,QACjE;AAAA,MACF,CAAC;AAED,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AACH;;;AGjFA,SAAS,KAAAC,UAAS;AAGlB,OAAOC,YAAW;AAUX,IAAM,oBAAoB;AAAA,EAC/B,yCAAyC,KAAK;AAAA,IAC5C,aAAa;AAAA;AAAA;AAAA,IAGb,aAAaC,GAAE,OAAO;AAAA,MACpB,mBAAmBA,GAChB,OAAO,EACP,SAAS,+DAA+D;AAAA,MAC3E,qBAAqBA,GAAE,OAAO,EAAE,SAAS,iCAAiC;AAAA,MAC1E,SAASA,GACN,MAAMA,GAAE,OAAO,CAAC,EAChB,SAAS,qEAAqE;AAAA,IACnF,CAAC;AAAA,IAED,SAAS,OAAO,EAAE,mBAAmB,qBAAqB,QAAQ,MAAM;AACtE,YAAM,MAAM,MAAMD,OAAM,mBAAmB;AAAA,QACzC,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU,OAAO;AAAA,QAC5B,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,UAAU,mBAAmB;AAAA,QAC9C;AAAA,MACF,CAAC;AAED,YAAM,SAAU,MAAM,IAAI,KAAK;AAE/B,UAAI,mBAAmB,MAAM;AAE7B,UAAI,WAAW,QAAQ;AACrB,cAAM,IAAI,MAAM,kBAAkB,OAAO,KAAK;AAAA,MAChD;AAEA,YAAM,gBAAgB,QAAQ,CAAC,EAAE,kBAAkB,EAAE,SAAS,MAAM;AACpE,YAAM,WAAW,CAAC,KAAK,MAAM,CAAC;AAE9B,UAAI;AACF,iBAAS,KAAK;AAAA,iJAC2H;AAE3I,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAAA,EAED,4CAA4C,KAAK;AAAA,IAC/C,aAAa;AAAA,IACb,aAAaC,GAAE,OAAO;AAAA,MACpB,mBAAmBA,GAChB,OAAO,EACP,SAAS,+DAA+D;AAAA,MAC3E,qBAAqBA,GAAE,OAAO,EAAE,SAAS,iCAAiC;AAAA,MAC1E,UAAUA,GACP,MAAMA,GAAE,MAAMA,GAAE,OAAO,CAAC,CAAC,EACzB,SAAS,6EAA6E;AAAA,IAC3F,CAAC;AAAA,IAED,SAAS,OAAO,EAAE,mBAAmB,qBAAqB,SAAS,MAAM;AACvE,YAAM,MAAM,MAAMD,OAAM,oBAAoB,aAAa;AAAA,QACvD,QAAQ;AAAA,QACR,MAAM,KAAK,UAAU,QAAQ;AAAA,QAC7B,SAAS;AAAA,UACP,gBAAgB;AAAA,UAChB,eAAe,UAAU,mBAAmB;AAAA,QAC9C;AAAA,MACF,CAAC;AAED,YAAM,SAAU,MAAM,IAAI,KAAK;AAE/B,UAAI,oBAAoB,MAAM;AAE9B,UAAI,OAAO,KAAK,CAAC,MAAM,WAAW,CAAC,GAAG;AACpC,cAAM,IAAI,MAAM,0DAA0D,KAAK,MAAM,CAAC;AAAA,MACxF;AAEA,aAAO,KAAK,MAAM;AAAA,IACpB;AAAA,EACF,CAAC;AACH;;;AC1FA,SAAS,KAAAE,UAAS;;;ACAX,SAAS,WAAW,KAA0B;AACnD,SAAO,OAAO,YAAY,OAAO,QAAQ,GAAG,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,MAAM,KAAK,CAAC;AAC5E;;;ADIA,IAAM,mBAAmBC,GAAE,MAAM;AAAA,EAC/BA,GAAE,QAAQ,WAAW;AAAA,EACrBA,GAAE,QAAQ,WAAW;AAAA,EACrBA,GAAE,QAAQ,WAAW;AAAA,EACrBA,GAAE,QAAQ,WAAW;AAAA,EACrBA,GAAE,QAAQ,cAAc;AAAA,EACxBA,GAAE,QAAQ,gBAAgB;AAAA,EAC1BA,GAAE,QAAQ,gBAAgB;AAAA,EAC1BA,GAAE,QAAQ,WAAW;AACvB,CAAC;AAED,IAAM,yBACJ;AAEK,IAAM,kBAAkB;AAAA,EAC7B,2BAA2B,KAAK;AAAA,IAC9B,aAAa;AAAA,yDACwC,sBAAsB;AAAA,IAC3E,aAAaA,GAAE,OAAO;AAAA,MACpB,MAAMA,GAAE,OAAO,EAAE,SAAS,uBAAuB;AAAA,MACjD,gBAAgB,iBAAiB,SAAS,wCAAwC;AAAA,MAClF,cAAcA,GACX,MAAM,gBAAgB,EACtB,SAAS,EACT,SAAS,iCAAiC;AAAA,IAC/C,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,MAAM,gBAAgB,aAAa,MAAM;AACzD,YAAM,QAAQ,MAAM,KAAK,KAAoB,qBAAqB;AAAA,QAChE;AAAA,QACA,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,MACF,CAAC;AAED,aAAO;AAAA,QACL,KAAK,KAAK;AAAA,QACV,0DAA0D,MAAM,WAAW;AAAA,MAC7E;AAAA,IACF;AAAA,EACF,CAAC;AAAA,EAED,uBAAuB,KAAK;AAAA,IAC1B,aAAa;AAAA,IACb,aAAaA,GAAE,OAAO;AAAA,MACpB,aAAaA,GAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,IACtE,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,YAAY,MAAM;AAClC,YAAM,KAAK,OAAO,CAAC,qBAAqB,WAAW,CAAC;AAEpD,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAAA,EAED,+BAA+B,KAAK;AAAA,IAClC,aAAa,8DAA8D,sBAAsB;AAAA,IACjG,SAAS,YAAY;AACnB,YAAM,MAAM,MAAM,KAAK,IAAqB,oBAAoB;AAEhE,YAAM,WAAW;AAAA,QACf;AAAA,UACE,IAAI,IAAI,CAAC,OAAO;AACd,kBAAM,SAAS;AAAA,cACb,aAAa,GAAG;AAAA,cAChB,eAAe,GAAG;AAAA,cAClB,OAAO,GAAG,UAAU,WAAW,SAAY,GAAG;AAAA,YAChD;AACA,mBAAO,WAAW,MAAM;AAAA,UAC1B,CAAC;AAAA,QACH;AAAA,MACF;AAEA,UAAI,IAAI,SAAS;AACf,iBAAS;AAAA,UACP;AAAA,QACF;AACF,eAAS;AAAA,QACP;AAAA,MACF;AAEA,aAAO;AAAA,IACT;AAAA,EACF,CAAC;AAAA,EAED,4BAA4B,KAAK;AAAA,IAC/B,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMf,sBAAsB;AAAA;AAAA,IAEpB,aAAaA,GAAE,OAAO;AAAA,MACpB,aAAaA,GAAE,OAAO,EAAE,SAAS,4CAA4C;AAAA,IAC/E,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,YAAY,MAAM;AAClC,YAAM,KAAK,MAAM,KAAK,IAAmB,CAAC,qBAAqB,WAAW,CAAC;AAE3E,aAAO,KAAK,EAAE;AAAA,IAChB;AAAA,EACF,CAAC;AAAA,EAED,+BAA+B,KAAK;AAAA,IAClC,aAAa;AAAA,IACb,aAAaA,GAAE,OAAO;AAAA,MACpB,IAAIA,GAAE,OAAO,EAAE,SAAS,0BAA0B;AAAA,MAClD,cAAcA,GACX,MAAM,gBAAgB,EACtB;AAAA,QACC;AAAA,MACF;AAAA,IACJ,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,IAAI,aAAa,MAAM;AACvC,YAAM,YAAY,MAAM,KAAK,KAAoB,CAAC,2BAA2B,EAAE,GAAG;AAAA,QAChF;AAAA,MACF,CAAC;AAED,aAAO,KAAK,SAAS;AAAA,IACvB;AAAA,EACF,CAAC;AAAA,EAED,+BAA+B,KAAK;AAAA,IAClC,aAAa;AAAA,IACb,aAAaA,GAAE,OAAO;AAAA,MACpB,IAAIA,GAAE,OAAO,EAAE,SAAS,0BAA0B;AAAA,IACpD,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,GAAG,MAAM;AACzB,YAAM,YAAY,MAAM,KAAK,KAAoB,CAAC,2BAA2B,EAAE,GAAG,CAAC,CAAC;AAEpF,aAAO,KAAK,SAAS;AAAA,IACvB;AAAA,EACF,CAAC;AAAA,EAED,sCAAsC,KAAK;AAAA,IACzC,aAAa;AAAA;AAAA,IAEb,aAAaA,GAAE,OAAO;AAAA,MACpB,IAAIA,GAAE,OAAO,EAAE,SAAS,0BAA0B;AAAA,IACpD,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,GAAG,MAAM;AACzB,YAAM,QAAQ,MAAM,KAAK,IAAwB,CAAC,kBAAkB,GAAG,EAAE,YAAY,CAAC;AAEtF,aAAO;AAAA,QACL,KAAK;AAAA,UACH,MAAM,MAAM;AAAA,UACZ,eAAe,MAAM;AAAA,UACrB,iBAAiB,MAAM;AAAA,QACzB,CAAC;AAAA,QACD;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AAAA,EAED,0BAA0B,KAAK;AAAA,IAC7B,aAAa;AAAA;AAAA;AAAA,IAGb,aAAaA,GAAE,OAAO;AAAA,MACpB,IAAIA,GAAE,OAAO,EAAE,SAAS,0BAA0B;AAAA,MAClD,QAAQA,GACL,MAAM;AAAA,QACLA,GAAE,QAAQ,IAAI;AAAA,QACdA,GAAE,QAAQ,IAAI;AAAA,QACdA,GAAE,QAAQ,KAAK;AAAA,QACfA,GAAE,QAAQ,IAAI;AAAA,QACdA,GAAE,QAAQ,IAAI;AAAA,QACdA,GAAE,QAAQ,IAAI;AAAA,MAChB,CAAC,EACA,SAAS,0BAA0B;AAAA,MACtC,MAAMA,GACH,MAAM;AAAA,QACLA,GAAE,QAAQ,mBAAmB;AAAA,QAC7BA,GAAE,QAAQ,oBAAoB;AAAA,QAC9BA,GAAE,QAAQ,UAAU,EAAE,SAAS,sBAAsB;AAAA,QACrDA,GACG,QAAQ,YAAY,EACpB,SAAS,mEAAmE;AAAA,QAC/EA,GAAE,QAAQ,WAAW,EAAE,SAAS,6BAA6B;AAAA,MAC/D,CAAC,EACA,SAAS,yBAAyB;AAAA,IACvC,CAAC;AAAA,IACD,SAAS,OAAO,EAAE,IAAI,QAAQ,KAAK,MAAM;AACvC,YAAM,QAAQ,MAAM,KAAK,IAAwB;AAAA,QAC/C;AAAA,QACA,GAAG,EAAE,WAAW,MAAM;AAAA,MACxB,CAAC;AAED,YAAM,OAAO,MAAM,IAAI;AAEvB,UAAI,CAAC,MAAM,QAAQ,IAAI;AACrB,cAAM,IAAI;AAAA,UACR,yBAAyB,IAAI,qBAAqB,OAAO,KAAK,KAAK,EAAE,KAAK,IAAI,CAAC;AAAA,QACjF;AAEF,aAAO;AAAA,QACL,KAAK,UAAU,eAAe,IAAI,CAAC;AAAA,QACnC;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,IAAM,iBAAiB,CAAC,SAAoB;AAjN5C;AAkNE,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO;AACjC,MAAI,KAAK,WAAW,KAAK,KAAK,WAAW,EAAG,QAAO;AACnD,QAAM,eAAe,KAAK,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,CAAC;AAClD,SAAO;AAAA,IACL,OAAO,aAAa,CAAC,EAAE;AAAA;AAAA;AAAA,IAGvB,MAAK,kBAAa,aAAa,SAAS,CAAC,MAApC,mBAAuC;AAAA,IAC5C,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC,EAAE,QAAQ,GAAG,EAAE,CAAC,CAAC;AAAA,EACtD;AACF;;;AExNO,IAAM,aAAa;AAAA,EACxB,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AAAA,EACH,GAAG;AACL;;;ACNO,IAAM,OAAO,CAACC,UACnB,OAAOA,UAAS,WAAWA,QAAO,KAAK,UAAUA,OAAM,MAAM,CAAC;AAEzD,IAAM,QAAQ;AAAA,EACnB,GAAG;AACL;AAGO,SAAS,KAAgCC,OAA2B;AACzE,SAAOA;AACT;;;ACZA,SAAS,KAAAC,UAAS;AAClB,SAAS,uBAAuB;;;ACHzB,IAAM,qBAAqB;;;AD2B3B,SAAS,4BACd,UACsC;AACtC,MAAI,OAAO,aAAa,YAAY,MAAM,QAAQ,QAAQ,GAAG;AAC3D,UAAM,QAAQ,MAAM,QAAQ,QAAQ,IAAI,WAAW,CAAC,QAAQ;AAG5D,UAAM,iBAAiB,MAAM;AAAA,MAAI,CAAC,SAChC,KAAK,SAAS,qBACV,GAAG,KAAK,MAAM,GAAG,kBAAkB,CAAC,kDACpC;AAAA,IACN;AAEA,WAAO;AAAA,MACL,SAAS,eAAe,IAAI,CAAC,UAAU,EAAE,MAAM,QAAQ,KAAK,EAAE;AAAA,IAChE;AAAA,EACF,MAAO,QAAO;AAChB;AAEA,SAAS,oBAAoB,QAAmB;AAC9C,QAAM,aAAa,gBAAgB,MAAM;AACzC,SAAO,WAAW;AAGlB,QAAM,6BAA6B,CAACC,YAAgB;AAClD,QAAIA,QAAO,SAAS,SAAU;AAE9B,WAAOA,QAAO;AACd,eAAW,SAAS,OAAO,OAAOA,QAAO,UAAU,GAAG;AACpD,iCAA2B,KAAK;AAAA,IAClC;AAAA,EACF;AAEA,6BAA2B,UAAU;AAErC,SAAO;AACT;AAEO,SAAS,eAAeC,QAAmC;AAChE,SAAO,OAAO,QAAQA,MAAK,EAAE,IAAI,CAAC,CAAC,MAAMC,KAAI,OAAO;AAAA,IAClD;AAAA,IACA,aAAaA,MAAK;AAAA,IAClB,aAAa,oBAAoBA,MAAK,eAAeC,GAAE,OAAO,CAAC,CAAC,CAAC;AAAA,EACnE,EAAE;AACJ;;;AVjEO,IAAM,SAAS,IAAI;AAAA,EACxB,EAAE,MAAM,WAAW,SAAS,QAAQ;AAAA,EACpC,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,EAAE;AAChC;AAEA,IAAM,YAAY,eAAe,KAAK;AAEtC,OAAO,kBAAkB,wBAAwB,YAAY;AAC3D,MAAI,+BAA+B,SAAS;AAC5C,SAAO,EAAE,OAAO,UAAU;AAC5B,CAAC;AAED,OAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,QAAM,WAAW,QAAQ,OAAO;AAChC,MAAI,yBAAyB,UAAU,QAAQ,OAAO,SAAS;AAC/D,MAAI;AACF,QAAI,YAAY,OAAO;AACrB,YAAMC,QAAO,MAAM,QAAQ;AAC3B,YAAM,SAAS,MAAMA,MAAK,QAAQ,QAAQ,OAAO,SAAS;AAE1D,YAAM,WAAW,4BAA4B,MAAM;AACnD,UAAI,kBAAkB,SAAS,QAAQ,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,KAAK,IAAI,CAAC;AAE1E,aAAO;AAAA,IACT;AACA,UAAM,IAAI,MAAM,iBAAiB,QAAQ,EAAE;AAAA,EAC7C,SAAS,OAAO;AACd,UAAM,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACjE,QAAI,yBAAyB,GAAG;AAChC,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,UAAU,GAAG;AAAA,QACrB;AAAA,MACF;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AACF,CAAC;;;AJtCD,OAAO;;;AgBJP,eAAsB,iBAAiB;AACrC,MAAI,6CAAsC;AAG1C,QAAM,MAAM,MAAM,KAAK,IAAQ,UAAU;AAEzC,MAAI,CAAC,MAAM,QAAQ,GAAG;AACpB,UAAM,IAAI,MAAM,kEAAkE;AAEpF,MAAI,gDAA2C;AACjD;;;AhBHA,QAAQ,GAAG,qBAAqB,CAAC,UAAU;AACzC,MAAI,uBAAuB,MAAM,MAAM,MAAM,SAAS,MAAM,KAAK;AACnE,CAAC;AAED,QAAQ,GAAG,sBAAsB,CAAC,UAAU;AAC1C,MAAI,iBAAiB,MAAO,KAAI,wBAAwB,MAAM,MAAM,MAAM,SAAS,MAAM,KAAK;AAAA,MACzF,KAAI,wBAAwB,KAAK;AACxC,CAAC;AAED,IAAM,YAAY,QAAQ,IAAI;AAC9B,IAAM,WAAW,QAAQ,IAAI;AAE7B,IAAM,gBAAgB;AACtB,IAAM,YAAY;AAClB,IAAM,aAAa;AAEnB,SAAS,iBAAiB;AACxB,QAAM,CAAC,KAAK,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,CAAC;AAC3C,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,aAAa;AAAA,WAC9B,QAAQ,QAAQ;AACvB,UAAM,CAAC,OAAO,QAAQ,GAAG,IAAI,IAAI;AACjC,UAAM,cAAc,UAAU;AAC9B,UAAM,aAAa,SAAS;AAE5B,QAAI,KAAK,SAAS,EAAG,OAAM,IAAI,MAAM,uBAAuB,UAAU,EAAE;AACxE,QAAI,CAAC,YAAa,OAAM,IAAI,MAAM,oBAAoB,UAAU,EAAE;AAClE,QAAI,CAAC,WAAY,OAAM,IAAI,MAAM,kBAAkB,UAAU,EAAE;AAE/D,WAAO,SAAS;AAChB,WAAO,QAAQ;AAEf,mBAAe;AAEf,SAAK;AAAA,MACH,gBAAgB,QAAQ,KAAK,CAAC;AAAA,IAChC,CAAC;AAAA,EACH,WAAW,QAAQ,OAAO;AACxB,UAAM,CAAC,OAAO,QAAQ,GAAG,IAAI,IAAI;AACjC,UAAM,cAAc,UAAU;AAC9B,UAAM,aAAa,SAAS;AAE5B,QAAI,CAAC,YAAa,OAAM,IAAI,MAAM,oBAAoB,SAAS,EAAE;AACjE,QAAI,CAAC,WAAY,OAAM,IAAI,MAAM,kBAAkB,SAAS,EAAE;AAC9D,QAAI,KAAK,SAAS,EAAG,OAAM,IAAI,MAAM,uBAAuB,SAAS,EAAE;AACvE,QAAI,qBAAqB;AAEzB,WAAO,SAAS;AAChB,WAAO,QAAQ;AAEf,mBAAe;AAGf,SAAK,EAAE,MAAM,CAAC,UAAU;AACtB,cAAQ,MAAM,iBAAiB,KAAK;AACpC,cAAQ,KAAK,CAAC;AAAA,IAChB,CAAC;AAAA,EACH,OAAO;AACL,UAAM,IAAI,MAAM,oBAAoB,GAAG,+BAA+B,aAAa,EAAE;AAAA,EACvF;AACF;AAEA,eAAe,OAAO;AACpB,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAChC;AAEA,IAAI;AACF,iBAAe;AACjB,SAAS,OAAO;AACd,MAAI,EAAE,iBAAiB,OAAQ,OAAM;AACrC,UAAQ,MAAM,MAAM,OAAO;AAC3B,UAAQ,KAAK,CAAC;AAChB;","names":["z","config","path","req","init","z","z","fetch","z","z","z","json","tool","z","schema","tools","tool","z","tool"]}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "version": "0.0.7",
3
+ "name": "@upstash/mcp-server",
4
+ "description": "MCP server for Upstash",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "bin": {
8
+ "mcp-server": "./dist/index.js"
9
+ },
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/upstash/mcp-server.git"
13
+ },
14
+ "bugs": {
15
+ "url": "https://github.com/upstash/mcp-server/issues"
16
+ },
17
+ "scripts": {
18
+ "build": "tsup && node -e \"require('fs').chmodSync('dist/index.js', '755')\"",
19
+ "watch": "npm run build && tsup --watch",
20
+ "inspector": "npx @modelcontextprotocol/inspector node --enable-source-maps dist/index.js run",
21
+ "setup": "node ./dist/index.js init",
22
+ "logs": "tail -n 20 -f ~/Library/Logs/Claude/mcp*.log",
23
+ "test": "bun test",
24
+ "fmt": "prettier --write .",
25
+ "lint": "eslint \"**/*.{js,ts,tsx}\" --quiet --fix"
26
+ },
27
+ "files": [
28
+ "dist"
29
+ ],
30
+ "devDependencies": {
31
+ "@typescript-eslint/eslint-plugin": "8.4.0",
32
+ "@typescript-eslint/parser": "8.4.0",
33
+ "eslint": "9.10.0",
34
+ "eslint-plugin-unicorn": "55.0.0",
35
+ "prettier": "^3.4.2",
36
+ "tsup": "^8.3.5"
37
+ },
38
+ "dependencies": {
39
+ "@modelcontextprotocol/sdk": "^1.0.3",
40
+ "@types/node": "15",
41
+ "chalk": "^5.3.0",
42
+ "dotenv": "^16.4.7",
43
+ "node-fetch": "^3.3.2",
44
+ "zod": "^3.24.1",
45
+ "zod-to-json-schema": "^3.24.1"
46
+ }
47
+ }