├── .gitattributes
├── .gitignore
├── Dockerfile
├── LICENSE
├── README.md
├── docker-compose.yml
├── package-lock.json
├── package.json
├── smithery.yaml
├── src
├── client.ts
├── fetch-capabilities.ts
├── fetch-metamcp.ts
├── fetch-tools.ts
├── index.ts
├── mcp-proxy.ts
├── report-tools.ts
├── sessions.ts
├── sse.ts
├── streamable-http.ts
├── tool-logs.ts
└── utils.ts
└── tsconfig.json
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the public line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # public
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 |
132 | dist/**/*
133 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use the official uv Debian image as base
2 | FROM ghcr.io/astral-sh/uv:debian
3 |
4 | # Install Node.js and npm
5 | RUN apt-get update && apt-get install -y \
6 | curl \
7 | gnupg \
8 | && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
9 | && apt-get install -y nodejs \
10 | && apt-get clean \
11 | && rm -rf /var/lib/apt/lists/*
12 |
13 | # Verify Node.js and npm installation
14 | RUN node --version && npm --version
15 |
16 | # Verify uv is installed correctly
17 | RUN uv --version
18 |
19 | # Verify npx is available
20 | RUN npx --version || npm install -g npx
21 |
22 | # Set the working directory
23 | WORKDIR /app
24 |
25 | # Copy package files
26 | COPY package*.json ./
27 |
28 | # Install dependencies
29 | RUN npm ci
30 |
31 | # Copy the rest of the application
32 | COPY . .
33 |
34 | # Build the application
35 | RUN npm run build
36 |
37 | # Set environment variables
38 | ENV NODE_ENV=production
39 |
40 | # Expose the application port
41 | EXPOSE 3000
42 |
43 | # Run the application
44 | ENTRYPOINT ["node", "dist/index.js"]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [2025] [Jincheng Zhang, MetaMCP, metamcp.com, https://github.com/metatool-ai/metatool-app]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MetaMCP MCP Server
2 |
3 | [https://metamcp.com](https://metamcp.com): The One MCP to manage all your MCPs
4 |
5 | MetaMCP MCP Server is a proxy server that joins multiple MCP servers into one. It fetches tool/prompt/resource configurations from MetaMCP App and routes tool/prompt/resource requests to the correct underlying server.
6 |
7 | [](https://smithery.ai/server/@metatool-ai/mcp-server-metamcp)
8 |
9 |
10 |
11 |
12 |
13 | MetaMCP App repo: https://github.com/metatool-ai/metatool-app
14 |
15 | ## Installation
16 |
17 | ### Installing via Smithery
18 |
19 | Sometimes Smithery works (confirmed in Windsurf locally) but sometimes it is unstable because MetaMCP is special that it runs other MCPs on top of it. Please consider using manual installation if it doesn't work instead.
20 |
21 | To install MetaMCP MCP Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@metatool-ai/mcp-server-metamcp):
22 |
23 | ```bash
24 | npx -y @smithery/cli install @metatool-ai/mcp-server-metamcp --client claude
25 | ```
26 |
27 | ### Manual Installation
28 |
29 | ```bash
30 | export METAMCP_API_KEY=
31 | npx -y @metamcp/mcp-server-metamcp@latest
32 | ```
33 |
34 | ```json
35 | {
36 | "mcpServers": {
37 | "MetaMCP": {
38 | "command": "npx",
39 | "args": ["-y", "@metamcp/mcp-server-metamcp@latest"],
40 | "env": {
41 | "METAMCP_API_KEY": ""
42 | }
43 | }
44 | }
45 | }
46 | ```
47 |
48 | ## Usage
49 |
50 | ### Using as a stdio server (default)
51 |
52 | ```bash
53 | mcp-server-metamcp --metamcp-api-key
54 | ```
55 |
56 | ### Using as an SSE server
57 |
58 | ```bash
59 | mcp-server-metamcp --metamcp-api-key --transport sse --port 12006
60 | ```
61 |
62 | With the SSE transport option, the server will start an Express.js web server that listens for SSE connections on the `/sse` endpoint and accepts messages on the `/messages` endpoint.
63 |
64 | ### Using as a Streamable HTTP server
65 |
66 | ```bash
67 | mcp-server-metamcp --metamcp-api-key --transport streamable-http --port 12006
68 | ```
69 |
70 | With the Streamable HTTP transport option, the server will start an Express.js web server that handles HTTP requests. You can optionally use `--stateless` mode for stateless operation.
71 |
72 | ### Using with Docker
73 |
74 | When running the server inside a Docker container and connecting to services on the host machine, use the `--use-docker-host` option to automatically transform localhost URLs:
75 |
76 | ```bash
77 | mcp-server-metamcp --metamcp-api-key --transport sse --port 12006 --use-docker-host
78 | ```
79 |
80 | This will transform any localhost or 127.0.0.1 URLs to `host.docker.internal`, allowing the container to properly connect to services running on the host.
81 |
82 | ### Configuring stderr handling
83 |
84 | For STDIO transport, you can control how stderr is handled from child MCP processes:
85 |
86 | ```bash
87 | # Use inherit to see stderr output from child processes
88 | mcp-server-metamcp --metamcp-api-key --stderr inherit
89 |
90 | # Use pipe to capture stderr (default is ignore)
91 | mcp-server-metamcp --metamcp-api-key --stderr pipe
92 |
93 | # Or set via environment variable
94 | METAMCP_STDERR=inherit mcp-server-metamcp --metamcp-api-key
95 | ```
96 |
97 | Available stderr options:
98 | - `ignore` (default): Ignore stderr output from child processes
99 | - `inherit`: Pass through stderr from child processes to the parent
100 | - `pipe`: Capture stderr in a pipe for processing
101 | - `overlapped`: Use overlapped I/O (Windows-specific)
102 |
103 | ### Command Line Options
104 |
105 | ```
106 | Options:
107 | --metamcp-api-key API key for MetaMCP (can also be set via METAMCP_API_KEY env var)
108 | --metamcp-api-base-url Base URL for MetaMCP API (can also be set via METAMCP_API_BASE_URL env var)
109 | --report Fetch all MCPs, initialize clients, and report tools to MetaMCP API
110 | --transport Transport type to use (stdio, sse, or streamable-http) (default: "stdio")
111 | --port Port to use for SSE or Streamable HTTP transport, defaults to 12006 (default: "12006")
112 | --require-api-auth Require API key in SSE or Streamable HTTP URL path
113 | --stateless Use stateless mode for Streamable HTTP transport
114 | --use-docker-host Transform localhost URLs to use host.docker.internal (can also be set via USE_DOCKER_HOST env var)
115 | --stderr Stderr handling for STDIO transport (overlapped, pipe, ignore, inherit) (default: "ignore")
116 | -h, --help display help for command
117 | ```
118 |
119 | ## Environment Variables
120 |
121 | - `METAMCP_API_KEY`: API key for MetaMCP
122 | - `METAMCP_API_BASE_URL`: Base URL for MetaMCP API
123 | - `USE_DOCKER_HOST`: When set to "true", transforms localhost URLs to host.docker.internal for Docker compatibility
124 | - `METAMCP_STDERR`: Stderr handling for STDIO transport (overlapped, pipe, ignore, inherit). Defaults to "ignore"
125 |
126 | ## Development
127 |
128 | ```bash
129 | # Install dependencies
130 | npm install
131 |
132 | # Build the application
133 | npm run build
134 |
135 | # Watch for changes
136 | npm run watch
137 | ```
138 |
139 | ## Highlights
140 |
141 | - Compatible with ANY MCP Client
142 | - Multi-Workspaces layer enables you to switch to another set of MCP configs within one-click.
143 | - GUI dynamic updates of MCP configs.
144 | - Namespace isolation for joined MCPs.
145 |
146 | ## Architecture Overview
147 |
148 | ```mermaid
149 | sequenceDiagram
150 | participant MCPClient as MCP Client (e.g. Claude Desktop)
151 | participant MetaMCP-mcp-server as MetaMCP MCP Server
152 | participant MetaMCPApp as MetaMCP App
153 | participant MCPServers as Installed MCP Servers in Metatool App
154 |
155 | MCPClient ->> MetaMCP-mcp-server: Request list tools
156 | MetaMCP-mcp-server ->> MetaMCPApp: Get tools configuration & status
157 | MetaMCPApp ->> MetaMCP-mcp-server: Return tools configuration & status
158 |
159 | loop For each listed MCP Server
160 | MetaMCP-mcp-server ->> MCPServers: Request list_tools
161 | MCPServers ->> MetaMCP-mcp-server: Return list of tools
162 | end
163 |
164 | MetaMCP-mcp-server ->> MetaMCP-mcp-server: Aggregate tool lists
165 | MetaMCP-mcp-server ->> MCPClient: Return aggregated list of tools
166 |
167 | MCPClient ->> MetaMCP-mcp-server: Call tool
168 | MetaMCP-mcp-server ->> MCPServers: call_tool to target MCP Server
169 | MCPServers ->> MetaMCP-mcp-server: Return tool response
170 | MetaMCP-mcp-server ->> MCPClient: Return tool response
171 | ```
172 |
173 | ## Credits
174 |
175 | - Inspirations and some code (refactored in this project) from https://github.com/adamwattis/mcp-proxy-server/
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | services:
2 | mcp-server:
3 | build:
4 | context: .
5 | dockerfile: Dockerfile
6 | ports:
7 | - "3000:3000"
8 | env_file:
9 | - .env.production.local
10 | entrypoint: ["/bin/bash"]
11 | command: ["-c", "uvx --version && echo 'uvx is working!' && tail -f /dev/null"]
12 | healthcheck:
13 | test: ["CMD", "ps", "aux", "|", "grep", "tail"]
14 | interval: 30s
15 | timeout: 10s
16 | retries: 3
17 | environment:
18 | - NODE_ENV=production
19 | restart: unless-stopped
20 | # Add any additional environment variables or command arguments here
21 | # command: --metamcp-api-key your-api-key --metamcp-api-base-url your-base-url
22 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@metamcp/mcp-server-metamcp",
3 | "version": "0.6.5",
4 | "lockfileVersion": 3,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "@metamcp/mcp-server-metamcp",
9 | "version": "0.6.5",
10 | "license": "Apache-2.0",
11 | "dependencies": {
12 | "@modelcontextprotocol/sdk": "^1.11.4",
13 | "axios": "^1.7.9",
14 | "commander": "^13.1.0",
15 | "express": "^4.21.2",
16 | "zod": "^3.24.2"
17 | },
18 | "bin": {
19 | "mcp-server-metamcp": "dist/index.js"
20 | },
21 | "devDependencies": {
22 | "@types/express": "^5.0.1",
23 | "@types/node": "^22.13.4",
24 | "dotenv-cli": "^8.0.0",
25 | "shx": "^0.3.4",
26 | "typescript": "^5.8.2"
27 | }
28 | },
29 | "node_modules/@modelcontextprotocol/sdk": {
30 | "version": "1.11.4",
31 | "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.4.tgz",
32 | "integrity": "sha512-OTbhe5slIjiOtLxXhKalkKGhIQrwvhgCDs/C2r8kcBTy5HR/g43aDQU0l7r8O0VGbJPTNJvDc7ZdQMdQDJXmbw==",
33 | "license": "MIT",
34 | "dependencies": {
35 | "ajv": "^8.17.1",
36 | "content-type": "^1.0.5",
37 | "cors": "^2.8.5",
38 | "cross-spawn": "^7.0.5",
39 | "eventsource": "^3.0.2",
40 | "express": "^5.0.1",
41 | "express-rate-limit": "^7.5.0",
42 | "pkce-challenge": "^5.0.0",
43 | "raw-body": "^3.0.0",
44 | "zod": "^3.23.8",
45 | "zod-to-json-schema": "^3.24.1"
46 | },
47 | "engines": {
48 | "node": ">=18"
49 | }
50 | },
51 | "node_modules/@modelcontextprotocol/sdk/node_modules/accepts": {
52 | "version": "2.0.0",
53 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz",
54 | "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==",
55 | "license": "MIT",
56 | "dependencies": {
57 | "mime-types": "^3.0.0",
58 | "negotiator": "^1.0.0"
59 | },
60 | "engines": {
61 | "node": ">= 0.6"
62 | }
63 | },
64 | "node_modules/@modelcontextprotocol/sdk/node_modules/body-parser": {
65 | "version": "2.2.0",
66 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz",
67 | "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==",
68 | "license": "MIT",
69 | "dependencies": {
70 | "bytes": "^3.1.2",
71 | "content-type": "^1.0.5",
72 | "debug": "^4.4.0",
73 | "http-errors": "^2.0.0",
74 | "iconv-lite": "^0.6.3",
75 | "on-finished": "^2.4.1",
76 | "qs": "^6.14.0",
77 | "raw-body": "^3.0.0",
78 | "type-is": "^2.0.0"
79 | },
80 | "engines": {
81 | "node": ">=18"
82 | }
83 | },
84 | "node_modules/@modelcontextprotocol/sdk/node_modules/content-disposition": {
85 | "version": "1.0.0",
86 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
87 | "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==",
88 | "license": "MIT",
89 | "dependencies": {
90 | "safe-buffer": "5.2.1"
91 | },
92 | "engines": {
93 | "node": ">= 0.6"
94 | }
95 | },
96 | "node_modules/@modelcontextprotocol/sdk/node_modules/cookie-signature": {
97 | "version": "1.2.2",
98 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz",
99 | "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==",
100 | "license": "MIT",
101 | "engines": {
102 | "node": ">=6.6.0"
103 | }
104 | },
105 | "node_modules/@modelcontextprotocol/sdk/node_modules/debug": {
106 | "version": "4.4.0",
107 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
108 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
109 | "license": "MIT",
110 | "dependencies": {
111 | "ms": "^2.1.3"
112 | },
113 | "engines": {
114 | "node": ">=6.0"
115 | },
116 | "peerDependenciesMeta": {
117 | "supports-color": {
118 | "optional": true
119 | }
120 | }
121 | },
122 | "node_modules/@modelcontextprotocol/sdk/node_modules/express": {
123 | "version": "5.1.0",
124 | "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
125 | "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
126 | "license": "MIT",
127 | "dependencies": {
128 | "accepts": "^2.0.0",
129 | "body-parser": "^2.2.0",
130 | "content-disposition": "^1.0.0",
131 | "content-type": "^1.0.5",
132 | "cookie": "^0.7.1",
133 | "cookie-signature": "^1.2.1",
134 | "debug": "^4.4.0",
135 | "encodeurl": "^2.0.0",
136 | "escape-html": "^1.0.3",
137 | "etag": "^1.8.1",
138 | "finalhandler": "^2.1.0",
139 | "fresh": "^2.0.0",
140 | "http-errors": "^2.0.0",
141 | "merge-descriptors": "^2.0.0",
142 | "mime-types": "^3.0.0",
143 | "on-finished": "^2.4.1",
144 | "once": "^1.4.0",
145 | "parseurl": "^1.3.3",
146 | "proxy-addr": "^2.0.7",
147 | "qs": "^6.14.0",
148 | "range-parser": "^1.2.1",
149 | "router": "^2.2.0",
150 | "send": "^1.1.0",
151 | "serve-static": "^2.2.0",
152 | "statuses": "^2.0.1",
153 | "type-is": "^2.0.1",
154 | "vary": "^1.1.2"
155 | },
156 | "engines": {
157 | "node": ">= 18"
158 | },
159 | "funding": {
160 | "type": "opencollective",
161 | "url": "https://opencollective.com/express"
162 | }
163 | },
164 | "node_modules/@modelcontextprotocol/sdk/node_modules/finalhandler": {
165 | "version": "2.1.0",
166 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz",
167 | "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==",
168 | "license": "MIT",
169 | "dependencies": {
170 | "debug": "^4.4.0",
171 | "encodeurl": "^2.0.0",
172 | "escape-html": "^1.0.3",
173 | "on-finished": "^2.4.1",
174 | "parseurl": "^1.3.3",
175 | "statuses": "^2.0.1"
176 | },
177 | "engines": {
178 | "node": ">= 0.8"
179 | }
180 | },
181 | "node_modules/@modelcontextprotocol/sdk/node_modules/fresh": {
182 | "version": "2.0.0",
183 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz",
184 | "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==",
185 | "license": "MIT",
186 | "engines": {
187 | "node": ">= 0.8"
188 | }
189 | },
190 | "node_modules/@modelcontextprotocol/sdk/node_modules/iconv-lite": {
191 | "version": "0.6.3",
192 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
193 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
194 | "license": "MIT",
195 | "dependencies": {
196 | "safer-buffer": ">= 2.1.2 < 3.0.0"
197 | },
198 | "engines": {
199 | "node": ">=0.10.0"
200 | }
201 | },
202 | "node_modules/@modelcontextprotocol/sdk/node_modules/media-typer": {
203 | "version": "1.1.0",
204 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz",
205 | "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==",
206 | "license": "MIT",
207 | "engines": {
208 | "node": ">= 0.8"
209 | }
210 | },
211 | "node_modules/@modelcontextprotocol/sdk/node_modules/merge-descriptors": {
212 | "version": "2.0.0",
213 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz",
214 | "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==",
215 | "license": "MIT",
216 | "engines": {
217 | "node": ">=18"
218 | },
219 | "funding": {
220 | "url": "https://github.com/sponsors/sindresorhus"
221 | }
222 | },
223 | "node_modules/@modelcontextprotocol/sdk/node_modules/mime-db": {
224 | "version": "1.54.0",
225 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
226 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
227 | "license": "MIT",
228 | "engines": {
229 | "node": ">= 0.6"
230 | }
231 | },
232 | "node_modules/@modelcontextprotocol/sdk/node_modules/mime-types": {
233 | "version": "3.0.1",
234 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz",
235 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==",
236 | "license": "MIT",
237 | "dependencies": {
238 | "mime-db": "^1.54.0"
239 | },
240 | "engines": {
241 | "node": ">= 0.6"
242 | }
243 | },
244 | "node_modules/@modelcontextprotocol/sdk/node_modules/ms": {
245 | "version": "2.1.3",
246 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
247 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
248 | "license": "MIT"
249 | },
250 | "node_modules/@modelcontextprotocol/sdk/node_modules/negotiator": {
251 | "version": "1.0.0",
252 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
253 | "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==",
254 | "license": "MIT",
255 | "engines": {
256 | "node": ">= 0.6"
257 | }
258 | },
259 | "node_modules/@modelcontextprotocol/sdk/node_modules/qs": {
260 | "version": "6.14.0",
261 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
262 | "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
263 | "license": "BSD-3-Clause",
264 | "dependencies": {
265 | "side-channel": "^1.1.0"
266 | },
267 | "engines": {
268 | "node": ">=0.6"
269 | },
270 | "funding": {
271 | "url": "https://github.com/sponsors/ljharb"
272 | }
273 | },
274 | "node_modules/@modelcontextprotocol/sdk/node_modules/send": {
275 | "version": "1.2.0",
276 | "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz",
277 | "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==",
278 | "license": "MIT",
279 | "dependencies": {
280 | "debug": "^4.3.5",
281 | "encodeurl": "^2.0.0",
282 | "escape-html": "^1.0.3",
283 | "etag": "^1.8.1",
284 | "fresh": "^2.0.0",
285 | "http-errors": "^2.0.0",
286 | "mime-types": "^3.0.1",
287 | "ms": "^2.1.3",
288 | "on-finished": "^2.4.1",
289 | "range-parser": "^1.2.1",
290 | "statuses": "^2.0.1"
291 | },
292 | "engines": {
293 | "node": ">= 18"
294 | }
295 | },
296 | "node_modules/@modelcontextprotocol/sdk/node_modules/serve-static": {
297 | "version": "2.2.0",
298 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz",
299 | "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==",
300 | "license": "MIT",
301 | "dependencies": {
302 | "encodeurl": "^2.0.0",
303 | "escape-html": "^1.0.3",
304 | "parseurl": "^1.3.3",
305 | "send": "^1.2.0"
306 | },
307 | "engines": {
308 | "node": ">= 18"
309 | }
310 | },
311 | "node_modules/@modelcontextprotocol/sdk/node_modules/type-is": {
312 | "version": "2.0.1",
313 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz",
314 | "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==",
315 | "license": "MIT",
316 | "dependencies": {
317 | "content-type": "^1.0.5",
318 | "media-typer": "^1.1.0",
319 | "mime-types": "^3.0.0"
320 | },
321 | "engines": {
322 | "node": ">= 0.6"
323 | }
324 | },
325 | "node_modules/@types/body-parser": {
326 | "version": "1.19.5",
327 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
328 | "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
329 | "dev": true,
330 | "license": "MIT",
331 | "dependencies": {
332 | "@types/connect": "*",
333 | "@types/node": "*"
334 | }
335 | },
336 | "node_modules/@types/connect": {
337 | "version": "3.4.38",
338 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
339 | "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
340 | "dev": true,
341 | "license": "MIT",
342 | "dependencies": {
343 | "@types/node": "*"
344 | }
345 | },
346 | "node_modules/@types/express": {
347 | "version": "5.0.1",
348 | "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz",
349 | "integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==",
350 | "dev": true,
351 | "license": "MIT",
352 | "dependencies": {
353 | "@types/body-parser": "*",
354 | "@types/express-serve-static-core": "^5.0.0",
355 | "@types/serve-static": "*"
356 | }
357 | },
358 | "node_modules/@types/express-serve-static-core": {
359 | "version": "5.0.6",
360 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
361 | "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
362 | "dev": true,
363 | "license": "MIT",
364 | "dependencies": {
365 | "@types/node": "*",
366 | "@types/qs": "*",
367 | "@types/range-parser": "*",
368 | "@types/send": "*"
369 | }
370 | },
371 | "node_modules/@types/http-errors": {
372 | "version": "2.0.4",
373 | "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
374 | "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
375 | "dev": true,
376 | "license": "MIT"
377 | },
378 | "node_modules/@types/mime": {
379 | "version": "1.3.5",
380 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
381 | "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
382 | "dev": true,
383 | "license": "MIT"
384 | },
385 | "node_modules/@types/node": {
386 | "version": "22.14.0",
387 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.0.tgz",
388 | "integrity": "sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==",
389 | "dev": true,
390 | "license": "MIT",
391 | "dependencies": {
392 | "undici-types": "~6.21.0"
393 | }
394 | },
395 | "node_modules/@types/qs": {
396 | "version": "6.9.18",
397 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
398 | "integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
399 | "dev": true,
400 | "license": "MIT"
401 | },
402 | "node_modules/@types/range-parser": {
403 | "version": "1.2.7",
404 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
405 | "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
406 | "dev": true,
407 | "license": "MIT"
408 | },
409 | "node_modules/@types/send": {
410 | "version": "0.17.4",
411 | "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
412 | "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
413 | "dev": true,
414 | "license": "MIT",
415 | "dependencies": {
416 | "@types/mime": "^1",
417 | "@types/node": "*"
418 | }
419 | },
420 | "node_modules/@types/serve-static": {
421 | "version": "1.15.7",
422 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
423 | "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
424 | "dev": true,
425 | "license": "MIT",
426 | "dependencies": {
427 | "@types/http-errors": "*",
428 | "@types/node": "*",
429 | "@types/send": "*"
430 | }
431 | },
432 | "node_modules/accepts": {
433 | "version": "1.3.8",
434 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
435 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
436 | "license": "MIT",
437 | "dependencies": {
438 | "mime-types": "~2.1.34",
439 | "negotiator": "0.6.3"
440 | },
441 | "engines": {
442 | "node": ">= 0.6"
443 | }
444 | },
445 | "node_modules/ajv": {
446 | "version": "8.17.1",
447 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
448 | "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
449 | "license": "MIT",
450 | "dependencies": {
451 | "fast-deep-equal": "^3.1.3",
452 | "fast-uri": "^3.0.1",
453 | "json-schema-traverse": "^1.0.0",
454 | "require-from-string": "^2.0.2"
455 | },
456 | "funding": {
457 | "type": "github",
458 | "url": "https://github.com/sponsors/epoberezkin"
459 | }
460 | },
461 | "node_modules/array-flatten": {
462 | "version": "1.1.1",
463 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
464 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
465 | "license": "MIT"
466 | },
467 | "node_modules/asynckit": {
468 | "version": "0.4.0",
469 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
470 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
471 | "license": "MIT"
472 | },
473 | "node_modules/axios": {
474 | "version": "1.8.4",
475 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
476 | "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
477 | "license": "MIT",
478 | "dependencies": {
479 | "follow-redirects": "^1.15.6",
480 | "form-data": "^4.0.0",
481 | "proxy-from-env": "^1.1.0"
482 | }
483 | },
484 | "node_modules/balanced-match": {
485 | "version": "1.0.2",
486 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
487 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
488 | "dev": true,
489 | "license": "MIT"
490 | },
491 | "node_modules/body-parser": {
492 | "version": "1.20.3",
493 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
494 | "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
495 | "license": "MIT",
496 | "dependencies": {
497 | "bytes": "3.1.2",
498 | "content-type": "~1.0.5",
499 | "debug": "2.6.9",
500 | "depd": "2.0.0",
501 | "destroy": "1.2.0",
502 | "http-errors": "2.0.0",
503 | "iconv-lite": "0.4.24",
504 | "on-finished": "2.4.1",
505 | "qs": "6.13.0",
506 | "raw-body": "2.5.2",
507 | "type-is": "~1.6.18",
508 | "unpipe": "1.0.0"
509 | },
510 | "engines": {
511 | "node": ">= 0.8",
512 | "npm": "1.2.8000 || >= 1.4.16"
513 | }
514 | },
515 | "node_modules/body-parser/node_modules/raw-body": {
516 | "version": "2.5.2",
517 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
518 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
519 | "license": "MIT",
520 | "dependencies": {
521 | "bytes": "3.1.2",
522 | "http-errors": "2.0.0",
523 | "iconv-lite": "0.4.24",
524 | "unpipe": "1.0.0"
525 | },
526 | "engines": {
527 | "node": ">= 0.8"
528 | }
529 | },
530 | "node_modules/brace-expansion": {
531 | "version": "1.1.11",
532 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
533 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
534 | "dev": true,
535 | "license": "MIT",
536 | "dependencies": {
537 | "balanced-match": "^1.0.0",
538 | "concat-map": "0.0.1"
539 | }
540 | },
541 | "node_modules/bytes": {
542 | "version": "3.1.2",
543 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
544 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
545 | "license": "MIT",
546 | "engines": {
547 | "node": ">= 0.8"
548 | }
549 | },
550 | "node_modules/call-bind-apply-helpers": {
551 | "version": "1.0.2",
552 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
553 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
554 | "license": "MIT",
555 | "dependencies": {
556 | "es-errors": "^1.3.0",
557 | "function-bind": "^1.1.2"
558 | },
559 | "engines": {
560 | "node": ">= 0.4"
561 | }
562 | },
563 | "node_modules/call-bound": {
564 | "version": "1.0.4",
565 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
566 | "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
567 | "license": "MIT",
568 | "dependencies": {
569 | "call-bind-apply-helpers": "^1.0.2",
570 | "get-intrinsic": "^1.3.0"
571 | },
572 | "engines": {
573 | "node": ">= 0.4"
574 | },
575 | "funding": {
576 | "url": "https://github.com/sponsors/ljharb"
577 | }
578 | },
579 | "node_modules/combined-stream": {
580 | "version": "1.0.8",
581 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
582 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
583 | "license": "MIT",
584 | "dependencies": {
585 | "delayed-stream": "~1.0.0"
586 | },
587 | "engines": {
588 | "node": ">= 0.8"
589 | }
590 | },
591 | "node_modules/commander": {
592 | "version": "13.1.0",
593 | "resolved": "https://registry.npmjs.org/commander/-/commander-13.1.0.tgz",
594 | "integrity": "sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==",
595 | "license": "MIT",
596 | "engines": {
597 | "node": ">=18"
598 | }
599 | },
600 | "node_modules/concat-map": {
601 | "version": "0.0.1",
602 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
603 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
604 | "dev": true,
605 | "license": "MIT"
606 | },
607 | "node_modules/content-disposition": {
608 | "version": "0.5.4",
609 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
610 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
611 | "license": "MIT",
612 | "dependencies": {
613 | "safe-buffer": "5.2.1"
614 | },
615 | "engines": {
616 | "node": ">= 0.6"
617 | }
618 | },
619 | "node_modules/content-type": {
620 | "version": "1.0.5",
621 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
622 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
623 | "license": "MIT",
624 | "engines": {
625 | "node": ">= 0.6"
626 | }
627 | },
628 | "node_modules/cookie": {
629 | "version": "0.7.1",
630 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
631 | "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
632 | "license": "MIT",
633 | "engines": {
634 | "node": ">= 0.6"
635 | }
636 | },
637 | "node_modules/cookie-signature": {
638 | "version": "1.0.6",
639 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
640 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
641 | "license": "MIT"
642 | },
643 | "node_modules/cors": {
644 | "version": "2.8.5",
645 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
646 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
647 | "license": "MIT",
648 | "dependencies": {
649 | "object-assign": "^4",
650 | "vary": "^1"
651 | },
652 | "engines": {
653 | "node": ">= 0.10"
654 | }
655 | },
656 | "node_modules/cross-spawn": {
657 | "version": "7.0.6",
658 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
659 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
660 | "license": "MIT",
661 | "dependencies": {
662 | "path-key": "^3.1.0",
663 | "shebang-command": "^2.0.0",
664 | "which": "^2.0.1"
665 | },
666 | "engines": {
667 | "node": ">= 8"
668 | }
669 | },
670 | "node_modules/debug": {
671 | "version": "2.6.9",
672 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
673 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
674 | "license": "MIT",
675 | "dependencies": {
676 | "ms": "2.0.0"
677 | }
678 | },
679 | "node_modules/delayed-stream": {
680 | "version": "1.0.0",
681 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
682 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
683 | "license": "MIT",
684 | "engines": {
685 | "node": ">=0.4.0"
686 | }
687 | },
688 | "node_modules/depd": {
689 | "version": "2.0.0",
690 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
691 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
692 | "license": "MIT",
693 | "engines": {
694 | "node": ">= 0.8"
695 | }
696 | },
697 | "node_modules/destroy": {
698 | "version": "1.2.0",
699 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
700 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
701 | "license": "MIT",
702 | "engines": {
703 | "node": ">= 0.8",
704 | "npm": "1.2.8000 || >= 1.4.16"
705 | }
706 | },
707 | "node_modules/dotenv": {
708 | "version": "16.4.7",
709 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz",
710 | "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==",
711 | "dev": true,
712 | "license": "BSD-2-Clause",
713 | "engines": {
714 | "node": ">=12"
715 | },
716 | "funding": {
717 | "url": "https://dotenvx.com"
718 | }
719 | },
720 | "node_modules/dotenv-cli": {
721 | "version": "8.0.0",
722 | "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-8.0.0.tgz",
723 | "integrity": "sha512-aLqYbK7xKOiTMIRf1lDPbI+Y+Ip/wo5k3eyp6ePysVaSqbyxjyK3dK35BTxG+rmd7djf5q2UPs4noPNH+cj0Qw==",
724 | "dev": true,
725 | "license": "MIT",
726 | "dependencies": {
727 | "cross-spawn": "^7.0.6",
728 | "dotenv": "^16.3.0",
729 | "dotenv-expand": "^10.0.0",
730 | "minimist": "^1.2.6"
731 | },
732 | "bin": {
733 | "dotenv": "cli.js"
734 | }
735 | },
736 | "node_modules/dotenv-expand": {
737 | "version": "10.0.0",
738 | "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-10.0.0.tgz",
739 | "integrity": "sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==",
740 | "dev": true,
741 | "license": "BSD-2-Clause",
742 | "engines": {
743 | "node": ">=12"
744 | }
745 | },
746 | "node_modules/dunder-proto": {
747 | "version": "1.0.1",
748 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
749 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
750 | "license": "MIT",
751 | "dependencies": {
752 | "call-bind-apply-helpers": "^1.0.1",
753 | "es-errors": "^1.3.0",
754 | "gopd": "^1.2.0"
755 | },
756 | "engines": {
757 | "node": ">= 0.4"
758 | }
759 | },
760 | "node_modules/ee-first": {
761 | "version": "1.1.1",
762 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
763 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
764 | "license": "MIT"
765 | },
766 | "node_modules/encodeurl": {
767 | "version": "2.0.0",
768 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
769 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
770 | "license": "MIT",
771 | "engines": {
772 | "node": ">= 0.8"
773 | }
774 | },
775 | "node_modules/es-define-property": {
776 | "version": "1.0.1",
777 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
778 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
779 | "license": "MIT",
780 | "engines": {
781 | "node": ">= 0.4"
782 | }
783 | },
784 | "node_modules/es-errors": {
785 | "version": "1.3.0",
786 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
787 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
788 | "license": "MIT",
789 | "engines": {
790 | "node": ">= 0.4"
791 | }
792 | },
793 | "node_modules/es-object-atoms": {
794 | "version": "1.1.1",
795 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
796 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
797 | "license": "MIT",
798 | "dependencies": {
799 | "es-errors": "^1.3.0"
800 | },
801 | "engines": {
802 | "node": ">= 0.4"
803 | }
804 | },
805 | "node_modules/es-set-tostringtag": {
806 | "version": "2.1.0",
807 | "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
808 | "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
809 | "license": "MIT",
810 | "dependencies": {
811 | "es-errors": "^1.3.0",
812 | "get-intrinsic": "^1.2.6",
813 | "has-tostringtag": "^1.0.2",
814 | "hasown": "^2.0.2"
815 | },
816 | "engines": {
817 | "node": ">= 0.4"
818 | }
819 | },
820 | "node_modules/escape-html": {
821 | "version": "1.0.3",
822 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
823 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
824 | "license": "MIT"
825 | },
826 | "node_modules/etag": {
827 | "version": "1.8.1",
828 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
829 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
830 | "license": "MIT",
831 | "engines": {
832 | "node": ">= 0.6"
833 | }
834 | },
835 | "node_modules/eventsource": {
836 | "version": "3.0.6",
837 | "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.6.tgz",
838 | "integrity": "sha512-l19WpE2m9hSuyP06+FbuUUf1G+R0SFLrtQfbRb9PRr+oimOfxQhgGCbVaXg5IvZyyTThJsxh6L/srkMiCeBPDA==",
839 | "license": "MIT",
840 | "dependencies": {
841 | "eventsource-parser": "^3.0.1"
842 | },
843 | "engines": {
844 | "node": ">=18.0.0"
845 | }
846 | },
847 | "node_modules/eventsource-parser": {
848 | "version": "3.0.1",
849 | "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.1.tgz",
850 | "integrity": "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==",
851 | "license": "MIT",
852 | "engines": {
853 | "node": ">=18.0.0"
854 | }
855 | },
856 | "node_modules/express": {
857 | "version": "4.21.2",
858 | "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
859 | "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
860 | "license": "MIT",
861 | "dependencies": {
862 | "accepts": "~1.3.8",
863 | "array-flatten": "1.1.1",
864 | "body-parser": "1.20.3",
865 | "content-disposition": "0.5.4",
866 | "content-type": "~1.0.4",
867 | "cookie": "0.7.1",
868 | "cookie-signature": "1.0.6",
869 | "debug": "2.6.9",
870 | "depd": "2.0.0",
871 | "encodeurl": "~2.0.0",
872 | "escape-html": "~1.0.3",
873 | "etag": "~1.8.1",
874 | "finalhandler": "1.3.1",
875 | "fresh": "0.5.2",
876 | "http-errors": "2.0.0",
877 | "merge-descriptors": "1.0.3",
878 | "methods": "~1.1.2",
879 | "on-finished": "2.4.1",
880 | "parseurl": "~1.3.3",
881 | "path-to-regexp": "0.1.12",
882 | "proxy-addr": "~2.0.7",
883 | "qs": "6.13.0",
884 | "range-parser": "~1.2.1",
885 | "safe-buffer": "5.2.1",
886 | "send": "0.19.0",
887 | "serve-static": "1.16.2",
888 | "setprototypeof": "1.2.0",
889 | "statuses": "2.0.1",
890 | "type-is": "~1.6.18",
891 | "utils-merge": "1.0.1",
892 | "vary": "~1.1.2"
893 | },
894 | "engines": {
895 | "node": ">= 0.10.0"
896 | },
897 | "funding": {
898 | "type": "opencollective",
899 | "url": "https://opencollective.com/express"
900 | }
901 | },
902 | "node_modules/express-rate-limit": {
903 | "version": "7.5.0",
904 | "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
905 | "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
906 | "license": "MIT",
907 | "engines": {
908 | "node": ">= 16"
909 | },
910 | "funding": {
911 | "url": "https://github.com/sponsors/express-rate-limit"
912 | },
913 | "peerDependencies": {
914 | "express": "^4.11 || 5 || ^5.0.0-beta.1"
915 | }
916 | },
917 | "node_modules/fast-deep-equal": {
918 | "version": "3.1.3",
919 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
920 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
921 | "license": "MIT"
922 | },
923 | "node_modules/fast-uri": {
924 | "version": "3.0.6",
925 | "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
926 | "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
927 | "funding": [
928 | {
929 | "type": "github",
930 | "url": "https://github.com/sponsors/fastify"
931 | },
932 | {
933 | "type": "opencollective",
934 | "url": "https://opencollective.com/fastify"
935 | }
936 | ],
937 | "license": "BSD-3-Clause"
938 | },
939 | "node_modules/finalhandler": {
940 | "version": "1.3.1",
941 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
942 | "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
943 | "license": "MIT",
944 | "dependencies": {
945 | "debug": "2.6.9",
946 | "encodeurl": "~2.0.0",
947 | "escape-html": "~1.0.3",
948 | "on-finished": "2.4.1",
949 | "parseurl": "~1.3.3",
950 | "statuses": "2.0.1",
951 | "unpipe": "~1.0.0"
952 | },
953 | "engines": {
954 | "node": ">= 0.8"
955 | }
956 | },
957 | "node_modules/follow-redirects": {
958 | "version": "1.15.9",
959 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
960 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
961 | "funding": [
962 | {
963 | "type": "individual",
964 | "url": "https://github.com/sponsors/RubenVerborgh"
965 | }
966 | ],
967 | "license": "MIT",
968 | "engines": {
969 | "node": ">=4.0"
970 | },
971 | "peerDependenciesMeta": {
972 | "debug": {
973 | "optional": true
974 | }
975 | }
976 | },
977 | "node_modules/form-data": {
978 | "version": "4.0.2",
979 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
980 | "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
981 | "license": "MIT",
982 | "dependencies": {
983 | "asynckit": "^0.4.0",
984 | "combined-stream": "^1.0.8",
985 | "es-set-tostringtag": "^2.1.0",
986 | "mime-types": "^2.1.12"
987 | },
988 | "engines": {
989 | "node": ">= 6"
990 | }
991 | },
992 | "node_modules/forwarded": {
993 | "version": "0.2.0",
994 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
995 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
996 | "license": "MIT",
997 | "engines": {
998 | "node": ">= 0.6"
999 | }
1000 | },
1001 | "node_modules/fresh": {
1002 | "version": "0.5.2",
1003 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
1004 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
1005 | "license": "MIT",
1006 | "engines": {
1007 | "node": ">= 0.6"
1008 | }
1009 | },
1010 | "node_modules/fs.realpath": {
1011 | "version": "1.0.0",
1012 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
1013 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
1014 | "dev": true,
1015 | "license": "ISC"
1016 | },
1017 | "node_modules/function-bind": {
1018 | "version": "1.1.2",
1019 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
1020 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
1021 | "license": "MIT",
1022 | "funding": {
1023 | "url": "https://github.com/sponsors/ljharb"
1024 | }
1025 | },
1026 | "node_modules/get-intrinsic": {
1027 | "version": "1.3.0",
1028 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
1029 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
1030 | "license": "MIT",
1031 | "dependencies": {
1032 | "call-bind-apply-helpers": "^1.0.2",
1033 | "es-define-property": "^1.0.1",
1034 | "es-errors": "^1.3.0",
1035 | "es-object-atoms": "^1.1.1",
1036 | "function-bind": "^1.1.2",
1037 | "get-proto": "^1.0.1",
1038 | "gopd": "^1.2.0",
1039 | "has-symbols": "^1.1.0",
1040 | "hasown": "^2.0.2",
1041 | "math-intrinsics": "^1.1.0"
1042 | },
1043 | "engines": {
1044 | "node": ">= 0.4"
1045 | },
1046 | "funding": {
1047 | "url": "https://github.com/sponsors/ljharb"
1048 | }
1049 | },
1050 | "node_modules/get-proto": {
1051 | "version": "1.0.1",
1052 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
1053 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
1054 | "license": "MIT",
1055 | "dependencies": {
1056 | "dunder-proto": "^1.0.1",
1057 | "es-object-atoms": "^1.0.0"
1058 | },
1059 | "engines": {
1060 | "node": ">= 0.4"
1061 | }
1062 | },
1063 | "node_modules/glob": {
1064 | "version": "7.2.3",
1065 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
1066 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
1067 | "deprecated": "Glob versions prior to v9 are no longer supported",
1068 | "dev": true,
1069 | "license": "ISC",
1070 | "dependencies": {
1071 | "fs.realpath": "^1.0.0",
1072 | "inflight": "^1.0.4",
1073 | "inherits": "2",
1074 | "minimatch": "^3.1.1",
1075 | "once": "^1.3.0",
1076 | "path-is-absolute": "^1.0.0"
1077 | },
1078 | "engines": {
1079 | "node": "*"
1080 | },
1081 | "funding": {
1082 | "url": "https://github.com/sponsors/isaacs"
1083 | }
1084 | },
1085 | "node_modules/gopd": {
1086 | "version": "1.2.0",
1087 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
1088 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
1089 | "license": "MIT",
1090 | "engines": {
1091 | "node": ">= 0.4"
1092 | },
1093 | "funding": {
1094 | "url": "https://github.com/sponsors/ljharb"
1095 | }
1096 | },
1097 | "node_modules/has-symbols": {
1098 | "version": "1.1.0",
1099 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
1100 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
1101 | "license": "MIT",
1102 | "engines": {
1103 | "node": ">= 0.4"
1104 | },
1105 | "funding": {
1106 | "url": "https://github.com/sponsors/ljharb"
1107 | }
1108 | },
1109 | "node_modules/has-tostringtag": {
1110 | "version": "1.0.2",
1111 | "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
1112 | "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
1113 | "license": "MIT",
1114 | "dependencies": {
1115 | "has-symbols": "^1.0.3"
1116 | },
1117 | "engines": {
1118 | "node": ">= 0.4"
1119 | },
1120 | "funding": {
1121 | "url": "https://github.com/sponsors/ljharb"
1122 | }
1123 | },
1124 | "node_modules/hasown": {
1125 | "version": "2.0.2",
1126 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
1127 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
1128 | "license": "MIT",
1129 | "dependencies": {
1130 | "function-bind": "^1.1.2"
1131 | },
1132 | "engines": {
1133 | "node": ">= 0.4"
1134 | }
1135 | },
1136 | "node_modules/http-errors": {
1137 | "version": "2.0.0",
1138 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
1139 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
1140 | "license": "MIT",
1141 | "dependencies": {
1142 | "depd": "2.0.0",
1143 | "inherits": "2.0.4",
1144 | "setprototypeof": "1.2.0",
1145 | "statuses": "2.0.1",
1146 | "toidentifier": "1.0.1"
1147 | },
1148 | "engines": {
1149 | "node": ">= 0.8"
1150 | }
1151 | },
1152 | "node_modules/iconv-lite": {
1153 | "version": "0.4.24",
1154 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
1155 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
1156 | "license": "MIT",
1157 | "dependencies": {
1158 | "safer-buffer": ">= 2.1.2 < 3"
1159 | },
1160 | "engines": {
1161 | "node": ">=0.10.0"
1162 | }
1163 | },
1164 | "node_modules/inflight": {
1165 | "version": "1.0.6",
1166 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
1167 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
1168 | "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
1169 | "dev": true,
1170 | "license": "ISC",
1171 | "dependencies": {
1172 | "once": "^1.3.0",
1173 | "wrappy": "1"
1174 | }
1175 | },
1176 | "node_modules/inherits": {
1177 | "version": "2.0.4",
1178 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
1179 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
1180 | "license": "ISC"
1181 | },
1182 | "node_modules/interpret": {
1183 | "version": "1.4.0",
1184 | "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz",
1185 | "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==",
1186 | "dev": true,
1187 | "license": "MIT",
1188 | "engines": {
1189 | "node": ">= 0.10"
1190 | }
1191 | },
1192 | "node_modules/ipaddr.js": {
1193 | "version": "1.9.1",
1194 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
1195 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
1196 | "license": "MIT",
1197 | "engines": {
1198 | "node": ">= 0.10"
1199 | }
1200 | },
1201 | "node_modules/is-core-module": {
1202 | "version": "2.16.1",
1203 | "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
1204 | "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
1205 | "dev": true,
1206 | "license": "MIT",
1207 | "dependencies": {
1208 | "hasown": "^2.0.2"
1209 | },
1210 | "engines": {
1211 | "node": ">= 0.4"
1212 | },
1213 | "funding": {
1214 | "url": "https://github.com/sponsors/ljharb"
1215 | }
1216 | },
1217 | "node_modules/is-promise": {
1218 | "version": "4.0.0",
1219 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
1220 | "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==",
1221 | "license": "MIT"
1222 | },
1223 | "node_modules/isexe": {
1224 | "version": "2.0.0",
1225 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
1226 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
1227 | "license": "ISC"
1228 | },
1229 | "node_modules/json-schema-traverse": {
1230 | "version": "1.0.0",
1231 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
1232 | "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
1233 | "license": "MIT"
1234 | },
1235 | "node_modules/math-intrinsics": {
1236 | "version": "1.1.0",
1237 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
1238 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
1239 | "license": "MIT",
1240 | "engines": {
1241 | "node": ">= 0.4"
1242 | }
1243 | },
1244 | "node_modules/media-typer": {
1245 | "version": "0.3.0",
1246 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1247 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
1248 | "license": "MIT",
1249 | "engines": {
1250 | "node": ">= 0.6"
1251 | }
1252 | },
1253 | "node_modules/merge-descriptors": {
1254 | "version": "1.0.3",
1255 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
1256 | "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
1257 | "license": "MIT",
1258 | "funding": {
1259 | "url": "https://github.com/sponsors/sindresorhus"
1260 | }
1261 | },
1262 | "node_modules/methods": {
1263 | "version": "1.1.2",
1264 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1265 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
1266 | "license": "MIT",
1267 | "engines": {
1268 | "node": ">= 0.6"
1269 | }
1270 | },
1271 | "node_modules/mime": {
1272 | "version": "1.6.0",
1273 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1274 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
1275 | "license": "MIT",
1276 | "bin": {
1277 | "mime": "cli.js"
1278 | },
1279 | "engines": {
1280 | "node": ">=4"
1281 | }
1282 | },
1283 | "node_modules/mime-db": {
1284 | "version": "1.52.0",
1285 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1286 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
1287 | "license": "MIT",
1288 | "engines": {
1289 | "node": ">= 0.6"
1290 | }
1291 | },
1292 | "node_modules/mime-types": {
1293 | "version": "2.1.35",
1294 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1295 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1296 | "license": "MIT",
1297 | "dependencies": {
1298 | "mime-db": "1.52.0"
1299 | },
1300 | "engines": {
1301 | "node": ">= 0.6"
1302 | }
1303 | },
1304 | "node_modules/minimatch": {
1305 | "version": "3.1.2",
1306 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
1307 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1308 | "dev": true,
1309 | "license": "ISC",
1310 | "dependencies": {
1311 | "brace-expansion": "^1.1.7"
1312 | },
1313 | "engines": {
1314 | "node": "*"
1315 | }
1316 | },
1317 | "node_modules/minimist": {
1318 | "version": "1.2.8",
1319 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
1320 | "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
1321 | "dev": true,
1322 | "license": "MIT",
1323 | "funding": {
1324 | "url": "https://github.com/sponsors/ljharb"
1325 | }
1326 | },
1327 | "node_modules/ms": {
1328 | "version": "2.0.0",
1329 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1330 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
1331 | "license": "MIT"
1332 | },
1333 | "node_modules/negotiator": {
1334 | "version": "0.6.3",
1335 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1336 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
1337 | "license": "MIT",
1338 | "engines": {
1339 | "node": ">= 0.6"
1340 | }
1341 | },
1342 | "node_modules/object-assign": {
1343 | "version": "4.1.1",
1344 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1345 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1346 | "license": "MIT",
1347 | "engines": {
1348 | "node": ">=0.10.0"
1349 | }
1350 | },
1351 | "node_modules/object-inspect": {
1352 | "version": "1.13.4",
1353 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
1354 | "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
1355 | "license": "MIT",
1356 | "engines": {
1357 | "node": ">= 0.4"
1358 | },
1359 | "funding": {
1360 | "url": "https://github.com/sponsors/ljharb"
1361 | }
1362 | },
1363 | "node_modules/on-finished": {
1364 | "version": "2.4.1",
1365 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1366 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1367 | "license": "MIT",
1368 | "dependencies": {
1369 | "ee-first": "1.1.1"
1370 | },
1371 | "engines": {
1372 | "node": ">= 0.8"
1373 | }
1374 | },
1375 | "node_modules/once": {
1376 | "version": "1.4.0",
1377 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1378 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
1379 | "license": "ISC",
1380 | "dependencies": {
1381 | "wrappy": "1"
1382 | }
1383 | },
1384 | "node_modules/parseurl": {
1385 | "version": "1.3.3",
1386 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1387 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1388 | "license": "MIT",
1389 | "engines": {
1390 | "node": ">= 0.8"
1391 | }
1392 | },
1393 | "node_modules/path-is-absolute": {
1394 | "version": "1.0.1",
1395 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
1396 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
1397 | "dev": true,
1398 | "license": "MIT",
1399 | "engines": {
1400 | "node": ">=0.10.0"
1401 | }
1402 | },
1403 | "node_modules/path-key": {
1404 | "version": "3.1.1",
1405 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
1406 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
1407 | "license": "MIT",
1408 | "engines": {
1409 | "node": ">=8"
1410 | }
1411 | },
1412 | "node_modules/path-parse": {
1413 | "version": "1.0.7",
1414 | "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
1415 | "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
1416 | "dev": true,
1417 | "license": "MIT"
1418 | },
1419 | "node_modules/path-to-regexp": {
1420 | "version": "0.1.12",
1421 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
1422 | "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
1423 | "license": "MIT"
1424 | },
1425 | "node_modules/pkce-challenge": {
1426 | "version": "5.0.0",
1427 | "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
1428 | "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
1429 | "license": "MIT",
1430 | "engines": {
1431 | "node": ">=16.20.0"
1432 | }
1433 | },
1434 | "node_modules/proxy-addr": {
1435 | "version": "2.0.7",
1436 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1437 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1438 | "license": "MIT",
1439 | "dependencies": {
1440 | "forwarded": "0.2.0",
1441 | "ipaddr.js": "1.9.1"
1442 | },
1443 | "engines": {
1444 | "node": ">= 0.10"
1445 | }
1446 | },
1447 | "node_modules/proxy-from-env": {
1448 | "version": "1.1.0",
1449 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
1450 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
1451 | "license": "MIT"
1452 | },
1453 | "node_modules/qs": {
1454 | "version": "6.13.0",
1455 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
1456 | "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
1457 | "license": "BSD-3-Clause",
1458 | "dependencies": {
1459 | "side-channel": "^1.0.6"
1460 | },
1461 | "engines": {
1462 | "node": ">=0.6"
1463 | },
1464 | "funding": {
1465 | "url": "https://github.com/sponsors/ljharb"
1466 | }
1467 | },
1468 | "node_modules/range-parser": {
1469 | "version": "1.2.1",
1470 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1471 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1472 | "license": "MIT",
1473 | "engines": {
1474 | "node": ">= 0.6"
1475 | }
1476 | },
1477 | "node_modules/raw-body": {
1478 | "version": "3.0.0",
1479 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz",
1480 | "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==",
1481 | "license": "MIT",
1482 | "dependencies": {
1483 | "bytes": "3.1.2",
1484 | "http-errors": "2.0.0",
1485 | "iconv-lite": "0.6.3",
1486 | "unpipe": "1.0.0"
1487 | },
1488 | "engines": {
1489 | "node": ">= 0.8"
1490 | }
1491 | },
1492 | "node_modules/raw-body/node_modules/iconv-lite": {
1493 | "version": "0.6.3",
1494 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
1495 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
1496 | "license": "MIT",
1497 | "dependencies": {
1498 | "safer-buffer": ">= 2.1.2 < 3.0.0"
1499 | },
1500 | "engines": {
1501 | "node": ">=0.10.0"
1502 | }
1503 | },
1504 | "node_modules/rechoir": {
1505 | "version": "0.6.2",
1506 | "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
1507 | "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==",
1508 | "dev": true,
1509 | "dependencies": {
1510 | "resolve": "^1.1.6"
1511 | },
1512 | "engines": {
1513 | "node": ">= 0.10"
1514 | }
1515 | },
1516 | "node_modules/require-from-string": {
1517 | "version": "2.0.2",
1518 | "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
1519 | "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
1520 | "license": "MIT",
1521 | "engines": {
1522 | "node": ">=0.10.0"
1523 | }
1524 | },
1525 | "node_modules/resolve": {
1526 | "version": "1.22.10",
1527 | "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
1528 | "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
1529 | "dev": true,
1530 | "license": "MIT",
1531 | "dependencies": {
1532 | "is-core-module": "^2.16.0",
1533 | "path-parse": "^1.0.7",
1534 | "supports-preserve-symlinks-flag": "^1.0.0"
1535 | },
1536 | "bin": {
1537 | "resolve": "bin/resolve"
1538 | },
1539 | "engines": {
1540 | "node": ">= 0.4"
1541 | },
1542 | "funding": {
1543 | "url": "https://github.com/sponsors/ljharb"
1544 | }
1545 | },
1546 | "node_modules/router": {
1547 | "version": "2.2.0",
1548 | "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz",
1549 | "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==",
1550 | "license": "MIT",
1551 | "dependencies": {
1552 | "debug": "^4.4.0",
1553 | "depd": "^2.0.0",
1554 | "is-promise": "^4.0.0",
1555 | "parseurl": "^1.3.3",
1556 | "path-to-regexp": "^8.0.0"
1557 | },
1558 | "engines": {
1559 | "node": ">= 18"
1560 | }
1561 | },
1562 | "node_modules/router/node_modules/debug": {
1563 | "version": "4.4.0",
1564 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
1565 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
1566 | "license": "MIT",
1567 | "dependencies": {
1568 | "ms": "^2.1.3"
1569 | },
1570 | "engines": {
1571 | "node": ">=6.0"
1572 | },
1573 | "peerDependenciesMeta": {
1574 | "supports-color": {
1575 | "optional": true
1576 | }
1577 | }
1578 | },
1579 | "node_modules/router/node_modules/ms": {
1580 | "version": "2.1.3",
1581 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1582 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1583 | "license": "MIT"
1584 | },
1585 | "node_modules/router/node_modules/path-to-regexp": {
1586 | "version": "8.2.0",
1587 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
1588 | "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==",
1589 | "license": "MIT",
1590 | "engines": {
1591 | "node": ">=16"
1592 | }
1593 | },
1594 | "node_modules/safe-buffer": {
1595 | "version": "5.2.1",
1596 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1597 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1598 | "funding": [
1599 | {
1600 | "type": "github",
1601 | "url": "https://github.com/sponsors/feross"
1602 | },
1603 | {
1604 | "type": "patreon",
1605 | "url": "https://www.patreon.com/feross"
1606 | },
1607 | {
1608 | "type": "consulting",
1609 | "url": "https://feross.org/support"
1610 | }
1611 | ],
1612 | "license": "MIT"
1613 | },
1614 | "node_modules/safer-buffer": {
1615 | "version": "2.1.2",
1616 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1617 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
1618 | "license": "MIT"
1619 | },
1620 | "node_modules/send": {
1621 | "version": "0.19.0",
1622 | "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
1623 | "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
1624 | "license": "MIT",
1625 | "dependencies": {
1626 | "debug": "2.6.9",
1627 | "depd": "2.0.0",
1628 | "destroy": "1.2.0",
1629 | "encodeurl": "~1.0.2",
1630 | "escape-html": "~1.0.3",
1631 | "etag": "~1.8.1",
1632 | "fresh": "0.5.2",
1633 | "http-errors": "2.0.0",
1634 | "mime": "1.6.0",
1635 | "ms": "2.1.3",
1636 | "on-finished": "2.4.1",
1637 | "range-parser": "~1.2.1",
1638 | "statuses": "2.0.1"
1639 | },
1640 | "engines": {
1641 | "node": ">= 0.8.0"
1642 | }
1643 | },
1644 | "node_modules/send/node_modules/encodeurl": {
1645 | "version": "1.0.2",
1646 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
1647 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
1648 | "license": "MIT",
1649 | "engines": {
1650 | "node": ">= 0.8"
1651 | }
1652 | },
1653 | "node_modules/send/node_modules/ms": {
1654 | "version": "2.1.3",
1655 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1656 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1657 | "license": "MIT"
1658 | },
1659 | "node_modules/serve-static": {
1660 | "version": "1.16.2",
1661 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
1662 | "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
1663 | "license": "MIT",
1664 | "dependencies": {
1665 | "encodeurl": "~2.0.0",
1666 | "escape-html": "~1.0.3",
1667 | "parseurl": "~1.3.3",
1668 | "send": "0.19.0"
1669 | },
1670 | "engines": {
1671 | "node": ">= 0.8.0"
1672 | }
1673 | },
1674 | "node_modules/setprototypeof": {
1675 | "version": "1.2.0",
1676 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1677 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
1678 | "license": "ISC"
1679 | },
1680 | "node_modules/shebang-command": {
1681 | "version": "2.0.0",
1682 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
1683 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
1684 | "license": "MIT",
1685 | "dependencies": {
1686 | "shebang-regex": "^3.0.0"
1687 | },
1688 | "engines": {
1689 | "node": ">=8"
1690 | }
1691 | },
1692 | "node_modules/shebang-regex": {
1693 | "version": "3.0.0",
1694 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
1695 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
1696 | "license": "MIT",
1697 | "engines": {
1698 | "node": ">=8"
1699 | }
1700 | },
1701 | "node_modules/shelljs": {
1702 | "version": "0.8.5",
1703 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz",
1704 | "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==",
1705 | "dev": true,
1706 | "license": "BSD-3-Clause",
1707 | "dependencies": {
1708 | "glob": "^7.0.0",
1709 | "interpret": "^1.0.0",
1710 | "rechoir": "^0.6.2"
1711 | },
1712 | "bin": {
1713 | "shjs": "bin/shjs"
1714 | },
1715 | "engines": {
1716 | "node": ">=4"
1717 | }
1718 | },
1719 | "node_modules/shx": {
1720 | "version": "0.3.4",
1721 | "resolved": "https://registry.npmjs.org/shx/-/shx-0.3.4.tgz",
1722 | "integrity": "sha512-N6A9MLVqjxZYcVn8hLmtneQWIJtp8IKzMP4eMnx+nqkvXoqinUPCbUFLp2UcWTEIUONhlk0ewxr/jaVGlc+J+g==",
1723 | "dev": true,
1724 | "license": "MIT",
1725 | "dependencies": {
1726 | "minimist": "^1.2.3",
1727 | "shelljs": "^0.8.5"
1728 | },
1729 | "bin": {
1730 | "shx": "lib/cli.js"
1731 | },
1732 | "engines": {
1733 | "node": ">=6"
1734 | }
1735 | },
1736 | "node_modules/side-channel": {
1737 | "version": "1.1.0",
1738 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
1739 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
1740 | "license": "MIT",
1741 | "dependencies": {
1742 | "es-errors": "^1.3.0",
1743 | "object-inspect": "^1.13.3",
1744 | "side-channel-list": "^1.0.0",
1745 | "side-channel-map": "^1.0.1",
1746 | "side-channel-weakmap": "^1.0.2"
1747 | },
1748 | "engines": {
1749 | "node": ">= 0.4"
1750 | },
1751 | "funding": {
1752 | "url": "https://github.com/sponsors/ljharb"
1753 | }
1754 | },
1755 | "node_modules/side-channel-list": {
1756 | "version": "1.0.0",
1757 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
1758 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
1759 | "license": "MIT",
1760 | "dependencies": {
1761 | "es-errors": "^1.3.0",
1762 | "object-inspect": "^1.13.3"
1763 | },
1764 | "engines": {
1765 | "node": ">= 0.4"
1766 | },
1767 | "funding": {
1768 | "url": "https://github.com/sponsors/ljharb"
1769 | }
1770 | },
1771 | "node_modules/side-channel-map": {
1772 | "version": "1.0.1",
1773 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
1774 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
1775 | "license": "MIT",
1776 | "dependencies": {
1777 | "call-bound": "^1.0.2",
1778 | "es-errors": "^1.3.0",
1779 | "get-intrinsic": "^1.2.5",
1780 | "object-inspect": "^1.13.3"
1781 | },
1782 | "engines": {
1783 | "node": ">= 0.4"
1784 | },
1785 | "funding": {
1786 | "url": "https://github.com/sponsors/ljharb"
1787 | }
1788 | },
1789 | "node_modules/side-channel-weakmap": {
1790 | "version": "1.0.2",
1791 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
1792 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
1793 | "license": "MIT",
1794 | "dependencies": {
1795 | "call-bound": "^1.0.2",
1796 | "es-errors": "^1.3.0",
1797 | "get-intrinsic": "^1.2.5",
1798 | "object-inspect": "^1.13.3",
1799 | "side-channel-map": "^1.0.1"
1800 | },
1801 | "engines": {
1802 | "node": ">= 0.4"
1803 | },
1804 | "funding": {
1805 | "url": "https://github.com/sponsors/ljharb"
1806 | }
1807 | },
1808 | "node_modules/statuses": {
1809 | "version": "2.0.1",
1810 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1811 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1812 | "license": "MIT",
1813 | "engines": {
1814 | "node": ">= 0.8"
1815 | }
1816 | },
1817 | "node_modules/supports-preserve-symlinks-flag": {
1818 | "version": "1.0.0",
1819 | "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
1820 | "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
1821 | "dev": true,
1822 | "license": "MIT",
1823 | "engines": {
1824 | "node": ">= 0.4"
1825 | },
1826 | "funding": {
1827 | "url": "https://github.com/sponsors/ljharb"
1828 | }
1829 | },
1830 | "node_modules/toidentifier": {
1831 | "version": "1.0.1",
1832 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1833 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1834 | "license": "MIT",
1835 | "engines": {
1836 | "node": ">=0.6"
1837 | }
1838 | },
1839 | "node_modules/type-is": {
1840 | "version": "1.6.18",
1841 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1842 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1843 | "license": "MIT",
1844 | "dependencies": {
1845 | "media-typer": "0.3.0",
1846 | "mime-types": "~2.1.24"
1847 | },
1848 | "engines": {
1849 | "node": ">= 0.6"
1850 | }
1851 | },
1852 | "node_modules/typescript": {
1853 | "version": "5.8.2",
1854 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
1855 | "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
1856 | "dev": true,
1857 | "license": "Apache-2.0",
1858 | "bin": {
1859 | "tsc": "bin/tsc",
1860 | "tsserver": "bin/tsserver"
1861 | },
1862 | "engines": {
1863 | "node": ">=14.17"
1864 | }
1865 | },
1866 | "node_modules/undici-types": {
1867 | "version": "6.21.0",
1868 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
1869 | "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
1870 | "dev": true,
1871 | "license": "MIT"
1872 | },
1873 | "node_modules/unpipe": {
1874 | "version": "1.0.0",
1875 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1876 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1877 | "license": "MIT",
1878 | "engines": {
1879 | "node": ">= 0.8"
1880 | }
1881 | },
1882 | "node_modules/utils-merge": {
1883 | "version": "1.0.1",
1884 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1885 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1886 | "license": "MIT",
1887 | "engines": {
1888 | "node": ">= 0.4.0"
1889 | }
1890 | },
1891 | "node_modules/vary": {
1892 | "version": "1.1.2",
1893 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1894 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1895 | "license": "MIT",
1896 | "engines": {
1897 | "node": ">= 0.8"
1898 | }
1899 | },
1900 | "node_modules/which": {
1901 | "version": "2.0.2",
1902 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
1903 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
1904 | "license": "ISC",
1905 | "dependencies": {
1906 | "isexe": "^2.0.0"
1907 | },
1908 | "bin": {
1909 | "node-which": "bin/node-which"
1910 | },
1911 | "engines": {
1912 | "node": ">= 8"
1913 | }
1914 | },
1915 | "node_modules/wrappy": {
1916 | "version": "1.0.2",
1917 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1918 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
1919 | "license": "ISC"
1920 | },
1921 | "node_modules/zod": {
1922 | "version": "3.24.2",
1923 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.2.tgz",
1924 | "integrity": "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==",
1925 | "license": "MIT",
1926 | "funding": {
1927 | "url": "https://github.com/sponsors/colinhacks"
1928 | }
1929 | },
1930 | "node_modules/zod-to-json-schema": {
1931 | "version": "3.24.5",
1932 | "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz",
1933 | "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==",
1934 | "license": "ISC",
1935 | "peerDependencies": {
1936 | "zod": "^3.24.1"
1937 | }
1938 | }
1939 | }
1940 | }
1941 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@metamcp/mcp-server-metamcp",
3 | "version": "0.6.5",
4 | "description": "MCP Server MetaMCP manages all your other MCPs in one MCP.",
5 | "scripts": {
6 | "build": "tsc && shx chmod +x dist/*.js",
7 | "watch": "tsc --watch",
8 | "inspector": "dotenv -e .env.local npx @modelcontextprotocol/inspector dist/index.js -e METAMCP_API_KEY=${METAMCP_API_KEY} -e METAMCP_API_BASE_URL=${METAMCP_API_BASE_URL}",
9 | "inspector:prod": "dotenv -e .env.production.local npx @modelcontextprotocol/inspector dist/index.js -e METAMCP_API_KEY=${METAMCP_API_KEY}",
10 | "report": "dotenv -e .env.local -- node dist/index.js --report"
11 | },
12 | "repository": {
13 | "type": "git",
14 | "url": "git+https://github.com/metatool-ai/mcp-server-metamcp.git"
15 | },
16 | "author": "James Zhang",
17 | "license": "Apache-2.0",
18 | "bugs": {
19 | "url": "https://github.com/metatool-ai/mcp-server-metamcp/issues"
20 | },
21 | "homepage": "https://github.com/metatool-ai/mcp-server-metamcp#readme",
22 | "dependencies": {
23 | "@modelcontextprotocol/sdk": "^1.11.4",
24 | "axios": "^1.7.9",
25 | "commander": "^13.1.0",
26 | "express": "^4.21.2",
27 | "zod": "^3.24.2"
28 | },
29 | "devDependencies": {
30 | "@types/express": "^5.0.1",
31 | "@types/node": "^22.13.4",
32 | "dotenv-cli": "^8.0.0",
33 | "shx": "^0.3.4",
34 | "typescript": "^5.8.2"
35 | },
36 | "type": "module",
37 | "bin": {
38 | "mcp-server-metamcp": "dist/index.js"
39 | },
40 | "files": [
41 | "dist"
42 | ]
43 | }
44 |
--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------
1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
2 |
3 | startCommand:
4 | type: stdio
5 | configSchema:
6 | # JSON Schema defining the configuration options for the MCP.
7 | type: object
8 | required:
9 | - metamcpApiKey
10 | properties:
11 | metamcpApiKey:
12 | type: string
13 | description: The API key from metamcp.com/api-keys. Required.
14 | metamcpApiBaseUrl:
15 | type: string
16 | description: Optional override for the MetaMCP App URL (default is https://api.metamcp.com).
17 | commandFunction:
18 | # A function that produces the CLI command to start the MCP on stdio.
19 | # Note: Command line arguments can also be used directly:
20 | # --metamcp-api-key --metamcp-api-base-url
21 | |-
22 | (config) => ({ command: 'node', args: ['dist/index.js'], env: { METAMCP_API_KEY: config.metamcpApiKey, ...(config.metamcpApiBaseUrl && { METAMCP_API_BASE_URL: config.metamcpApiBaseUrl }) } })
23 |
--------------------------------------------------------------------------------
/src/client.ts:
--------------------------------------------------------------------------------
1 | import { Client } from "@modelcontextprotocol/sdk/client/index.js";
2 | import {
3 | StdioClientTransport,
4 | StdioServerParameters,
5 | } from "@modelcontextprotocol/sdk/client/stdio.js";
6 | import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js";
7 | import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
8 | import { Transport } from "@modelcontextprotocol/sdk/shared/transport.js";
9 | import { ServerParameters, IOType } from "./fetch-metamcp.js";
10 |
11 | const sleep = (time: number) =>
12 | new Promise((resolve) => setTimeout(() => resolve(), time));
13 | export interface ConnectedClient {
14 | client: Client;
15 | cleanup: () => Promise;
16 | }
17 |
18 | /**
19 | * Transforms localhost URLs to use host.docker.internal when running inside Docker
20 | */
21 | const transformDockerUrl = (url: string): string => {
22 | if (process.env.USE_DOCKER_HOST === "true") {
23 | return url.replace(/localhost|127\.0\.0\.1/g, "host.docker.internal");
24 | }
25 | return url;
26 | };
27 |
28 | export const createMetaMcpClient = (
29 | serverParams: ServerParameters
30 | ): { client: Client | undefined; transport: Transport | undefined } => {
31 | let transport: Transport | undefined;
32 |
33 | // Create the appropriate transport based on server type
34 | // Default to "STDIO" if type is undefined
35 | if (!serverParams.type || serverParams.type === "STDIO") {
36 | // Get stderr value from serverParams, environment variable, or default to "ignore"
37 | const stderrValue: IOType =
38 | serverParams.stderr ||
39 | (process.env.METAMCP_STDERR as IOType) ||
40 | "ignore";
41 |
42 | const stdioParams: StdioServerParameters = {
43 | command: serverParams.command || "",
44 | args: serverParams.args || undefined,
45 | env: serverParams.env || undefined,
46 | stderr: stderrValue,
47 | };
48 | transport = new StdioClientTransport(stdioParams);
49 |
50 | // Handle stderr stream when set to "pipe"
51 | if (stderrValue === "pipe" && (transport as any).stderr) {
52 | const stderrStream = (transport as any).stderr;
53 |
54 | stderrStream.on('data', (chunk: Buffer) => {
55 | console.error(`[${serverParams.name}] ${chunk.toString().trim()}`);
56 | });
57 |
58 | stderrStream.on('error', (error: Error) => {
59 | console.error(`[${serverParams.name}] stderr error:`, error);
60 | });
61 | }
62 | } else if (serverParams.type === "SSE" && serverParams.url) {
63 | // Transform the URL if USE_DOCKER_HOST is set to "true"
64 | const transformedUrl = transformDockerUrl(serverParams.url);
65 |
66 | if (!serverParams.oauth_tokens) {
67 | transport = new SSEClientTransport(new URL(transformedUrl));
68 | } else {
69 | const headers: HeadersInit = {};
70 | headers[
71 | "Authorization"
72 | ] = `Bearer ${serverParams.oauth_tokens.access_token}`;
73 | transport = new SSEClientTransport(new URL(transformedUrl), {
74 | requestInit: {
75 | headers,
76 | },
77 | eventSourceInit: {
78 | fetch: (url, init) => fetch(url, { ...init, headers }),
79 | },
80 | });
81 | }
82 | } else if (serverParams.type === "STREAMABLE_HTTP" && serverParams.url) {
83 | // Transform the URL if USE_DOCKER_HOST is set to "true"
84 | const transformedUrl = transformDockerUrl(serverParams.url);
85 |
86 | if (!serverParams.oauth_tokens) {
87 | transport = new StreamableHTTPClientTransport(new URL(transformedUrl));
88 | } else {
89 | const headers: HeadersInit = {};
90 | headers[
91 | "Authorization"
92 | ] = `Bearer ${serverParams.oauth_tokens.access_token}`;
93 | transport = new StreamableHTTPClientTransport(new URL(transformedUrl), {
94 | requestInit: {
95 | headers,
96 | },
97 | });
98 | }
99 | } else {
100 | console.error(`Unsupported server type: ${serverParams.type}`);
101 | return { client: undefined, transport: undefined };
102 | }
103 |
104 | const client = new Client(
105 | {
106 | name: "MetaMCP",
107 | version: "0.6.5",
108 | },
109 | {
110 | capabilities: {
111 | prompts: {},
112 | resources: { subscribe: true },
113 | tools: {},
114 | },
115 | }
116 | );
117 | return { client, transport };
118 | };
119 |
120 | export const connectMetaMcpClient = async (
121 | client: Client,
122 | transport: Transport
123 | ): Promise => {
124 | const waitFor = 2500;
125 | const retries = 3;
126 | let count = 0;
127 | let retry = true;
128 |
129 | while (retry) {
130 | try {
131 | await client.connect(transport);
132 |
133 | return {
134 | client,
135 | cleanup: async () => {
136 | await transport.close();
137 | await client.close();
138 | },
139 | };
140 | } catch (error) {
141 | count++;
142 | retry = count < retries;
143 | if (retry) {
144 | try {
145 | await client.close();
146 | } catch {}
147 | await sleep(waitFor);
148 | }
149 | }
150 | }
151 | };
152 |
--------------------------------------------------------------------------------
/src/fetch-capabilities.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { getMetaMcpApiBaseUrl, getMetaMcpApiKey } from "./utils.js";
3 |
4 | export enum ProfileCapability {
5 | TOOLS_MANAGEMENT = "TOOLS_MANAGEMENT",
6 | TOOL_LOGS = "TOOL_LOGS",
7 | }
8 |
9 | let _capabilitiesCache: ProfileCapability[] | null = null;
10 | let _capabilitiesCacheTimestamp: number = 0;
11 | const CACHE_TTL_MS = 1000; // 1 second cache TTL
12 |
13 | export async function getProfileCapabilities(
14 | forceRefresh: boolean = false
15 | ): Promise {
16 | const currentTime = Date.now();
17 | const cacheAge = currentTime - _capabilitiesCacheTimestamp;
18 |
19 | // Use cache if it exists, is not null, and either:
20 | // 1. forceRefresh is false, or
21 | // 2. forceRefresh is true but cache is less than 1 second old
22 | if (
23 | _capabilitiesCache !== null &&
24 | (!forceRefresh || cacheAge < CACHE_TTL_MS)
25 | ) {
26 | return _capabilitiesCache;
27 | }
28 |
29 | try {
30 | const apiKey = getMetaMcpApiKey();
31 | const apiBaseUrl = getMetaMcpApiBaseUrl();
32 |
33 | if (!apiKey) {
34 | console.error(
35 | "METAMCP_API_KEY is not set. Please set it via environment variable or command line argument."
36 | );
37 | return _capabilitiesCache || [];
38 | }
39 |
40 | const headers = { Authorization: `Bearer ${apiKey}` };
41 | const response = await axios.get(`${apiBaseUrl}/api/profile-capabilities`, {
42 | headers,
43 | });
44 | const data = response.data;
45 |
46 | // Access the 'profileCapabilities' array in the response
47 | if (data && data.profileCapabilities) {
48 | const capabilities = data.profileCapabilities
49 | .map((capability: string) => {
50 | // Map string to enum value if it exists, otherwise return undefined
51 | return ProfileCapability[
52 | capability as keyof typeof ProfileCapability
53 | ];
54 | })
55 | .filter(
56 | (
57 | capability: ProfileCapability | undefined
58 | ): capability is ProfileCapability => capability !== undefined
59 | );
60 |
61 | _capabilitiesCache = capabilities;
62 | _capabilitiesCacheTimestamp = currentTime;
63 | return capabilities;
64 | }
65 |
66 | return _capabilitiesCache || [];
67 | } catch (error) {
68 | // Return empty array if API doesn't exist or has errors
69 | if (_capabilitiesCache !== null) {
70 | return _capabilitiesCache;
71 | }
72 | return [];
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/fetch-metamcp.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import {
3 | getDefaultEnvironment,
4 | getMetaMcpApiBaseUrl,
5 | getMetaMcpApiKey,
6 | } from "./utils.js";
7 |
8 | // Define IOType for stderr handling
9 | export type IOType = "overlapped" | "pipe" | "ignore" | "inherit";
10 |
11 | // Define a new interface for server parameters that can be STDIO, SSE or STREAMABLE_HTTP
12 | export interface ServerParameters {
13 | uuid: string;
14 | name: string;
15 | description: string;
16 | type?: "STDIO" | "SSE" | "STREAMABLE_HTTP"; // Optional field, defaults to "STDIO" when undefined
17 | command?: string | null;
18 | args?: string[] | null;
19 | env?: Record | null;
20 | stderr?: IOType; // Optional field for stderr handling, defaults to "ignore"
21 | url?: string | null;
22 | created_at: string;
23 | profile_uuid: string;
24 | status: string;
25 | oauth_tokens?: {
26 | access_token: string;
27 | token_type: string;
28 | expires_in?: number | undefined;
29 | scope?: string | undefined;
30 | refresh_token?: string | undefined;
31 | } | null;
32 | }
33 |
34 | let _mcpServersCache: Record | null = null;
35 | let _mcpServersCacheTimestamp: number = 0;
36 | const CACHE_TTL_MS = 1000; // 1 second cache TTL
37 |
38 | export async function getMcpServers(
39 | forceRefresh: boolean = false
40 | ): Promise> {
41 | const currentTime = Date.now();
42 | const cacheAge = currentTime - _mcpServersCacheTimestamp;
43 |
44 | // Use cache if it exists, is not null, and either:
45 | // 1. forceRefresh is false, or
46 | // 2. forceRefresh is true but cache is less than 1 second old
47 | if (_mcpServersCache !== null && (!forceRefresh || cacheAge < CACHE_TTL_MS)) {
48 | return _mcpServersCache;
49 | }
50 |
51 | try {
52 | const apiKey = getMetaMcpApiKey();
53 | const apiBaseUrl = getMetaMcpApiBaseUrl();
54 |
55 | if (!apiKey) {
56 | console.error(
57 | "METAMCP_API_KEY is not set. Please set it via environment variable or command line argument."
58 | );
59 | return _mcpServersCache || {};
60 | }
61 |
62 | const headers = { Authorization: `Bearer ${apiKey}` };
63 | const response = await axios.get(`${apiBaseUrl}/api/mcp-servers`, {
64 | headers,
65 | });
66 | const data = response.data;
67 |
68 | const serverDict: Record = {};
69 | for (const serverParams of data) {
70 | const params: ServerParameters = {
71 | ...serverParams,
72 | type: serverParams.type || "STDIO",
73 | };
74 |
75 | // Process based on server type
76 | if (params.type === "STDIO") {
77 | if ("args" in params && !params.args) {
78 | params.args = undefined;
79 | }
80 |
81 | params.env = {
82 | ...getDefaultEnvironment(),
83 | ...(params.env || {}),
84 | };
85 | } else if (params.type === "SSE" || params.type === "STREAMABLE_HTTP") {
86 | // For SSE or STREAMABLE_HTTP servers, ensure url is present
87 | if (!params.url) {
88 | console.warn(
89 | `${params.type} server ${params.uuid} is missing url field, skipping`
90 | );
91 | continue;
92 | }
93 | }
94 |
95 | const uuid = params.uuid;
96 | if (uuid) {
97 | serverDict[uuid] = params;
98 | }
99 | }
100 |
101 | _mcpServersCache = serverDict;
102 | _mcpServersCacheTimestamp = currentTime;
103 | return serverDict;
104 | } catch (error) {
105 | if (_mcpServersCache !== null) {
106 | return _mcpServersCache;
107 | }
108 | return {};
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/fetch-tools.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { getMetaMcpApiBaseUrl, getMetaMcpApiKey } from "./utils.js";
3 |
4 | enum ToolStatus {
5 | ACTIVE = "ACTIVE",
6 | INACTIVE = "INACTIVE",
7 | }
8 |
9 | // Define interface for tool parameters with only required fields
10 | export interface ToolParameters {
11 | mcp_server_uuid: string;
12 | name: string;
13 | status: ToolStatus;
14 | }
15 |
16 | let _toolsCache: Record | null = null;
17 | let _toolsCacheTimestamp: number = 0;
18 | const CACHE_TTL_MS = 1000; // 1 second cache TTL
19 |
20 | export async function getInactiveTools(
21 | forceRefresh: boolean = false
22 | ): Promise> {
23 | const currentTime = Date.now();
24 | const cacheAge = currentTime - _toolsCacheTimestamp;
25 |
26 | // Use cache if it exists, is not null, and either:
27 | // 1. forceRefresh is false, or
28 | // 2. forceRefresh is true but cache is less than 1 second old
29 | if (_toolsCache !== null && (!forceRefresh || cacheAge < CACHE_TTL_MS)) {
30 | return _toolsCache;
31 | }
32 |
33 | try {
34 | const apiKey = getMetaMcpApiKey();
35 | const apiBaseUrl = getMetaMcpApiBaseUrl();
36 |
37 | if (!apiKey) {
38 | console.error(
39 | "METAMCP_API_KEY is not set. Please set it via environment variable or command line argument."
40 | );
41 | return _toolsCache || {};
42 | }
43 |
44 | const headers = { Authorization: `Bearer ${apiKey}` };
45 | const response = await axios.get(
46 | `${apiBaseUrl}/api/tools?status=${ToolStatus.INACTIVE}`,
47 | {
48 | headers,
49 | }
50 | );
51 | const data = response.data;
52 |
53 | const toolDict: Record = {};
54 | // Access the 'results' array in the response
55 | if (data && data.results) {
56 | for (const tool of data.results) {
57 | const params: ToolParameters = {
58 | mcp_server_uuid: tool.mcp_server_uuid,
59 | name: tool.name,
60 | status: tool.status,
61 | };
62 |
63 | const uniqueId = `${tool.mcp_server_uuid}:${tool.name}`;
64 | toolDict[uniqueId] = params;
65 | }
66 | }
67 |
68 | _toolsCache = toolDict;
69 | _toolsCacheTimestamp = currentTime;
70 | return toolDict;
71 | } catch (error) {
72 | // Return empty object if API doesn't exist or has errors
73 | if (_toolsCache !== null) {
74 | return _toolsCache;
75 | }
76 | return {};
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4 | import { createServer } from "./mcp-proxy.js";
5 | import { Command } from "commander";
6 | import { reportAllTools } from "./report-tools.js";
7 | import { cleanupAllSessions } from "./sessions.js";
8 | import { startSSEServer } from "./sse.js";
9 | import { startStreamableHTTPServer } from "./streamable-http.js";
10 | import { IOType } from "./fetch-metamcp.js";
11 |
12 | const program = new Command();
13 |
14 | program
15 | .name("mcp-server-metamcp")
16 | .description("MetaMCP MCP Server - The One MCP to manage all your MCPs")
17 | .option(
18 | "--metamcp-api-key ",
19 | "API key for MetaMCP (can also be set via METAMCP_API_KEY env var)"
20 | )
21 | .option(
22 | "--metamcp-api-base-url ",
23 | "Base URL for MetaMCP API (can also be set via METAMCP_API_BASE_URL env var)"
24 | )
25 | .option(
26 | "--report",
27 | "Fetch all MCPs, initialize clients, and report tools to MetaMCP API"
28 | )
29 | .option("--transport ", "Transport type to use (stdio, sse, or streamable-http)", "stdio")
30 | .option("--port ", "Port to use for SSE or Streamable HTTP transport, defaults to 12006", "12006")
31 | .option("--require-api-auth", "Require API key in SSE or Streamable HTTP URL path")
32 | .option("--stateless", "Use stateless mode for Streamable HTTP transport")
33 | .option(
34 | "--use-docker-host",
35 | "Transform localhost URLs to use host.docker.internal (can also be set via USE_DOCKER_HOST env var)"
36 | )
37 | .option(
38 | "--stderr ",
39 | "Stderr handling for STDIO transport (overlapped, pipe, ignore, inherit)",
40 | "ignore"
41 | )
42 | .parse(process.argv);
43 |
44 | const options = program.opts();
45 |
46 | // Validate stderr option
47 | const validStderrTypes: IOType[] = ["overlapped", "pipe", "ignore", "inherit"];
48 | if (!validStderrTypes.includes(options.stderr as IOType)) {
49 | console.error(`Invalid stderr type: ${options.stderr}. Must be one of: ${validStderrTypes.join(", ")}`);
50 | process.exit(1);
51 | }
52 |
53 | // Set environment variables from command line arguments
54 | if (options.metamcpApiKey) {
55 | process.env.METAMCP_API_KEY = options.metamcpApiKey;
56 | }
57 | if (options.metamcpApiBaseUrl) {
58 | process.env.METAMCP_API_BASE_URL = options.metamcpApiBaseUrl;
59 | }
60 | if (options.useDockerHost) {
61 | process.env.USE_DOCKER_HOST = "true";
62 | }
63 | if (options.stderr) {
64 | process.env.METAMCP_STDERR = options.stderr;
65 | }
66 |
67 | async function main() {
68 | // If --report flag is set, run the reporting function instead of starting the server
69 | if (options.report) {
70 | await reportAllTools();
71 | await cleanupAllSessions();
72 | return;
73 | }
74 |
75 | const { server, cleanup } = await createServer();
76 |
77 | if (options.transport.toLowerCase() === "sse") {
78 | // Start SSE server
79 | const port = parseInt(options.port) || 12006;
80 | const sseCleanup = await startSSEServer(server, {
81 | port,
82 | requireApiAuth: options.requireApiAuth,
83 | });
84 |
85 | // Cleanup on exit
86 | const handleExit = async () => {
87 | await cleanup();
88 | await sseCleanup();
89 | await server.close();
90 | process.exit(0);
91 | };
92 |
93 | process.on("SIGINT", handleExit);
94 | process.on("SIGTERM", handleExit);
95 | } else if (options.transport.toLowerCase() === "streamable-http") {
96 | // Start Streamable HTTP server
97 | const port = parseInt(options.port) || 12006;
98 | const streamableHttpCleanup = await startStreamableHTTPServer(server, {
99 | port,
100 | requireApiAuth: options.requireApiAuth,
101 | stateless: options.stateless,
102 | });
103 |
104 | // Cleanup on exit
105 | const handleExit = async () => {
106 | await cleanup();
107 | await streamableHttpCleanup();
108 | await server.close();
109 | process.exit(0);
110 | };
111 |
112 | process.on("SIGINT", handleExit);
113 | process.on("SIGTERM", handleExit);
114 | } else {
115 | // Default: Start stdio server
116 | const transport = new StdioServerTransport();
117 | await server.connect(transport);
118 |
119 | const handleExit = async () => {
120 | await cleanup();
121 | await transport.close();
122 | await server.close();
123 | process.exit(0);
124 | };
125 |
126 | // Cleanup on exit
127 | process.on("SIGINT", handleExit);
128 | process.on("SIGTERM", handleExit);
129 |
130 | process.stdin.resume();
131 | process.stdin.on("end", handleExit);
132 | process.stdin.on("close", handleExit);
133 | }
134 | }
135 |
136 | main().catch((error) => {
137 | console.error("Server error:", error);
138 | });
139 |
--------------------------------------------------------------------------------
/src/mcp-proxy.ts:
--------------------------------------------------------------------------------
1 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2 | import {
3 | CallToolRequestSchema,
4 | GetPromptRequestSchema,
5 | ListPromptsRequestSchema,
6 | ListResourcesRequestSchema,
7 | ListToolsRequestSchema,
8 | ReadResourceRequestSchema,
9 | Tool,
10 | ListToolsResultSchema,
11 | ListPromptsResultSchema,
12 | ListResourcesResultSchema,
13 | ReadResourceResultSchema,
14 | ListResourceTemplatesRequestSchema,
15 | ListResourceTemplatesResultSchema,
16 | ResourceTemplate,
17 | CompatibilityCallToolResultSchema,
18 | GetPromptResultSchema,
19 | } from "@modelcontextprotocol/sdk/types.js";
20 | import { z } from "zod";
21 | import { getMcpServers } from "./fetch-metamcp.js";
22 | import { getSessionKey, sanitizeName } from "./utils.js";
23 | import { cleanupAllSessions, getSession, initSessions } from "./sessions.js";
24 | import { ConnectedClient } from "./client.js";
25 | import { reportToolsToMetaMcp } from "./report-tools.js";
26 | import { getInactiveTools, ToolParameters } from "./fetch-tools.js";
27 | import {
28 | getProfileCapabilities,
29 | ProfileCapability,
30 | } from "./fetch-capabilities.js";
31 | import { ToolLogManager } from "./tool-logs.js";
32 |
33 | const toolToClient: Record = {};
34 | const toolToServerUuid: Record = {};
35 | const promptToClient: Record = {};
36 | const resourceToClient: Record = {};
37 | const inactiveToolsMap: Record = {};
38 |
39 | export const createServer = async () => {
40 | const server = new Server(
41 | {
42 | name: "MetaMCP",
43 | version: "0.6.5",
44 | },
45 | {
46 | capabilities: {
47 | prompts: {},
48 | resources: {},
49 | tools: {},
50 | },
51 | }
52 | );
53 |
54 | // Initialize sessions in the background when server starts
55 | initSessions().catch();
56 |
57 | // List Tools Handler
58 | server.setRequestHandler(ListToolsRequestSchema, async (request) => {
59 | const profileCapabilities = await getProfileCapabilities(true);
60 | const serverParams = await getMcpServers(true);
61 |
62 | // Fetch inactive tools only if tools management capability is present
63 | let inactiveTools: Record = {};
64 | if (profileCapabilities.includes(ProfileCapability.TOOLS_MANAGEMENT)) {
65 | inactiveTools = await getInactiveTools(true);
66 | // Clear existing inactive tools map before rebuilding
67 | Object.keys(inactiveToolsMap).forEach(
68 | (key) => delete inactiveToolsMap[key]
69 | );
70 | }
71 |
72 | const allTools: Tool[] = [];
73 |
74 | await Promise.allSettled(
75 | Object.entries(serverParams).map(async ([uuid, params]) => {
76 | const sessionKey = getSessionKey(uuid, params);
77 | const session = await getSession(sessionKey, uuid, params);
78 | if (!session) return;
79 |
80 | const capabilities = session.client.getServerCapabilities();
81 | if (!capabilities?.tools) return;
82 |
83 | const serverName = session.client.getServerVersion()?.name || "";
84 | try {
85 | const result = await session.client.request(
86 | {
87 | method: "tools/list",
88 | params: { _meta: request.params?._meta },
89 | },
90 | ListToolsResultSchema
91 | );
92 |
93 | const toolsWithSource =
94 | result.tools
95 | ?.filter((tool) => {
96 | // Only filter inactive tools if tools management is enabled
97 | if (
98 | profileCapabilities.includes(
99 | ProfileCapability.TOOLS_MANAGEMENT
100 | )
101 | ) {
102 | return !inactiveTools[`${uuid}:${tool.name}`];
103 | }
104 | return true;
105 | })
106 | .map((tool) => {
107 | const toolName = `${sanitizeName(serverName)}__${tool.name}`;
108 | toolToClient[toolName] = session;
109 | toolToServerUuid[toolName] = uuid;
110 | return {
111 | ...tool,
112 | name: toolName,
113 | description: tool.description,
114 | };
115 | }) || [];
116 |
117 | // Update our inactive tools map only if tools management is enabled
118 | if (
119 | profileCapabilities.includes(ProfileCapability.TOOLS_MANAGEMENT)
120 | ) {
121 | result.tools?.forEach((tool) => {
122 | const isInactive = inactiveTools[`${uuid}:${tool.name}`];
123 | if (isInactive) {
124 | const formattedName = `${sanitizeName(serverName)}__${
125 | tool.name
126 | }`;
127 | inactiveToolsMap[formattedName] = true;
128 | }
129 | });
130 |
131 | // Report full tools for this server
132 | reportToolsToMetaMcp(
133 | result.tools.map((tool) => ({
134 | name: tool.name,
135 | description: tool.description,
136 | toolSchema: tool.inputSchema,
137 | mcp_server_uuid: uuid,
138 | }))
139 | ).catch();
140 | }
141 |
142 | allTools.push(...toolsWithSource);
143 | } catch (error) {
144 | console.error(`Error fetching tools from: ${serverName}`, error);
145 | }
146 | })
147 | );
148 |
149 | return { tools: allTools };
150 | });
151 |
152 | // Call Tool Handler
153 | server.setRequestHandler(CallToolRequestSchema, async (request) => {
154 | const { name, arguments: args } = request.params;
155 | const originalToolName = name.split("__")[1];
156 | const clientForTool = toolToClient[name];
157 | const toolLogManager = ToolLogManager.getInstance();
158 | let logId: string | undefined;
159 | let startTime = Date.now();
160 |
161 | if (!clientForTool) {
162 | throw new Error(`Unknown tool: ${name}`);
163 | }
164 |
165 | // Get MCP server UUID for the tool
166 | const mcpServerUuid = toolToServerUuid[name] || "";
167 |
168 | if (!mcpServerUuid) {
169 | console.error(`Could not determine MCP server UUID for tool: ${name}`);
170 | }
171 |
172 | // Get profile capabilities
173 | const profileCapabilities = await getProfileCapabilities();
174 |
175 | // Only check inactive tools if tools management capability is present
176 | if (
177 | profileCapabilities.includes(ProfileCapability.TOOLS_MANAGEMENT) &&
178 | inactiveToolsMap[name]
179 | ) {
180 | throw new Error(`Tool is inactive: ${name}`);
181 | }
182 |
183 | // Check if TOOL_LOGS capability is enabled
184 | const hasToolsLogCapability = profileCapabilities.includes(
185 | ProfileCapability.TOOL_LOGS
186 | );
187 |
188 | try {
189 | // Create initial pending log only if TOOL_LOGS capability is present
190 | if (hasToolsLogCapability) {
191 | const log = await toolLogManager.createLog(
192 | originalToolName,
193 | mcpServerUuid,
194 | args || {}
195 | );
196 | logId = log.id;
197 | }
198 |
199 | // Reset the timer right before making the actual tool call
200 | startTime = Date.now();
201 |
202 | // Use the correct schema for tool calls
203 | const result = await clientForTool.client.request(
204 | {
205 | method: "tools/call",
206 | params: {
207 | name: originalToolName,
208 | arguments: args || {},
209 | _meta: {
210 | progressToken: request.params._meta?.progressToken,
211 | },
212 | },
213 | },
214 | CompatibilityCallToolResultSchema
215 | );
216 |
217 | const executionTime = Date.now() - startTime;
218 |
219 | // Update log with success result only if TOOL_LOGS capability is present
220 | if (hasToolsLogCapability && logId) {
221 | try {
222 | await toolLogManager.completeLog(logId, result, executionTime);
223 | } catch (logError) {}
224 | }
225 |
226 | return result;
227 | } catch (error: any) {
228 | const executionTime = Date.now() - startTime;
229 |
230 | // Update log with error only if TOOL_LOGS capability is present
231 | if (hasToolsLogCapability && logId) {
232 | try {
233 | await toolLogManager.failLog(
234 | logId,
235 | error.message || "Unknown error",
236 | executionTime
237 | );
238 | } catch (logError) {}
239 | }
240 |
241 | console.error(
242 | `Error calling tool "${name}" through ${
243 | clientForTool.client.getServerVersion()?.name || "unknown"
244 | }:`,
245 | error
246 | );
247 | throw error;
248 | }
249 | });
250 |
251 | // Get Prompt Handler
252 | server.setRequestHandler(GetPromptRequestSchema, async (request) => {
253 | const { name } = request.params;
254 | const clientForPrompt = promptToClient[name];
255 |
256 | if (!clientForPrompt) {
257 | throw new Error(`Unknown prompt: ${name}`);
258 | }
259 |
260 | try {
261 | const promptName = name.split("__")[1];
262 | const response = await clientForPrompt.client.request(
263 | {
264 | method: "prompts/get",
265 | params: {
266 | name: promptName,
267 | arguments: request.params.arguments || {},
268 | _meta: request.params._meta,
269 | },
270 | },
271 | GetPromptResultSchema
272 | );
273 |
274 | return response;
275 | } catch (error) {
276 | console.error(
277 | `Error getting prompt through ${
278 | clientForPrompt.client.getServerVersion()?.name
279 | }:`,
280 | error
281 | );
282 | throw error;
283 | }
284 | });
285 |
286 | // List Prompts Handler
287 | server.setRequestHandler(ListPromptsRequestSchema, async (request) => {
288 | const serverParams = await getMcpServers(true);
289 | const allPrompts: z.infer["prompts"] = [];
290 |
291 | await Promise.allSettled(
292 | Object.entries(serverParams).map(async ([uuid, params]) => {
293 | const sessionKey = getSessionKey(uuid, params);
294 | const session = await getSession(sessionKey, uuid, params);
295 | if (!session) return;
296 |
297 | const capabilities = session.client.getServerCapabilities();
298 | if (!capabilities?.prompts) return;
299 |
300 | const serverName = session.client.getServerVersion()?.name || "";
301 | try {
302 | const result = await session.client.request(
303 | {
304 | method: "prompts/list",
305 | params: {
306 | cursor: request.params?.cursor,
307 | _meta: request.params?._meta,
308 | },
309 | },
310 | ListPromptsResultSchema
311 | );
312 |
313 | if (result.prompts) {
314 | const promptsWithSource = result.prompts.map((prompt) => {
315 | const promptName = `${sanitizeName(serverName)}__${prompt.name}`;
316 | promptToClient[promptName] = session;
317 | return {
318 | ...prompt,
319 | name: promptName,
320 | description: prompt.description || "",
321 | };
322 | });
323 | allPrompts.push(...promptsWithSource);
324 | }
325 | } catch (error) {
326 | console.error(`Error fetching prompts from: ${serverName}`, error);
327 | }
328 | })
329 | );
330 |
331 | return {
332 | prompts: allPrompts,
333 | nextCursor: request.params?.cursor,
334 | };
335 | });
336 |
337 | // List Resources Handler
338 | server.setRequestHandler(ListResourcesRequestSchema, async (request) => {
339 | const serverParams = await getMcpServers(true);
340 | const allResources: z.infer["resources"] =
341 | [];
342 |
343 | await Promise.allSettled(
344 | Object.entries(serverParams).map(async ([uuid, params]) => {
345 | const sessionKey = getSessionKey(uuid, params);
346 | const session = await getSession(sessionKey, uuid, params);
347 | if (!session) return;
348 |
349 | const capabilities = session.client.getServerCapabilities();
350 | if (!capabilities?.resources) return;
351 |
352 | const serverName = session.client.getServerVersion()?.name || "";
353 | try {
354 | const result = await session.client.request(
355 | {
356 | method: "resources/list",
357 | params: {
358 | cursor: request.params?.cursor,
359 | _meta: request.params?._meta,
360 | },
361 | },
362 | ListResourcesResultSchema
363 | );
364 |
365 | if (result.resources) {
366 | const resourcesWithSource = result.resources.map((resource) => {
367 | resourceToClient[resource.uri] = session;
368 | return {
369 | ...resource,
370 | name: resource.name || "",
371 | };
372 | });
373 | allResources.push(...resourcesWithSource);
374 | }
375 | } catch (error) {
376 | console.error(`Error fetching resources from: ${serverName}`, error);
377 | }
378 | })
379 | );
380 |
381 | return {
382 | resources: allResources,
383 | nextCursor: request.params?.cursor,
384 | };
385 | });
386 |
387 | // Read Resource Handler
388 | server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
389 | const { uri } = request.params;
390 | const clientForResource = resourceToClient[uri];
391 |
392 | if (!clientForResource) {
393 | throw new Error(`Unknown resource: ${uri}`);
394 | }
395 |
396 | try {
397 | return await clientForResource.client.request(
398 | {
399 | method: "resources/read",
400 | params: {
401 | uri,
402 | _meta: request.params._meta,
403 | },
404 | },
405 | ReadResourceResultSchema
406 | );
407 | } catch (error) {
408 | console.error(
409 | `Error reading resource through ${
410 | clientForResource.client.getServerVersion()?.name
411 | }:`,
412 | error
413 | );
414 | throw error;
415 | }
416 | });
417 |
418 | // List Resource Templates Handler
419 | server.setRequestHandler(
420 | ListResourceTemplatesRequestSchema,
421 | async (request) => {
422 | const serverParams = await getMcpServers(true);
423 | const allTemplates: ResourceTemplate[] = [];
424 |
425 | await Promise.allSettled(
426 | Object.entries(serverParams).map(async ([uuid, params]) => {
427 | const sessionKey = getSessionKey(uuid, params);
428 | const session = await getSession(sessionKey, uuid, params);
429 | if (!session) return;
430 |
431 | const capabilities = session.client.getServerCapabilities();
432 | if (!capabilities?.resources) return;
433 |
434 | try {
435 | const result = await session.client.request(
436 | {
437 | method: "resources/templates/list",
438 | params: {
439 | cursor: request.params?.cursor,
440 | _meta: request.params?._meta,
441 | },
442 | },
443 | ListResourceTemplatesResultSchema
444 | );
445 |
446 | if (result.resourceTemplates) {
447 | const templatesWithSource = result.resourceTemplates.map(
448 | (template) => ({
449 | ...template,
450 | name: template.name || "",
451 | })
452 | );
453 | allTemplates.push(...templatesWithSource);
454 | }
455 | } catch (error) {
456 | return;
457 | }
458 | })
459 | );
460 |
461 | return {
462 | resourceTemplates: allTemplates,
463 | nextCursor: request.params?.cursor,
464 | };
465 | }
466 | );
467 |
468 | const cleanup = async () => {
469 | await cleanupAllSessions();
470 | };
471 |
472 | return { server, cleanup };
473 | };
474 |
--------------------------------------------------------------------------------
/src/report-tools.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { getMetaMcpApiBaseUrl, getMetaMcpApiKey } from "./utils.js";
3 | import { getMcpServers } from "./fetch-metamcp.js";
4 | import { initSessions, getSession } from "./sessions.js";
5 | import { getSessionKey } from "./utils.js";
6 | import { ListToolsResultSchema } from "@modelcontextprotocol/sdk/types.js";
7 |
8 | // Define interface for tool data structure
9 | export interface MetaMcpTool {
10 | name: string;
11 | description?: string;
12 | toolSchema: any;
13 | mcp_server_uuid: string;
14 | }
15 |
16 | // API route handler for submitting tools to MetaMCP
17 | export async function reportToolsToMetaMcp(tools: MetaMcpTool[]) {
18 | try {
19 | const apiKey = getMetaMcpApiKey();
20 | const apiBaseUrl = getMetaMcpApiBaseUrl();
21 |
22 | if (!apiKey) {
23 | return { error: "API key not set" };
24 | }
25 |
26 | // Validate that tools is an array
27 | if (!Array.isArray(tools) || tools.length === 0) {
28 | return {
29 | error: "Request must include a non-empty array of tools",
30 | status: 400,
31 | };
32 | }
33 |
34 | // Validate required fields for all tools and prepare for submission
35 | const validTools = [];
36 | const errors = [];
37 |
38 | for (const tool of tools) {
39 | const { name, description, toolSchema, mcp_server_uuid } = tool;
40 |
41 | // Validate required fields for each tool
42 | if (!name || !toolSchema || !mcp_server_uuid) {
43 | errors.push({
44 | tool,
45 | error:
46 | "Missing required fields: name, toolSchema, or mcp_server_uuid",
47 | });
48 | continue;
49 | }
50 |
51 | validTools.push({
52 | name,
53 | description,
54 | toolSchema,
55 | mcp_server_uuid,
56 | });
57 | }
58 |
59 | // Submit valid tools to MetaMCP API
60 | let results: any[] = [];
61 | if (validTools.length > 0) {
62 | try {
63 | const response = await axios.post(
64 | `${apiBaseUrl}/api/tools`,
65 | { tools: validTools },
66 | {
67 | headers: {
68 | "Content-Type": "application/json",
69 | Authorization: `Bearer ${apiKey}`,
70 | },
71 | }
72 | );
73 |
74 | results = response.data.results || [];
75 | } catch (error: any) {
76 | if (error.response) {
77 | // The request was made and the server responded with a status code outside of 2xx
78 | return {
79 | error: error.response.data.error || "Failed to submit tools",
80 | status: error.response.status,
81 | details: error.response.data,
82 | };
83 | } else if (error.request) {
84 | // The request was made but no response was received
85 | return {
86 | error: "No response received from server",
87 | details: error.request,
88 | };
89 | } else {
90 | // Something happened in setting up the request
91 | return {
92 | error: "Error setting up request",
93 | details: error.message,
94 | };
95 | }
96 | }
97 | }
98 |
99 | return {
100 | results,
101 | errors,
102 | success: results.length > 0,
103 | failureCount: errors.length,
104 | successCount: results.length,
105 | };
106 | } catch (error: any) {
107 | return {
108 | error: "Failed to process tools request",
109 | status: 500,
110 | };
111 | }
112 | }
113 |
114 | // Function to fetch all MCP servers, initialize clients, and report tools to MetaMCP API
115 | export async function reportAllTools() {
116 | console.log("Fetching all MCPs and initializing clients...");
117 |
118 | // Get all MCP servers
119 | const serverParams = await getMcpServers();
120 |
121 | // Initialize all sessions
122 | await initSessions();
123 |
124 | console.log(`Found ${Object.keys(serverParams).length} MCP servers`);
125 |
126 | // For each server, get its tools and report them
127 | await Promise.allSettled(
128 | Object.entries(serverParams).map(async ([uuid, params]) => {
129 | const sessionKey = getSessionKey(uuid, params);
130 | const session = await getSession(sessionKey, uuid, params);
131 |
132 | if (!session) {
133 | console.log(`Could not establish session for ${params.name} (${uuid})`);
134 | return;
135 | }
136 |
137 | const capabilities = session.client.getServerCapabilities();
138 | if (!capabilities?.tools) {
139 | console.log(`Server ${params.name} (${uuid}) does not support tools`);
140 | return;
141 | }
142 |
143 | try {
144 | console.log(`Fetching tools from ${params.name} (${uuid})...`);
145 |
146 | const result = await session.client.request(
147 | { method: "tools/list", params: {} },
148 | ListToolsResultSchema
149 | );
150 |
151 | if (result.tools && result.tools.length > 0) {
152 | console.log(
153 | `Reporting ${result.tools.length} tools from ${params.name} to MetaMCP API...`
154 | );
155 |
156 | const reportResult = await reportToolsToMetaMcp(
157 | result.tools.map((tool) => ({
158 | name: tool.name,
159 | description: tool.description,
160 | toolSchema: tool.inputSchema,
161 | mcp_server_uuid: uuid,
162 | }))
163 | );
164 |
165 | console.log(
166 | `Reported tools from ${params.name}: ${reportResult.successCount} succeeded, ${reportResult.failureCount} failed`
167 | );
168 | } else {
169 | console.log(`No tools found for ${params.name}`);
170 | }
171 | } catch (error) {
172 | console.error(`Error reporting tools for ${params.name}:`, error);
173 | }
174 | })
175 | );
176 |
177 | console.log("Finished reporting all tools to MetaMCP API");
178 | process.exit(0);
179 | }
180 |
--------------------------------------------------------------------------------
/src/sessions.ts:
--------------------------------------------------------------------------------
1 | import { getMcpServers, ServerParameters } from "./fetch-metamcp.js";
2 | import {
3 | ConnectedClient,
4 | createMetaMcpClient,
5 | connectMetaMcpClient,
6 | } from "./client.js";
7 | import { getSessionKey } from "./utils.js";
8 |
9 | const _sessions: Record = {};
10 |
11 | export const getSession = async (
12 | sessionKey: string,
13 | uuid: string,
14 | params: ServerParameters
15 | ): Promise => {
16 | if (sessionKey in _sessions) {
17 | return _sessions[sessionKey];
18 | } else {
19 | // Close existing session for this UUID if it exists with a different hash
20 | const old_session_keys = Object.keys(_sessions).filter((k) =>
21 | k.startsWith(`${uuid}_`)
22 | );
23 |
24 | await Promise.allSettled(
25 | old_session_keys.map(async (old_session_key) => {
26 | await _sessions[old_session_key].cleanup();
27 | delete _sessions[old_session_key];
28 | })
29 | );
30 |
31 | const { client, transport } = createMetaMcpClient(params);
32 | if (!client || !transport) {
33 | return;
34 | }
35 |
36 | const newClient = await connectMetaMcpClient(client, transport);
37 | if (!newClient) {
38 | return;
39 | }
40 |
41 | _sessions[sessionKey] = newClient;
42 |
43 | return newClient;
44 | }
45 | };
46 |
47 | export const initSessions = async (): Promise => {
48 | const serverParams = await getMcpServers(true);
49 |
50 | await Promise.allSettled(
51 | Object.entries(serverParams).map(async ([uuid, params]) => {
52 | const sessionKey = getSessionKey(uuid, params);
53 | try {
54 | await getSession(sessionKey, uuid, params);
55 | } catch (error) {}
56 | })
57 | );
58 | };
59 |
60 | export const cleanupAllSessions = async (): Promise => {
61 | await Promise.allSettled(
62 | Object.entries(_sessions).map(async ([sessionKey, session]) => {
63 | await session.cleanup();
64 | delete _sessions[sessionKey];
65 | })
66 | );
67 | };
68 |
--------------------------------------------------------------------------------
/src/sse.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4 |
5 | export interface SSEServerOptions {
6 | port: number;
7 | requireApiAuth?: boolean;
8 | }
9 |
10 | // Starts an SSE server and returns a cleanup function
11 | export async function startSSEServer(
12 | server: Server,
13 | options: SSEServerOptions
14 | ): Promise<() => Promise> {
15 | const app = express();
16 | const port = options.port || 12006;
17 | const requireApiAuth = options.requireApiAuth || false;
18 | const apiKey = process.env.METAMCP_API_KEY;
19 |
20 | // to support multiple simultaneous connections we have a lookup object from
21 | // sessionId to transport
22 | const transports: { [sessionId: string]: SSEServerTransport } = {};
23 |
24 | // Define the SSE endpoint based on authentication requirement
25 | const sseEndpoint = requireApiAuth ? `/:apiKey/sse` : `/sse`;
26 |
27 | app.get(sseEndpoint, async (req: express.Request, res: express.Response) => {
28 | // If API auth is required, validate the API key
29 | if (requireApiAuth) {
30 | const requestApiKey = req.params.apiKey;
31 | if (!apiKey || requestApiKey !== apiKey) {
32 | res.status(401).send("Unauthorized: Invalid API key");
33 | return;
34 | }
35 | }
36 |
37 | // Set the messages path based on authentication requirement
38 | const messagesPath = requireApiAuth ? `/${apiKey}/messages` : `/messages`;
39 | const transport = new SSEServerTransport(messagesPath, res);
40 | transports[transport.sessionId] = transport;
41 | res.on("close", () => {
42 | delete transports[transport.sessionId];
43 | });
44 | await server.connect(transport);
45 | });
46 |
47 | // Define the messages endpoint
48 | const messagesEndpoint = requireApiAuth ? `/:apiKey/messages` : `/messages`;
49 |
50 | app.post(
51 | messagesEndpoint,
52 | async (req: express.Request, res: express.Response) => {
53 | // If API auth is required, validate the API key
54 | if (requireApiAuth) {
55 | const requestApiKey = req.params.apiKey;
56 | if (!apiKey || requestApiKey !== apiKey) {
57 | res.status(401).send("Unauthorized: Invalid API key");
58 | return;
59 | }
60 | }
61 |
62 | const sessionId = req.query.sessionId as string;
63 | const transport = transports[sessionId];
64 | if (transport) {
65 | await transport.handlePostMessage(req, res);
66 | } else {
67 | res.status(400).send("No transport found for sessionId");
68 | }
69 | }
70 | );
71 |
72 | const serverInstance = app.listen(port, () => {
73 | const baseUrl = `http://localhost:${port}`;
74 | const sseUrl = requireApiAuth
75 | ? `${baseUrl}/${apiKey}/sse`
76 | : `${baseUrl}/sse`;
77 | console.log(`SSE server listening on port ${port}`);
78 | console.log(`SSE endpoint: ${sseUrl}`);
79 | });
80 |
81 | // Return cleanup function
82 | return async () => {
83 | // Close all active transports
84 | await Promise.all(
85 | Object.values(transports).map((transport) => transport.close())
86 | );
87 | serverInstance.close();
88 | };
89 | }
90 |
--------------------------------------------------------------------------------
/src/streamable-http.ts:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4 | import { randomUUID } from "crypto";
5 |
6 | export interface StreamableHTTPServerOptions {
7 | port: number;
8 | requireApiAuth?: boolean;
9 | stateless?: boolean;
10 | }
11 |
12 | // Starts a Streamable HTTP server and returns a cleanup function
13 | export async function startStreamableHTTPServer(
14 | server: Server,
15 | options: StreamableHTTPServerOptions
16 | ): Promise<() => Promise> {
17 | const app = express();
18 | app.use(express.json());
19 |
20 | const port = options.port || 12006;
21 | const requireApiAuth = options.requireApiAuth || false;
22 | const stateless = options.stateless || false;
23 | const apiKey = process.env.METAMCP_API_KEY;
24 |
25 | // Map to store transports by session ID (when using stateful mode)
26 | const transports: { [sessionId: string]: StreamableHTTPServerTransport } = {};
27 |
28 | // Define the MCP endpoint path based on authentication requirement
29 | const mcpEndpoint = requireApiAuth ? `/:apiKey/mcp` : `/mcp`;
30 |
31 | // Handle all HTTP methods for the MCP endpoint
32 | app.all(mcpEndpoint, async (req: express.Request, res: express.Response) => {
33 | // If API auth is required, validate the API key
34 | if (requireApiAuth) {
35 | const requestApiKey = req.params.apiKey;
36 | if (!apiKey || requestApiKey !== apiKey) {
37 | res.status(401).send("Unauthorized: Invalid API key");
38 | return;
39 | }
40 | }
41 |
42 | if (stateless) {
43 | // Stateless mode: Create a new transport for each request
44 | const transport = new StreamableHTTPServerTransport({
45 | sessionIdGenerator: undefined, // No session management
46 | });
47 |
48 | res.on("close", () => {
49 | transport.close();
50 | });
51 |
52 | try {
53 | // Connect to the server
54 | await server.connect(transport);
55 | // Handle the request
56 | await transport.handleRequest(req, res, req.body);
57 | } catch (error) {
58 | console.error("Error handling streamable HTTP request:", error);
59 | if (!res.headersSent) {
60 | res.status(500).json({
61 | jsonrpc: "2.0",
62 | error: {
63 | code: -32603,
64 | message: "Internal server error",
65 | },
66 | id: null,
67 | });
68 | }
69 | }
70 | } else {
71 | // Stateful mode: Use session management
72 | const sessionId = req.headers["mcp-session-id"] as string | undefined;
73 | let transport: StreamableHTTPServerTransport;
74 |
75 | if (sessionId && transports[sessionId]) {
76 | // Reuse existing transport
77 | transport = transports[sessionId];
78 | } else if (!sessionId && req.method === "POST") {
79 | // New initialization request
80 | transport = new StreamableHTTPServerTransport({
81 | sessionIdGenerator: () => randomUUID(),
82 | onsessioninitialized: (sessionId) => {
83 | // Store the transport by session ID
84 | transports[sessionId] = transport;
85 | }
86 | });
87 |
88 | // Clean up transport when closed
89 | transport.onclose = () => {
90 | if (transport.sessionId) {
91 | delete transports[transport.sessionId];
92 | }
93 | };
94 |
95 | // Connect to the server
96 | await server.connect(transport);
97 | } else {
98 | // Invalid request
99 | res.status(400).json({
100 | jsonrpc: "2.0",
101 | error: {
102 | code: -32000,
103 | message: "Bad Request: No valid session ID provided",
104 | },
105 | id: null,
106 | });
107 | return;
108 | }
109 |
110 | try {
111 | // Handle the request
112 | await transport.handleRequest(req, res, req.body);
113 | } catch (error) {
114 | console.error("Error handling streamable HTTP request:", error);
115 | if (!res.headersSent) {
116 | res.status(500).json({
117 | jsonrpc: "2.0",
118 | error: {
119 | code: -32603,
120 | message: "Internal server error",
121 | },
122 | id: null,
123 | });
124 | }
125 | }
126 | }
127 | });
128 |
129 | const serverInstance = app.listen(port, () => {
130 | const baseUrl = `http://localhost:${port}`;
131 | const mcpUrl = requireApiAuth ? `${baseUrl}/${apiKey}/mcp` : `${baseUrl}/mcp`;
132 | console.log(`Streamable HTTP server listening on port ${port}`);
133 | console.log(`MCP endpoint: ${mcpUrl}`);
134 | console.log(`Mode: ${stateless ? "Stateless" : "Stateful"}`);
135 | });
136 |
137 | // Return cleanup function
138 | return async () => {
139 | // Close all active transports
140 | await Promise.all(
141 | Object.values(transports).map((transport) => transport.close())
142 | );
143 | serverInstance.close();
144 | };
145 | }
--------------------------------------------------------------------------------
/src/tool-logs.ts:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 | import { getMetaMcpApiBaseUrl, getMetaMcpApiKey } from "./utils.js";
3 | import {
4 | ProfileCapability,
5 | getProfileCapabilities,
6 | } from "./fetch-capabilities.js";
7 |
8 | // Define status enum for tool execution
9 | export enum ToolExecutionStatus {
10 | SUCCESS = "SUCCESS",
11 | ERROR = "ERROR",
12 | PENDING = "PENDING",
13 | }
14 |
15 | // Define interface for tool execution log data
16 | export interface ToolExecutionLog {
17 | id?: string;
18 | tool_name: string;
19 | payload: any;
20 | status: ToolExecutionStatus;
21 | result?: any;
22 | mcp_server_uuid: string;
23 | error_message?: string | null;
24 | execution_time_ms: number;
25 | created_at?: string;
26 | updated_at?: string;
27 | }
28 |
29 | // Response interfaces
30 | export interface ToolLogResponse {
31 | id?: string;
32 | success: boolean;
33 | data?: any;
34 | error?: string;
35 | status?: number;
36 | details?: any;
37 | }
38 |
39 | // Class to manage tool execution logs
40 | export class ToolLogManager {
41 | private static instance: ToolLogManager;
42 | private logStore: Map = new Map();
43 |
44 | private constructor() {}
45 |
46 | public static getInstance(): ToolLogManager {
47 | if (!ToolLogManager.instance) {
48 | ToolLogManager.instance = new ToolLogManager();
49 | }
50 | return ToolLogManager.instance;
51 | }
52 |
53 | /**
54 | * Creates a new tool execution log
55 | * @param toolName Name of the tool
56 | * @param serverUuid UUID of the MCP server
57 | * @param payload The input parameters for the tool
58 | * @returns Log object with tracking ID
59 | */
60 | public async createLog(
61 | toolName: string,
62 | serverUuid: string,
63 | payload: any
64 | ): Promise {
65 | // Check for TOOL_LOGS capability first
66 | const profileCapabilities = await getProfileCapabilities();
67 | const hasToolsLogCapability = profileCapabilities.includes(
68 | ProfileCapability.TOOL_LOGS
69 | );
70 |
71 | // Generate a temporary ID for tracking
72 | const tempId = `${Date.now()}-${Math.random()
73 | .toString(36)
74 | .substring(2, 9)}`;
75 |
76 | const log: ToolExecutionLog = {
77 | id: tempId, // Will be replaced with the real ID from the API
78 | tool_name: toolName,
79 | mcp_server_uuid: serverUuid,
80 | payload,
81 | status: ToolExecutionStatus.PENDING,
82 | execution_time_ms: 0,
83 | created_at: new Date().toISOString(),
84 | };
85 |
86 | // Store in memory
87 | this.logStore.set(tempId, log);
88 |
89 | // Submit to API only if TOOL_LOGS capability is present
90 | if (hasToolsLogCapability) {
91 | const response = await reportToolExecutionLog(log);
92 |
93 | // Update with real ID if available
94 | if (response.success && response.data?.id) {
95 | const newId = response.data.id;
96 | log.id = newId;
97 | this.logStore.delete(tempId);
98 | this.logStore.set(newId, log);
99 | }
100 | }
101 |
102 | return log;
103 | }
104 |
105 | /**
106 | * Updates the status of a tool execution log
107 | * @param logId ID of the log to update
108 | * @param status New status
109 | * @param result Optional result data
110 | * @param errorMessage Optional error message
111 | * @param executionTimeMs Optional execution time in milliseconds
112 | * @returns Updated log
113 | */
114 | public async updateLogStatus(
115 | logId: string,
116 | status: ToolExecutionStatus,
117 | result?: any,
118 | errorMessage?: string | null,
119 | executionTimeMs?: number
120 | ): Promise {
121 | const log = this.logStore.get(logId);
122 |
123 | if (!log) {
124 | console.error(`Cannot update log: Log with ID ${logId} not found`);
125 | return null;
126 | }
127 |
128 | // Update log properties
129 | log.status = status;
130 | if (result !== undefined) log.result = result;
131 | if (errorMessage !== undefined) log.error_message = errorMessage;
132 | if (executionTimeMs !== undefined) log.execution_time_ms = executionTimeMs;
133 | log.updated_at = new Date().toISOString();
134 |
135 | // Update in memory
136 | this.logStore.set(logId, log);
137 |
138 | // Check for TOOL_LOGS capability before sending update to API
139 | const profileCapabilities = await getProfileCapabilities();
140 | const hasToolsLogCapability = profileCapabilities.includes(
141 | ProfileCapability.TOOL_LOGS
142 | );
143 |
144 | // Send update to API only if TOOL_LOGS capability is present
145 | if (hasToolsLogCapability) {
146 | await updateToolExecutionLog(logId, {
147 | status,
148 | result,
149 | error_message: errorMessage,
150 | execution_time_ms: executionTimeMs,
151 | });
152 | }
153 |
154 | return log;
155 | }
156 |
157 | /**
158 | * Get a log by ID
159 | * @param logId ID of the log
160 | * @returns Log or null if not found
161 | */
162 | public getLog(logId: string): ToolExecutionLog | null {
163 | return this.logStore.get(logId) || null;
164 | }
165 |
166 | /**
167 | * Complete the tool execution log with success status
168 | * @param logId ID of the log to complete
169 | * @param result Result data
170 | * @param executionTimeMs Execution time in milliseconds
171 | * @returns Updated log
172 | */
173 | public async completeLog(
174 | logId: string,
175 | result: any,
176 | executionTimeMs: number
177 | ): Promise {
178 | return this.updateLogStatus(
179 | logId,
180 | ToolExecutionStatus.SUCCESS,
181 | result,
182 | null,
183 | executionTimeMs
184 | );
185 | }
186 |
187 | /**
188 | * Mark the tool execution log as failed
189 | * @param logId ID of the log to fail
190 | * @param errorMessage Error message
191 | * @param executionTimeMs Execution time in milliseconds
192 | * @returns Updated log
193 | */
194 | public async failLog(
195 | logId: string,
196 | errorMessage: string,
197 | executionTimeMs: number
198 | ): Promise {
199 | return this.updateLogStatus(
200 | logId,
201 | ToolExecutionStatus.ERROR,
202 | null,
203 | errorMessage,
204 | executionTimeMs
205 | );
206 | }
207 | }
208 |
209 | /**
210 | * Reports a tool execution log to the MetaMCP API
211 | * @param logData The tool execution log data
212 | * @returns Result of the API call
213 | */
214 | export async function reportToolExecutionLog(
215 | logData: ToolExecutionLog
216 | ): Promise {
217 | try {
218 | // Check for TOOL_LOGS capability first
219 | const profileCapabilities = await getProfileCapabilities();
220 | const hasToolsLogCapability = profileCapabilities.includes(
221 | ProfileCapability.TOOL_LOGS
222 | );
223 |
224 | if (!hasToolsLogCapability) {
225 | return { success: false, error: "TOOL_LOGS capability not enabled" };
226 | }
227 |
228 | const apiKey = getMetaMcpApiKey();
229 | const apiBaseUrl = getMetaMcpApiBaseUrl();
230 |
231 | if (!apiKey) {
232 | return { success: false, error: "API key not set" };
233 | }
234 |
235 | // Validate required fields
236 | if (!logData.tool_name || !logData.mcp_server_uuid) {
237 | return {
238 | success: false,
239 | error: "Missing required fields: tool_name or mcp_server_uuid",
240 | status: 400,
241 | };
242 | }
243 |
244 | // Submit log to MetaMCP API
245 | try {
246 | const response = await axios.post(
247 | `${apiBaseUrl}/api/tool-execution-logs`,
248 | logData,
249 | {
250 | headers: {
251 | "Content-Type": "application/json",
252 | Authorization: `Bearer ${apiKey}`,
253 | },
254 | }
255 | );
256 |
257 | return {
258 | success: true,
259 | data: response.data,
260 | };
261 | } catch (error: any) {
262 | if (error.response) {
263 | // The request was made and the server responded with a status code outside of 2xx
264 | return {
265 | success: false,
266 | error:
267 | error.response.data.error || "Failed to submit tool execution log",
268 | status: error.response.status,
269 | details: error.response.data,
270 | };
271 | } else if (error.request) {
272 | // The request was made but no response was received
273 | return {
274 | success: false,
275 | error: "No response received from server",
276 | details: error.request,
277 | };
278 | } else {
279 | // Something happened in setting up the request
280 | return {
281 | success: false,
282 | error: "Error setting up request",
283 | details: error.message,
284 | };
285 | }
286 | }
287 | } catch (error: any) {
288 | return {
289 | success: false,
290 | error: "Failed to process tool execution log request",
291 | status: 500,
292 | details: error.message,
293 | };
294 | }
295 | }
296 |
297 | /**
298 | * Updates an existing tool execution log
299 | * @param logId The ID of the log to update
300 | * @param updateData The updated log data
301 | * @returns Result of the API call
302 | */
303 | export async function updateToolExecutionLog(
304 | logId: string,
305 | updateData: Partial
306 | ): Promise {
307 | try {
308 | // Check for TOOL_LOGS capability first
309 | const profileCapabilities = await getProfileCapabilities();
310 | const hasToolsLogCapability = profileCapabilities.includes(
311 | ProfileCapability.TOOL_LOGS
312 | );
313 |
314 | if (!hasToolsLogCapability) {
315 | return { success: false, error: "TOOL_LOGS capability not enabled" };
316 | }
317 |
318 | const apiKey = getMetaMcpApiKey();
319 | const apiBaseUrl = getMetaMcpApiBaseUrl();
320 |
321 | if (!apiKey) {
322 | return { success: false, error: "API key not set" };
323 | }
324 |
325 | if (!logId) {
326 | return {
327 | success: false,
328 | error: "Log ID is required for updates",
329 | };
330 | }
331 |
332 | // Submit update to MetaMCP API
333 | try {
334 | const response = await axios.put(
335 | `${apiBaseUrl}/api/tool-execution-logs/${logId}`,
336 | updateData,
337 | {
338 | headers: {
339 | "Content-Type": "application/json",
340 | Authorization: `Bearer ${apiKey}`,
341 | },
342 | }
343 | );
344 |
345 | return {
346 | success: true,
347 | data: response.data,
348 | };
349 | } catch (error: any) {
350 | if (error.response) {
351 | return {
352 | success: false,
353 | error:
354 | error.response.data.error || "Failed to update tool execution log",
355 | status: error.response.status,
356 | details: error.response.data,
357 | };
358 | } else if (error.request) {
359 | return {
360 | success: false,
361 | error: "No response received from server",
362 | details: error.request,
363 | };
364 | } else {
365 | return {
366 | success: false,
367 | error: "Error setting up request",
368 | details: error.message,
369 | };
370 | }
371 | }
372 | } catch (error: any) {
373 | return {
374 | success: false,
375 | error: "Failed to process update request",
376 | status: 500,
377 | details: error.message,
378 | };
379 | }
380 | }
381 |
382 | /**
383 | * Simple function to log a tool execution
384 | * @param toolName Name of the tool
385 | * @param serverUuid UUID of the MCP server
386 | * @param payload The input parameters for the tool
387 | * @param result The result of the tool execution
388 | * @param status Status of the execution
389 | * @param errorMessage Optional error message if execution failed
390 | * @param executionTimeMs Time taken to execute the tool in milliseconds
391 | * @returns Result of the API call
392 | */
393 | export async function logToolExecution(
394 | toolName: string,
395 | serverUuid: string,
396 | payload: any,
397 | result: any = null,
398 | status: ToolExecutionStatus = ToolExecutionStatus.SUCCESS,
399 | errorMessage: string | null = null,
400 | executionTimeMs: number = 0
401 | ): Promise {
402 | // Check for TOOL_LOGS capability first
403 | const profileCapabilities = await getProfileCapabilities();
404 | const hasToolsLogCapability = profileCapabilities.includes(
405 | ProfileCapability.TOOL_LOGS
406 | );
407 |
408 | if (!hasToolsLogCapability) {
409 | return { success: false, error: "TOOL_LOGS capability not enabled" };
410 | }
411 |
412 | const logData: ToolExecutionLog = {
413 | tool_name: toolName,
414 | mcp_server_uuid: serverUuid,
415 | payload,
416 | status,
417 | result,
418 | error_message: errorMessage,
419 | execution_time_ms: executionTimeMs,
420 | };
421 |
422 | return await reportToolExecutionLog(logData);
423 | }
424 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | import { ServerParameters } from "./fetch-metamcp.js";
2 | import crypto from "crypto";
3 |
4 | /**
5 | * Environment variables to inherit by default, if an environment is not explicitly given.
6 | */
7 | export const DEFAULT_INHERITED_ENV_VARS =
8 | process.platform === "win32"
9 | ? [
10 | "APPDATA",
11 | "HOMEDRIVE",
12 | "HOMEPATH",
13 | "LOCALAPPDATA",
14 | "PATH",
15 | "PROCESSOR_ARCHITECTURE",
16 | "SYSTEMDRIVE",
17 | "SYSTEMROOT",
18 | "TEMP",
19 | "USERNAME",
20 | "USERPROFILE",
21 | ]
22 | : /* list inspired by the default env inheritance of sudo */
23 | ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"];
24 |
25 | /**
26 | * Returns a default environment object including only environment variables deemed safe to inherit.
27 | */
28 | export function getDefaultEnvironment(): Record {
29 | const env: Record = {};
30 |
31 | for (const key of DEFAULT_INHERITED_ENV_VARS) {
32 | const value = process.env[key];
33 | if (value === undefined) {
34 | continue;
35 | }
36 |
37 | if (value.startsWith("()")) {
38 | // Skip functions, which are a security risk.
39 | continue;
40 | }
41 |
42 | env[key] = value;
43 | }
44 |
45 | return env;
46 | }
47 |
48 | /**
49 | * Get the MetaMCP API base URL from environment variables
50 | */
51 | export function getMetaMcpApiBaseUrl(): string {
52 | return process.env.METAMCP_API_BASE_URL || "https://api.metamcp.com";
53 | }
54 |
55 | /**
56 | * Get the MetaMCP API key from environment variables
57 | */
58 | export function getMetaMcpApiKey(): string | undefined {
59 | return process.env.METAMCP_API_KEY;
60 | }
61 |
62 | export function sanitizeName(name: string): string {
63 | return name.replace(/[^a-zA-Z0-9_-]/g, "");
64 | }
65 |
66 | export function computeParamsHash(
67 | params: ServerParameters,
68 | uuid: string
69 | ): string {
70 | let paramsDict: any;
71 |
72 | // Default to "STDIO" if type is undefined
73 | if (!params.type || params.type === "STDIO") {
74 | paramsDict = {
75 | uuid,
76 | type: "STDIO", // Explicitly set type to "STDIO" for consistent hashing
77 | command: params.command,
78 | args: params.args,
79 | env: params.env
80 | ? Object.fromEntries(
81 | Object.entries(params.env).sort((a, b) => a[0].localeCompare(b[0]))
82 | )
83 | : null,
84 | };
85 | } else if (params.type === "SSE" || params.type === "STREAMABLE_HTTP") {
86 | paramsDict = {
87 | uuid,
88 | type: params.type,
89 | url: params.url,
90 | };
91 | } else {
92 | throw new Error(`Unsupported server type: ${params.type}`);
93 | }
94 |
95 | const paramsJson = JSON.stringify(paramsDict);
96 | return crypto.createHash("sha256").update(paramsJson).digest("hex");
97 | }
98 |
99 | export function getSessionKey(uuid: string, params: ServerParameters): string {
100 | return `${uuid}_${computeParamsHash(params, uuid)}`;
101 | }
102 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2022",
4 | "module": "Node16",
5 | "moduleResolution": "Node16",
6 | "outDir": "./dist",
7 | "rootDir": "./src",
8 | "strict": true,
9 | "esModuleInterop": true,
10 | "skipLibCheck": true,
11 | "forceConsistentCasingInFileNames": true
12 | },
13 | "include": ["src/**/*"],
14 | "exclude": ["node_modules"]
15 | }
16 |
--------------------------------------------------------------------------------