From fb667fd4d08e7bb118b60f2b40018baebc09ad48 Mon Sep 17 00:00:00 2001 From: Max Gerber <89937743+max-stytch@users.noreply.github.com> Date: Fri, 14 Mar 2025 16:16:31 -0700 Subject: [PATCH 01/22] fix: Prefer 127.0.0.1 over localhost --- bin/cli.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/cli.js b/bin/cli.js index 94348fb..115e118 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -96,7 +96,7 @@ async function main() { await Promise.any([server, client, delay(2 * 1000)]); const portParam = SERVER_PORT === "3000" ? "" : `?proxyPort=${SERVER_PORT}`; console.log( - `\nšŸ” MCP Inspector is up and running at http://localhost:${CLIENT_PORT}${portParam} šŸš€`, + `\nšŸ” MCP Inspector is up and running at http://127.0.0.1:${CLIENT_PORT}${portParam} šŸš€`, ); try { From cda3905e5a64ebd0c8c3072c852fc777b5d0fd80 Mon Sep 17 00:00:00 2001 From: Maxwell Gerber Date: Fri, 14 Mar 2025 16:33:39 -0700 Subject: [PATCH 02/22] fix: Update URL in CONTRIBUTING.md too --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9ad5699..b225713 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,7 +7,7 @@ Thanks for your interest in contributing! This guide explains how to get involve 1. Fork the repository and clone it locally 2. Install dependencies with `npm install` 3. Run `npm run dev` to start both client and server in development mode -4. Use the web UI at http://localhost:5173 to interact with the inspector +4. Use the web UI at http://127.0.0.1:5173 to interact with the inspector ## Development Process & Pull Requests From 536b7e0a99170b402fec55946b157642bb6f5e74 Mon Sep 17 00:00:00 2001 From: Maxwell Gerber Date: Tue, 18 Mar 2025 09:29:48 -0700 Subject: [PATCH 03/22] fix: Update vite host --- client/vite.config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/vite.config.ts b/client/vite.config.ts index b3d0f45..c971d58 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -5,7 +5,9 @@ import { defineConfig } from "vite"; // https://vitejs.dev/config/ export default defineConfig({ plugins: [react()], - server: {}, + server: { + host: "127.0.0.1", + }, resolve: { alias: { "@": path.resolve(__dirname, "./src"), From 029e482e05a57f5610662d080c5aba59f2dd762c Mon Sep 17 00:00:00 2001 From: Nathan Arseneau Date: Wed, 19 Mar 2025 20:15:14 -0400 Subject: [PATCH 04/22] fix: set default value for input fields in ToolsTab component --- client/src/components/ToolsTab.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/client/src/components/ToolsTab.tsx b/client/src/components/ToolsTab.tsx index 777db80..dcfae44 100644 --- a/client/src/components/ToolsTab.tsx +++ b/client/src/components/ToolsTab.tsx @@ -229,6 +229,7 @@ const ToolsTab = ({ id={key} name={key} placeholder={prop.description} + value={(params[key] as string) ?? ""} onChange={(e) => setParams({ ...params, From dcbd1dad410dc8e6abb6e98cda17688a6c64ce4f Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Fri, 21 Mar 2025 06:54:46 -0700 Subject: [PATCH 05/22] Bump prismjs from 1.29.0 to 1.30.0 to address --- client/package.json | 2 +- package-lock.json | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/package.json b/client/package.json index 0471765..700bbee 100644 --- a/client/package.json +++ b/client/package.json @@ -38,7 +38,7 @@ "cmdk": "^1.0.4", "lucide-react": "^0.447.0", "pkce-challenge": "^4.1.0", - "prismjs": "^1.29.0", + "prismjs": "^1.30.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-simple-code-editor": "^0.14.1", diff --git a/package-lock.json b/package-lock.json index 21c9c30..1db7ef2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -49,7 +49,7 @@ "cmdk": "^1.0.4", "lucide-react": "^0.447.0", "pkce-challenge": "^4.1.0", - "prismjs": "^1.29.0", + "prismjs": "^1.30.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-simple-code-editor": "^0.14.1", @@ -8841,9 +8841,9 @@ } }, "node_modules/prismjs": { - "version": "1.29.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", - "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==", + "version": "1.30.0", + "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", + "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", "license": "MIT", "engines": { "node": ">=6" From 9b0da1f892fe60429b7f72cb1239a0859d68f561 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Sun, 23 Mar 2025 08:19:52 -0700 Subject: [PATCH 06/22] Add note on security considerations for proxy server --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index a6ab6d4..4eb2467 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,10 @@ For more details on ways to use the inspector, see the [Inspector section of the The inspector supports bearer token authentication for SSE connections. Enter your token in the UI when connecting to an MCP server, and it will be sent in the Authorization header. +### Security Considerations + +The MCP Inspector includes a proxy server that can run and communicate with local MCP processes. The proxy server should not be exposed to untrusted networks as it has permissions to spawn local processes and can connect to any specified MCP server. + ### From this repository If you're working on the inspector itself: From ec738314875c0b984dca92df88c2be73c7ef4df2 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Sun, 23 Mar 2025 08:30:06 -0700 Subject: [PATCH 07/22] Fix formatting --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4eb2467..f1bd97c 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ The inspector supports bearer token authentication for SSE connections. Enter yo ### Security Considerations -The MCP Inspector includes a proxy server that can run and communicate with local MCP processes. The proxy server should not be exposed to untrusted networks as it has permissions to spawn local processes and can connect to any specified MCP server. +The MCP Inspector includes a proxy server that can run and communicate with local MCP processes. The proxy server should not be exposed to untrusted networks as it has permissions to spawn local processes and can connect to any specified MCP server. ### From this repository From 210975e38531b68be7af1eae72dee7aa0c0c5ab0 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Sun, 23 Mar 2025 12:44:30 -0700 Subject: [PATCH 08/22] Add test dependencies --- client/package.json | 4 +- package-lock.json | 272 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 275 insertions(+), 1 deletion(-) diff --git a/client/package.json b/client/package.json index 0471765..c3e4ed3 100644 --- a/client/package.json +++ b/client/package.json @@ -24,8 +24,8 @@ }, "dependencies": { "@modelcontextprotocol/sdk": "^1.6.1", - "@radix-ui/react-dialog": "^1.1.3", "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.3", "@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.3", @@ -50,6 +50,8 @@ }, "devDependencies": { "@eslint/js": "^9.11.1", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.2.0", "@types/jest": "^29.5.14", "@types/node": "^22.7.5", "@types/react": "^18.3.10", diff --git a/package-lock.json b/package-lock.json index 68929c5..23bc2a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -65,6 +65,8 @@ }, "devDependencies": { "@eslint/js": "^9.11.1", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/react": "^16.2.0", "@types/jest": "^29.5.14", "@types/node": "^22.7.5", "@types/react": "^18.3.10", @@ -87,6 +89,13 @@ "vite": "^5.4.8" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.2.tgz", + "integrity": "sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A==", + "dev": true, + "license": "MIT" + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -576,6 +585,19 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/runtime": { + "version": "7.26.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", + "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", @@ -3786,6 +3808,148 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/dom/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@testing-library/dom/node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/@testing-library/dom/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", + "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "chalk": "^3.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "lodash": "^4.17.21", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/jest-dom/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@testing-library/react": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.2.0.tgz", + "integrity": "sha512-2cSskAvA1QNtKc8Y9VJQRv0tm3hLVgxRGDB+KYhIaPQJ1I+RHbhIXcM+zClKXzMes/wshsMVzf4B9vS4IZpqDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@tootallnate/once": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", @@ -3820,6 +3984,14 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "license": "MIT" }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -4585,6 +4757,16 @@ "node": ">=10" } }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/array-flatten": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", @@ -5331,6 +5513,13 @@ "node": ">= 8" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -5467,6 +5656,16 @@ "node": ">= 0.8" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -5524,6 +5723,14 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "license": "MIT" }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, "node_modules/domexception": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", @@ -6796,6 +7003,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -7984,6 +8201,17 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", @@ -8137,6 +8365,16 @@ "node": ">=6" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -9151,6 +9389,27 @@ "node": ">=8.10.0" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, + "license": "MIT" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -9837,6 +10096,19 @@ "node": ">=6" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", From fc76a7c7d49cf2a05c70846387c8da6daabcb454 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Sun, 23 Mar 2025 12:46:30 -0700 Subject: [PATCH 09/22] Add setup file and remove old testing mock that no longer exists from moduleNameMapper --- client/jest.config.cjs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/client/jest.config.cjs b/client/jest.config.cjs index 3830e79..73e0d16 100644 --- a/client/jest.config.cjs +++ b/client/jest.config.cjs @@ -1,12 +1,11 @@ module.exports = { preset: "ts-jest", testEnvironment: "jsdom", + setupFilesAfterEnv: [ + '/src/setupTests.ts' + ], moduleNameMapper: { - "^@/(.*)$": "/src/$1", - "^../components/DynamicJsonForm$": - "/src/utils/__mocks__/DynamicJsonForm.ts", - "^../../components/DynamicJsonForm$": - "/src/utils/__mocks__/DynamicJsonForm.ts", + "^@/(.*)$": "/src/$1" }, transform: { "^.+\\.tsx?$": [ @@ -14,9 +13,9 @@ module.exports = { { useESM: true, jsx: "react-jsx", - tsconfig: "tsconfig.jest.json", - }, - ], + tsconfig: "tsconfig.jest.json" + } + ] }, extensionsToTreatAsEsm: [".ts", ".tsx"], testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", @@ -25,13 +24,13 @@ module.exports = { "/node_modules/", "/dist/", "/bin/", - "\\.config\\.(js|ts|cjs|mjs)$", + "\\.config\\.(js|ts|cjs|mjs)$" ], // Exclude the same patterns from coverage reports coveragePathIgnorePatterns: [ "/node_modules/", "/dist/", "/bin/", - "\\.config\\.(js|ts|cjs|mjs)$", - ], + "\\.config\\.(js|ts|cjs|mjs)$" + ] }; From 85f0e216791d5c0e6e610bb1b30743de8b4b15a7 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Sun, 23 Mar 2025 13:22:13 -0700 Subject: [PATCH 10/22] Use commonjs for jest --- client/jest.config.cjs | 1 - 1 file changed, 1 deletion(-) diff --git a/client/jest.config.cjs b/client/jest.config.cjs index 73e0d16..6a48e7d 100644 --- a/client/jest.config.cjs +++ b/client/jest.config.cjs @@ -11,7 +11,6 @@ module.exports = { "^.+\\.tsx?$": [ "ts-jest", { - useESM: true, jsx: "react-jsx", tsconfig: "tsconfig.jest.json" } From 668cc915e4028132dd13254b406919a74b76bfd8 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Sun, 23 Mar 2025 13:22:31 -0700 Subject: [PATCH 11/22] Add jest-dom types --- client/tsconfig.app.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/tsconfig.app.json b/client/tsconfig.app.json index 980c215..e6620f7 100644 --- a/client/tsconfig.app.json +++ b/client/tsconfig.app.json @@ -24,7 +24,8 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "resolveJsonModule": true + "resolveJsonModule": true, + "types": ["jest", "@testing-library/jest-dom", "node"] }, "include": ["src"] } From 451704471c6324007cd263acf557a66ac943f95d Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Sun, 23 Mar 2025 13:25:06 -0700 Subject: [PATCH 12/22] Remove setup --- client/jest.config.cjs | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/jest.config.cjs b/client/jest.config.cjs index 6a48e7d..949df20 100644 --- a/client/jest.config.cjs +++ b/client/jest.config.cjs @@ -1,9 +1,6 @@ module.exports = { preset: "ts-jest", testEnvironment: "jsdom", - setupFilesAfterEnv: [ - '/src/setupTests.ts' - ], moduleNameMapper: { "^@/(.*)$": "/src/$1" }, From 61e229a5529d343134c8333e2fb1700a43b4a79b Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Sun, 23 Mar 2025 13:35:24 -0700 Subject: [PATCH 13/22] Add sidebar tests --- .../src/components/__tests__/Sidebar.test.tsx | 278 ++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 client/src/components/__tests__/Sidebar.test.tsx diff --git a/client/src/components/__tests__/Sidebar.test.tsx b/client/src/components/__tests__/Sidebar.test.tsx new file mode 100644 index 0000000..765de2f --- /dev/null +++ b/client/src/components/__tests__/Sidebar.test.tsx @@ -0,0 +1,278 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import { describe, it, beforeEach, jest } from '@jest/globals'; +import Sidebar from '../Sidebar'; + +// Mock theme hook +jest.mock('../../lib/useTheme', () => ({ + __esModule: true, + default: () => ['light', jest.fn()], +})); + +describe('Sidebar Environment Variables', () => { + const defaultProps = { + connectionStatus: 'disconnected' as const, + transportType: 'stdio' as const, + setTransportType: jest.fn(), + command: '', + setCommand: jest.fn(), + args: '', + setArgs: jest.fn(), + sseUrl: '', + setSseUrl: jest.fn(), + env: {}, + setEnv: jest.fn(), + bearerToken: '', + setBearerToken: jest.fn(), + onConnect: jest.fn(), + stdErrNotifications: [], + logLevel: 'info' as const, + sendLogLevelRequest: jest.fn(), + loggingSupported: true, + }; + + const renderSidebar = (props = {}) => { + return render(); + }; + + const openEnvVarsSection = () => { + const button = screen.getByText('Environment Variables'); + fireEvent.click(button); + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('Basic Operations', () => { + it('should add a new environment variable', () => { + const setEnv = jest.fn(); + renderSidebar({ env: {}, setEnv }); + + openEnvVarsSection(); + + const addButton = screen.getByText('Add Environment Variable'); + fireEvent.click(addButton); + + expect(setEnv).toHaveBeenCalledWith({ '': '' }); + }); + + it('should remove an environment variable', () => { + const setEnv = jest.fn(); + const initialEnv = { TEST_KEY: 'test_value' }; + renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + const removeButton = screen.getByRole('button', { name: 'Ɨ' }); + fireEvent.click(removeButton); + + expect(setEnv).toHaveBeenCalledWith({}); + }); + + it('should update environment variable value', () => { + const setEnv = jest.fn(); + const initialEnv = { TEST_KEY: 'test_value' }; + renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + const valueInput = screen.getByDisplayValue('test_value'); + fireEvent.change(valueInput, { target: { value: 'new_value' } }); + + expect(setEnv).toHaveBeenCalledWith({ TEST_KEY: 'new_value' }); + }); + + it('should toggle value visibility', () => { + const initialEnv = { TEST_KEY: 'test_value' }; + renderSidebar({ env: initialEnv }); + + openEnvVarsSection(); + + const valueInput = screen.getByDisplayValue('test_value'); + expect(valueInput).toHaveProperty('type', 'password'); + + const toggleButton = screen.getByRole('button', { name: /show value/i }); + fireEvent.click(toggleButton); + + expect(valueInput).toHaveProperty('type', 'text'); + }); + }); + + describe('Key Editing', () => { + it('should maintain order when editing first key', () => { + const setEnv = jest.fn(); + const initialEnv = { + FIRST_KEY: 'first_value', + SECOND_KEY: 'second_value', + THIRD_KEY: 'third_value', + }; + renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + const firstKeyInput = screen.getByDisplayValue('FIRST_KEY'); + fireEvent.change(firstKeyInput, { target: { value: 'NEW_FIRST_KEY' } }); + + expect(setEnv).toHaveBeenCalledWith({ + NEW_FIRST_KEY: 'first_value', + SECOND_KEY: 'second_value', + THIRD_KEY: 'third_value', + }); + }); + + it('should maintain order when editing middle key', () => { + const setEnv = jest.fn(); + const initialEnv = { + FIRST_KEY: 'first_value', + SECOND_KEY: 'second_value', + THIRD_KEY: 'third_value', + }; + renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + const middleKeyInput = screen.getByDisplayValue('SECOND_KEY'); + fireEvent.change(middleKeyInput, { target: { value: 'NEW_SECOND_KEY' } }); + + expect(setEnv).toHaveBeenCalledWith({ + FIRST_KEY: 'first_value', + NEW_SECOND_KEY: 'second_value', + THIRD_KEY: 'third_value', + }); + }); + + it('should maintain order when editing last key', () => { + const setEnv = jest.fn(); + const initialEnv = { + FIRST_KEY: 'first_value', + SECOND_KEY: 'second_value', + THIRD_KEY: 'third_value', + }; + renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + const lastKeyInput = screen.getByDisplayValue('THIRD_KEY'); + fireEvent.change(lastKeyInput, { target: { value: 'NEW_THIRD_KEY' } }); + + expect(setEnv).toHaveBeenCalledWith({ + FIRST_KEY: 'first_value', + SECOND_KEY: 'second_value', + NEW_THIRD_KEY: 'third_value', + }); + }); + }); + + describe('Multiple Operations', () => { + it('should maintain state after multiple key edits', () => { + const setEnv = jest.fn(); + const initialEnv = { + FIRST_KEY: 'first_value', + SECOND_KEY: 'second_value', + }; + const { rerender } = renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + // First key edit + const firstKeyInput = screen.getByDisplayValue('FIRST_KEY'); + fireEvent.change(firstKeyInput, { target: { value: 'NEW_FIRST_KEY' } }); + + // Get the updated env from the first setEnv call + const updatedEnv = (setEnv.mock.calls[0][0] as Record); + + // Rerender with the updated env + rerender(); + + // Second key edit + const secondKeyInput = screen.getByDisplayValue('SECOND_KEY'); + fireEvent.change(secondKeyInput, { target: { value: 'NEW_SECOND_KEY' } }); + + // Verify the final state matches what we expect + expect(setEnv).toHaveBeenLastCalledWith({ + NEW_FIRST_KEY: 'first_value', + NEW_SECOND_KEY: 'second_value', + }); + }); + + it('should maintain visibility state after key edit', () => { + const initialEnv = { TEST_KEY: 'test_value' }; + const { rerender } = renderSidebar({ env: initialEnv }); + + openEnvVarsSection(); + + // Show the value + const toggleButton = screen.getByRole('button', { name: /show value/i }); + fireEvent.click(toggleButton); + + const valueInput = screen.getByDisplayValue('test_value'); + expect(valueInput).toHaveProperty('type', 'text'); + + // Edit the key + const keyInput = screen.getByDisplayValue('TEST_KEY'); + fireEvent.change(keyInput, { target: { value: 'NEW_KEY' } }); + + // Rerender with updated env + rerender(); + + // Value should still be visible + const updatedValueInput = screen.getByDisplayValue('test_value'); + expect(updatedValueInput).toHaveProperty('type', 'text'); + }); + }); + + describe('Edge Cases', () => { + it('should handle empty key', () => { + const setEnv = jest.fn(); + const initialEnv = { TEST_KEY: 'test_value' }; + renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + const keyInput = screen.getByDisplayValue('TEST_KEY'); + fireEvent.change(keyInput, { target: { value: '' } }); + + expect(setEnv).toHaveBeenCalledWith({ '': 'test_value' }); + }); + + it('should handle special characters in key', () => { + const setEnv = jest.fn(); + const initialEnv = { TEST_KEY: 'test_value' }; + renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + const keyInput = screen.getByDisplayValue('TEST_KEY'); + fireEvent.change(keyInput, { target: { value: 'TEST-KEY@123' } }); + + expect(setEnv).toHaveBeenCalledWith({ 'TEST-KEY@123': 'test_value' }); + }); + + it('should handle unicode characters', () => { + const setEnv = jest.fn(); + const initialEnv = { TEST_KEY: 'test_value' }; + renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + const keyInput = screen.getByDisplayValue('TEST_KEY'); + fireEvent.change(keyInput, { target: { value: 'TEST_šŸ”‘' } }); + + expect(setEnv).toHaveBeenCalledWith({ 'TEST_šŸ”‘': 'test_value' }); + }); + + it('should handle very long key names', () => { + const setEnv = jest.fn(); + const initialEnv = { TEST_KEY: 'test_value' }; + renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + const keyInput = screen.getByDisplayValue('TEST_KEY'); + const longKey = 'A'.repeat(100); + fireEvent.change(keyInput, { target: { value: longKey } }); + + expect(setEnv).toHaveBeenCalledWith({ [longKey]: 'test_value' }); + }); + }); +}); From cab1ed3dd849a85b37255eb2ec20ee7764565a4c Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 24 Mar 2025 08:28:28 -0700 Subject: [PATCH 14/22] Add some json form tests and handle css in ui tests --- client/jest.config.cjs | 3 +- client/src/__mocks__/styleMock.js | 1 + .../__tests__/DynamicJsonForm.test.tsx | 61 +++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 client/src/__mocks__/styleMock.js create mode 100644 client/src/components/__tests__/DynamicJsonForm.test.tsx diff --git a/client/jest.config.cjs b/client/jest.config.cjs index 949df20..b87383e 100644 --- a/client/jest.config.cjs +++ b/client/jest.config.cjs @@ -2,7 +2,8 @@ module.exports = { preset: "ts-jest", testEnvironment: "jsdom", moduleNameMapper: { - "^@/(.*)$": "/src/$1" + "^@/(.*)$": "/src/$1", + "\\.css$": "/src/__mocks__/styleMock.js" }, transform: { "^.+\\.tsx?$": [ diff --git a/client/src/__mocks__/styleMock.js b/client/src/__mocks__/styleMock.js new file mode 100644 index 0000000..f053ebf --- /dev/null +++ b/client/src/__mocks__/styleMock.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/client/src/components/__tests__/DynamicJsonForm.test.tsx b/client/src/components/__tests__/DynamicJsonForm.test.tsx new file mode 100644 index 0000000..d140663 --- /dev/null +++ b/client/src/components/__tests__/DynamicJsonForm.test.tsx @@ -0,0 +1,61 @@ +import { render, screen, fireEvent } from '@testing-library/react'; +import { describe, it, expect, jest } from '@jest/globals'; +import DynamicJsonForm from '../DynamicJsonForm'; +import type { JsonSchemaType } from '../DynamicJsonForm'; + +describe('DynamicJsonForm Integer Fields', () => { + const renderForm = (props = {}) => { + const defaultProps = { + schema: { + type: "integer" as const, + description: "Test integer field" + } satisfies JsonSchemaType, + value: undefined, + onChange: jest.fn() + }; + return render(); + }; + + describe('Basic Operations', () => { + it('should render number input with step=1', () => { + renderForm(); + const input = screen.getByRole('spinbutton'); + expect(input).toHaveProperty('type', 'number'); + expect(input).toHaveProperty('step', '1'); + }); + + it('should pass integer values to onChange', () => { + const onChange = jest.fn(); + renderForm({ onChange }); + + const input = screen.getByRole('spinbutton'); + fireEvent.change(input, { target: { value: '42' } }); + + expect(onChange).toHaveBeenCalledWith(42); + // Verify the value is a number, not a string + expect(typeof onChange.mock.calls[0][0]).toBe('number'); + }); + + it('should not pass string values to onChange', () => { + const onChange = jest.fn(); + renderForm({ onChange }); + + const input = screen.getByRole('spinbutton'); + fireEvent.change(input, { target: { value: 'abc' } }); + + expect(onChange).not.toHaveBeenCalled(); + }); + }); + + describe('Edge Cases', () => { + it('should handle non-numeric input by not calling onChange', () => { + const onChange = jest.fn(); + renderForm({ onChange }); + + const input = screen.getByRole('spinbutton'); + fireEvent.change(input, { target: { value: 'abc' } }); + + expect(onChange).not.toHaveBeenCalled(); + }); + }); +}); From 5735f2347a4d1465033806e4648583a5a35a4e0f Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 24 Mar 2025 08:52:45 -0700 Subject: [PATCH 15/22] Add tests related to issues/187 to confirm fixed --- .../__tests__/DynamicJsonForm.test.tsx | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/client/src/components/__tests__/DynamicJsonForm.test.tsx b/client/src/components/__tests__/DynamicJsonForm.test.tsx index d140663..f18c15f 100644 --- a/client/src/components/__tests__/DynamicJsonForm.test.tsx +++ b/client/src/components/__tests__/DynamicJsonForm.test.tsx @@ -3,6 +3,40 @@ import { describe, it, expect, jest } from '@jest/globals'; import DynamicJsonForm from '../DynamicJsonForm'; import type { JsonSchemaType } from '../DynamicJsonForm'; +describe('DynamicJsonForm String Fields', () => { + const renderForm = (props = {}) => { + const defaultProps = { + schema: { + type: "string" as const, + description: "Test string field" + } satisfies JsonSchemaType, + value: undefined, + onChange: jest.fn() + }; + return render(); + }; + + describe('Type Validation', () => { + it('should handle numeric input as string type', () => { + const onChange = jest.fn(); + renderForm({ onChange }); + + const input = screen.getByRole('textbox'); + fireEvent.change(input, { target: { value: '123321' } }); + + expect(onChange).toHaveBeenCalledWith('123321'); + // Verify the value is a string, not a number + expect(typeof onChange.mock.calls[0][0]).toBe('string'); + }); + + it('should render as text input, not number input', () => { + renderForm(); + const input = screen.getByRole('textbox'); + expect(input).toHaveProperty('type', 'text'); + }); + }); +}); + describe('DynamicJsonForm Integer Fields', () => { const renderForm = (props = {}) => { const defaultProps = { From fa3e2867c97e27c2bb434be7eaacd6698dc8fb4b Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 24 Mar 2025 09:07:53 -0700 Subject: [PATCH 16/22] Fix formatting --- client/jest.config.cjs | 14 +- .../__tests__/DynamicJsonForm.test.tsx | 86 ++--- .../src/components/__tests__/Sidebar.test.tsx | 296 +++++++++--------- 3 files changed, 198 insertions(+), 198 deletions(-) diff --git a/client/jest.config.cjs b/client/jest.config.cjs index b87383e..c360e72 100644 --- a/client/jest.config.cjs +++ b/client/jest.config.cjs @@ -3,16 +3,16 @@ module.exports = { testEnvironment: "jsdom", moduleNameMapper: { "^@/(.*)$": "/src/$1", - "\\.css$": "/src/__mocks__/styleMock.js" + "\\.css$": "/src/__mocks__/styleMock.js", }, transform: { "^.+\\.tsx?$": [ "ts-jest", { jsx: "react-jsx", - tsconfig: "tsconfig.jest.json" - } - ] + tsconfig: "tsconfig.jest.json", + }, + ], }, extensionsToTreatAsEsm: [".ts", ".tsx"], testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", @@ -21,13 +21,13 @@ module.exports = { "/node_modules/", "/dist/", "/bin/", - "\\.config\\.(js|ts|cjs|mjs)$" + "\\.config\\.(js|ts|cjs|mjs)$", ], // Exclude the same patterns from coverage reports coveragePathIgnorePatterns: [ "/node_modules/", "/dist/", "/bin/", - "\\.config\\.(js|ts|cjs|mjs)$" - ] + "\\.config\\.(js|ts|cjs|mjs)$", + ], }; diff --git a/client/src/components/__tests__/DynamicJsonForm.test.tsx b/client/src/components/__tests__/DynamicJsonForm.test.tsx index f18c15f..fce6014 100644 --- a/client/src/components/__tests__/DynamicJsonForm.test.tsx +++ b/client/src/components/__tests__/DynamicJsonForm.test.tsx @@ -1,94 +1,94 @@ -import { render, screen, fireEvent } from '@testing-library/react'; -import { describe, it, expect, jest } from '@jest/globals'; -import DynamicJsonForm from '../DynamicJsonForm'; -import type { JsonSchemaType } from '../DynamicJsonForm'; +import { render, screen, fireEvent } from "@testing-library/react"; +import { describe, it, expect, jest } from "@jest/globals"; +import DynamicJsonForm from "../DynamicJsonForm"; +import type { JsonSchemaType } from "../DynamicJsonForm"; -describe('DynamicJsonForm String Fields', () => { +describe("DynamicJsonForm String Fields", () => { const renderForm = (props = {}) => { const defaultProps = { schema: { type: "string" as const, - description: "Test string field" + description: "Test string field", } satisfies JsonSchemaType, value: undefined, - onChange: jest.fn() + onChange: jest.fn(), }; return render(); }; - describe('Type Validation', () => { - it('should handle numeric input as string type', () => { + describe("Type Validation", () => { + it("should handle numeric input as string type", () => { const onChange = jest.fn(); renderForm({ onChange }); - - const input = screen.getByRole('textbox'); - fireEvent.change(input, { target: { value: '123321' } }); - - expect(onChange).toHaveBeenCalledWith('123321'); + + const input = screen.getByRole("textbox"); + fireEvent.change(input, { target: { value: "123321" } }); + + expect(onChange).toHaveBeenCalledWith("123321"); // Verify the value is a string, not a number - expect(typeof onChange.mock.calls[0][0]).toBe('string'); + expect(typeof onChange.mock.calls[0][0]).toBe("string"); }); - it('should render as text input, not number input', () => { + it("should render as text input, not number input", () => { renderForm(); - const input = screen.getByRole('textbox'); - expect(input).toHaveProperty('type', 'text'); + const input = screen.getByRole("textbox"); + expect(input).toHaveProperty("type", "text"); }); }); }); -describe('DynamicJsonForm Integer Fields', () => { +describe("DynamicJsonForm Integer Fields", () => { const renderForm = (props = {}) => { const defaultProps = { schema: { type: "integer" as const, - description: "Test integer field" + description: "Test integer field", } satisfies JsonSchemaType, value: undefined, - onChange: jest.fn() + onChange: jest.fn(), }; return render(); }; - describe('Basic Operations', () => { - it('should render number input with step=1', () => { + describe("Basic Operations", () => { + it("should render number input with step=1", () => { renderForm(); - const input = screen.getByRole('spinbutton'); - expect(input).toHaveProperty('type', 'number'); - expect(input).toHaveProperty('step', '1'); + const input = screen.getByRole("spinbutton"); + expect(input).toHaveProperty("type", "number"); + expect(input).toHaveProperty("step", "1"); }); - it('should pass integer values to onChange', () => { + it("should pass integer values to onChange", () => { const onChange = jest.fn(); renderForm({ onChange }); - - const input = screen.getByRole('spinbutton'); - fireEvent.change(input, { target: { value: '42' } }); - + + const input = screen.getByRole("spinbutton"); + fireEvent.change(input, { target: { value: "42" } }); + expect(onChange).toHaveBeenCalledWith(42); // Verify the value is a number, not a string - expect(typeof onChange.mock.calls[0][0]).toBe('number'); + expect(typeof onChange.mock.calls[0][0]).toBe("number"); }); - it('should not pass string values to onChange', () => { + it("should not pass string values to onChange", () => { const onChange = jest.fn(); renderForm({ onChange }); - - const input = screen.getByRole('spinbutton'); - fireEvent.change(input, { target: { value: 'abc' } }); - + + const input = screen.getByRole("spinbutton"); + fireEvent.change(input, { target: { value: "abc" } }); + expect(onChange).not.toHaveBeenCalled(); }); }); - describe('Edge Cases', () => { - it('should handle non-numeric input by not calling onChange', () => { + describe("Edge Cases", () => { + it("should handle non-numeric input by not calling onChange", () => { const onChange = jest.fn(); renderForm({ onChange }); - - const input = screen.getByRole('spinbutton'); - fireEvent.change(input, { target: { value: 'abc' } }); - + + const input = screen.getByRole("spinbutton"); + fireEvent.change(input, { target: { value: "abc" } }); + expect(onChange).not.toHaveBeenCalled(); }); }); diff --git a/client/src/components/__tests__/Sidebar.test.tsx b/client/src/components/__tests__/Sidebar.test.tsx index 765de2f..8c0b313 100644 --- a/client/src/components/__tests__/Sidebar.test.tsx +++ b/client/src/components/__tests__/Sidebar.test.tsx @@ -1,31 +1,31 @@ -import { render, screen, fireEvent } from '@testing-library/react'; -import { describe, it, beforeEach, jest } from '@jest/globals'; -import Sidebar from '../Sidebar'; +import { render, screen, fireEvent } from "@testing-library/react"; +import { describe, it, beforeEach, jest } from "@jest/globals"; +import Sidebar from "../Sidebar"; // Mock theme hook -jest.mock('../../lib/useTheme', () => ({ +jest.mock("../../lib/useTheme", () => ({ __esModule: true, - default: () => ['light', jest.fn()], + default: () => ["light", jest.fn()], })); -describe('Sidebar Environment Variables', () => { +describe("Sidebar Environment Variables", () => { const defaultProps = { - connectionStatus: 'disconnected' as const, - transportType: 'stdio' as const, + connectionStatus: "disconnected" as const, + transportType: "stdio" as const, setTransportType: jest.fn(), - command: '', + command: "", setCommand: jest.fn(), - args: '', + args: "", setArgs: jest.fn(), - sseUrl: '', + sseUrl: "", setSseUrl: jest.fn(), env: {}, setEnv: jest.fn(), - bearerToken: '', + bearerToken: "", setBearerToken: jest.fn(), onConnect: jest.fn(), stdErrNotifications: [], - logLevel: 'info' as const, + logLevel: "info" as const, sendLogLevelRequest: jest.fn(), loggingSupported: true, }; @@ -35,7 +35,7 @@ describe('Sidebar Environment Variables', () => { }; const openEnvVarsSection = () => { - const button = screen.getByText('Environment Variables'); + const button = screen.getByText("Environment Variables"); fireEvent.click(button); }; @@ -43,236 +43,236 @@ describe('Sidebar Environment Variables', () => { jest.clearAllMocks(); }); - describe('Basic Operations', () => { - it('should add a new environment variable', () => { + describe("Basic Operations", () => { + it("should add a new environment variable", () => { const setEnv = jest.fn(); renderSidebar({ env: {}, setEnv }); - + openEnvVarsSection(); - - const addButton = screen.getByText('Add Environment Variable'); + + const addButton = screen.getByText("Add Environment Variable"); fireEvent.click(addButton); - - expect(setEnv).toHaveBeenCalledWith({ '': '' }); + + expect(setEnv).toHaveBeenCalledWith({ "": "" }); }); - it('should remove an environment variable', () => { + it("should remove an environment variable", () => { const setEnv = jest.fn(); - const initialEnv = { TEST_KEY: 'test_value' }; + const initialEnv = { TEST_KEY: "test_value" }; renderSidebar({ env: initialEnv, setEnv }); - + openEnvVarsSection(); - - const removeButton = screen.getByRole('button', { name: 'Ɨ' }); + + const removeButton = screen.getByRole("button", { name: "Ɨ" }); fireEvent.click(removeButton); - + expect(setEnv).toHaveBeenCalledWith({}); }); - it('should update environment variable value', () => { + it("should update environment variable value", () => { const setEnv = jest.fn(); - const initialEnv = { TEST_KEY: 'test_value' }; + const initialEnv = { TEST_KEY: "test_value" }; renderSidebar({ env: initialEnv, setEnv }); - + openEnvVarsSection(); - - const valueInput = screen.getByDisplayValue('test_value'); - fireEvent.change(valueInput, { target: { value: 'new_value' } }); - - expect(setEnv).toHaveBeenCalledWith({ TEST_KEY: 'new_value' }); + + const valueInput = screen.getByDisplayValue("test_value"); + fireEvent.change(valueInput, { target: { value: "new_value" } }); + + expect(setEnv).toHaveBeenCalledWith({ TEST_KEY: "new_value" }); }); - it('should toggle value visibility', () => { - const initialEnv = { TEST_KEY: 'test_value' }; + it("should toggle value visibility", () => { + const initialEnv = { TEST_KEY: "test_value" }; renderSidebar({ env: initialEnv }); - + openEnvVarsSection(); - - const valueInput = screen.getByDisplayValue('test_value'); - expect(valueInput).toHaveProperty('type', 'password'); - - const toggleButton = screen.getByRole('button', { name: /show value/i }); + + const valueInput = screen.getByDisplayValue("test_value"); + expect(valueInput).toHaveProperty("type", "password"); + + const toggleButton = screen.getByRole("button", { name: /show value/i }); fireEvent.click(toggleButton); - - expect(valueInput).toHaveProperty('type', 'text'); + + expect(valueInput).toHaveProperty("type", "text"); }); }); - describe('Key Editing', () => { - it('should maintain order when editing first key', () => { + describe("Key Editing", () => { + it("should maintain order when editing first key", () => { const setEnv = jest.fn(); const initialEnv = { - FIRST_KEY: 'first_value', - SECOND_KEY: 'second_value', - THIRD_KEY: 'third_value', + FIRST_KEY: "first_value", + SECOND_KEY: "second_value", + THIRD_KEY: "third_value", }; renderSidebar({ env: initialEnv, setEnv }); - + openEnvVarsSection(); - - const firstKeyInput = screen.getByDisplayValue('FIRST_KEY'); - fireEvent.change(firstKeyInput, { target: { value: 'NEW_FIRST_KEY' } }); - + + const firstKeyInput = screen.getByDisplayValue("FIRST_KEY"); + fireEvent.change(firstKeyInput, { target: { value: "NEW_FIRST_KEY" } }); + expect(setEnv).toHaveBeenCalledWith({ - NEW_FIRST_KEY: 'first_value', - SECOND_KEY: 'second_value', - THIRD_KEY: 'third_value', + NEW_FIRST_KEY: "first_value", + SECOND_KEY: "second_value", + THIRD_KEY: "third_value", }); }); - it('should maintain order when editing middle key', () => { + it("should maintain order when editing middle key", () => { const setEnv = jest.fn(); const initialEnv = { - FIRST_KEY: 'first_value', - SECOND_KEY: 'second_value', - THIRD_KEY: 'third_value', + FIRST_KEY: "first_value", + SECOND_KEY: "second_value", + THIRD_KEY: "third_value", }; renderSidebar({ env: initialEnv, setEnv }); - + openEnvVarsSection(); - - const middleKeyInput = screen.getByDisplayValue('SECOND_KEY'); - fireEvent.change(middleKeyInput, { target: { value: 'NEW_SECOND_KEY' } }); - + + const middleKeyInput = screen.getByDisplayValue("SECOND_KEY"); + fireEvent.change(middleKeyInput, { target: { value: "NEW_SECOND_KEY" } }); + expect(setEnv).toHaveBeenCalledWith({ - FIRST_KEY: 'first_value', - NEW_SECOND_KEY: 'second_value', - THIRD_KEY: 'third_value', + FIRST_KEY: "first_value", + NEW_SECOND_KEY: "second_value", + THIRD_KEY: "third_value", }); }); - it('should maintain order when editing last key', () => { + it("should maintain order when editing last key", () => { const setEnv = jest.fn(); const initialEnv = { - FIRST_KEY: 'first_value', - SECOND_KEY: 'second_value', - THIRD_KEY: 'third_value', + FIRST_KEY: "first_value", + SECOND_KEY: "second_value", + THIRD_KEY: "third_value", }; renderSidebar({ env: initialEnv, setEnv }); - + openEnvVarsSection(); - - const lastKeyInput = screen.getByDisplayValue('THIRD_KEY'); - fireEvent.change(lastKeyInput, { target: { value: 'NEW_THIRD_KEY' } }); - + + const lastKeyInput = screen.getByDisplayValue("THIRD_KEY"); + fireEvent.change(lastKeyInput, { target: { value: "NEW_THIRD_KEY" } }); + expect(setEnv).toHaveBeenCalledWith({ - FIRST_KEY: 'first_value', - SECOND_KEY: 'second_value', - NEW_THIRD_KEY: 'third_value', + FIRST_KEY: "first_value", + SECOND_KEY: "second_value", + NEW_THIRD_KEY: "third_value", }); }); }); - describe('Multiple Operations', () => { - it('should maintain state after multiple key edits', () => { + describe("Multiple Operations", () => { + it("should maintain state after multiple key edits", () => { const setEnv = jest.fn(); const initialEnv = { - FIRST_KEY: 'first_value', - SECOND_KEY: 'second_value', + FIRST_KEY: "first_value", + SECOND_KEY: "second_value", }; const { rerender } = renderSidebar({ env: initialEnv, setEnv }); - + openEnvVarsSection(); - + // First key edit - const firstKeyInput = screen.getByDisplayValue('FIRST_KEY'); - fireEvent.change(firstKeyInput, { target: { value: 'NEW_FIRST_KEY' } }); - + const firstKeyInput = screen.getByDisplayValue("FIRST_KEY"); + fireEvent.change(firstKeyInput, { target: { value: "NEW_FIRST_KEY" } }); + // Get the updated env from the first setEnv call - const updatedEnv = (setEnv.mock.calls[0][0] as Record); - + const updatedEnv = setEnv.mock.calls[0][0] as Record; + // Rerender with the updated env rerender(); - + // Second key edit - const secondKeyInput = screen.getByDisplayValue('SECOND_KEY'); - fireEvent.change(secondKeyInput, { target: { value: 'NEW_SECOND_KEY' } }); - + const secondKeyInput = screen.getByDisplayValue("SECOND_KEY"); + fireEvent.change(secondKeyInput, { target: { value: "NEW_SECOND_KEY" } }); + // Verify the final state matches what we expect expect(setEnv).toHaveBeenLastCalledWith({ - NEW_FIRST_KEY: 'first_value', - NEW_SECOND_KEY: 'second_value', + NEW_FIRST_KEY: "first_value", + NEW_SECOND_KEY: "second_value", }); }); - it('should maintain visibility state after key edit', () => { - const initialEnv = { TEST_KEY: 'test_value' }; + it("should maintain visibility state after key edit", () => { + const initialEnv = { TEST_KEY: "test_value" }; const { rerender } = renderSidebar({ env: initialEnv }); - + openEnvVarsSection(); - + // Show the value - const toggleButton = screen.getByRole('button', { name: /show value/i }); + const toggleButton = screen.getByRole("button", { name: /show value/i }); fireEvent.click(toggleButton); - - const valueInput = screen.getByDisplayValue('test_value'); - expect(valueInput).toHaveProperty('type', 'text'); - + + const valueInput = screen.getByDisplayValue("test_value"); + expect(valueInput).toHaveProperty("type", "text"); + // Edit the key - const keyInput = screen.getByDisplayValue('TEST_KEY'); - fireEvent.change(keyInput, { target: { value: 'NEW_KEY' } }); - + const keyInput = screen.getByDisplayValue("TEST_KEY"); + fireEvent.change(keyInput, { target: { value: "NEW_KEY" } }); + // Rerender with updated env - rerender(); - + rerender(); + // Value should still be visible - const updatedValueInput = screen.getByDisplayValue('test_value'); - expect(updatedValueInput).toHaveProperty('type', 'text'); + const updatedValueInput = screen.getByDisplayValue("test_value"); + expect(updatedValueInput).toHaveProperty("type", "text"); }); }); - describe('Edge Cases', () => { - it('should handle empty key', () => { + describe("Edge Cases", () => { + it("should handle empty key", () => { const setEnv = jest.fn(); - const initialEnv = { TEST_KEY: 'test_value' }; + const initialEnv = { TEST_KEY: "test_value" }; renderSidebar({ env: initialEnv, setEnv }); - + openEnvVarsSection(); - - const keyInput = screen.getByDisplayValue('TEST_KEY'); - fireEvent.change(keyInput, { target: { value: '' } }); - - expect(setEnv).toHaveBeenCalledWith({ '': 'test_value' }); + + const keyInput = screen.getByDisplayValue("TEST_KEY"); + fireEvent.change(keyInput, { target: { value: "" } }); + + expect(setEnv).toHaveBeenCalledWith({ "": "test_value" }); }); - it('should handle special characters in key', () => { + it("should handle special characters in key", () => { const setEnv = jest.fn(); - const initialEnv = { TEST_KEY: 'test_value' }; + const initialEnv = { TEST_KEY: "test_value" }; renderSidebar({ env: initialEnv, setEnv }); - + openEnvVarsSection(); - - const keyInput = screen.getByDisplayValue('TEST_KEY'); - fireEvent.change(keyInput, { target: { value: 'TEST-KEY@123' } }); - - expect(setEnv).toHaveBeenCalledWith({ 'TEST-KEY@123': 'test_value' }); + + const keyInput = screen.getByDisplayValue("TEST_KEY"); + fireEvent.change(keyInput, { target: { value: "TEST-KEY@123" } }); + + expect(setEnv).toHaveBeenCalledWith({ "TEST-KEY@123": "test_value" }); }); - it('should handle unicode characters', () => { + it("should handle unicode characters", () => { const setEnv = jest.fn(); - const initialEnv = { TEST_KEY: 'test_value' }; + const initialEnv = { TEST_KEY: "test_value" }; renderSidebar({ env: initialEnv, setEnv }); - + openEnvVarsSection(); - - const keyInput = screen.getByDisplayValue('TEST_KEY'); - fireEvent.change(keyInput, { target: { value: 'TEST_šŸ”‘' } }); - - expect(setEnv).toHaveBeenCalledWith({ 'TEST_šŸ”‘': 'test_value' }); + + const keyInput = screen.getByDisplayValue("TEST_KEY"); + fireEvent.change(keyInput, { target: { value: "TEST_šŸ”‘" } }); + + expect(setEnv).toHaveBeenCalledWith({ "TEST_šŸ”‘": "test_value" }); }); - it('should handle very long key names', () => { + it("should handle very long key names", () => { const setEnv = jest.fn(); - const initialEnv = { TEST_KEY: 'test_value' }; + const initialEnv = { TEST_KEY: "test_value" }; renderSidebar({ env: initialEnv, setEnv }); - + openEnvVarsSection(); - - const keyInput = screen.getByDisplayValue('TEST_KEY'); - const longKey = 'A'.repeat(100); + + const keyInput = screen.getByDisplayValue("TEST_KEY"); + const longKey = "A".repeat(100); fireEvent.change(keyInput, { target: { value: longKey } }); - - expect(setEnv).toHaveBeenCalledWith({ [longKey]: 'test_value' }); + + expect(setEnv).toHaveBeenCalledWith({ [longKey]: "test_value" }); }); }); }); From a7f25153c445769add41e89dfaf15b7f5c60971f Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 24 Mar 2025 09:18:58 -0700 Subject: [PATCH 17/22] Add failing ToolsTab test that should get fixed with pull/198 --- .../components/__tests__/ToolsTab.test.tsx | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 client/src/components/__tests__/ToolsTab.test.tsx diff --git a/client/src/components/__tests__/ToolsTab.test.tsx b/client/src/components/__tests__/ToolsTab.test.tsx new file mode 100644 index 0000000..2688b52 --- /dev/null +++ b/client/src/components/__tests__/ToolsTab.test.tsx @@ -0,0 +1,72 @@ +import { render, screen, fireEvent } from "@testing-library/react"; +import { describe, it, expect, jest } from "@jest/globals"; +import ToolsTab from "../ToolsTab"; +import { Tool } from "@modelcontextprotocol/sdk/types.js"; +import { Tabs } from "@/components/ui/tabs"; + +describe("ToolsTab", () => { + const mockTools: Tool[] = [ + { + name: "tool1", + description: "First tool", + inputSchema: { + type: "object" as const, + properties: { + num: { type: "number" as const } + } + } + }, + { + name: "tool2", + description: "Second tool", + inputSchema: { + type: "object" as const, + properties: { + num: { type: "number" as const } + } + } + } + ]; + + const defaultProps = { + tools: mockTools, + listTools: jest.fn(), + clearTools: jest.fn(), + callTool: jest.fn(), + selectedTool: null, + setSelectedTool: jest.fn(), + toolResult: null, + nextCursor: "", + error: null + }; + + const renderToolsTab = (props = {}) => { + return render( + + + + ); + }; + + it("should reset input values when switching tools", () => { + const { rerender } = renderToolsTab({ + selectedTool: mockTools[0] + }); + + // Enter a value in the first tool's input + const input = screen.getByRole("spinbutton") as HTMLInputElement; + fireEvent.change(input, { target: { value: "42" } }); + expect(input.value).toBe("42"); + + // Switch to second tool + rerender( + + + + ); + + // Verify input is reset + const newInput = screen.getByRole("spinbutton") as HTMLInputElement; + expect(newInput.value).toBe(""); + }); +}); From b7fa23676a6255176dad029dd1e79252ccba9cb4 Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 24 Mar 2025 09:27:51 -0700 Subject: [PATCH 18/22] Fix formatting --- .../components/__tests__/ToolsTab.test.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/client/src/components/__tests__/ToolsTab.test.tsx b/client/src/components/__tests__/ToolsTab.test.tsx index 2688b52..2a45065 100644 --- a/client/src/components/__tests__/ToolsTab.test.tsx +++ b/client/src/components/__tests__/ToolsTab.test.tsx @@ -12,9 +12,9 @@ describe("ToolsTab", () => { inputSchema: { type: "object" as const, properties: { - num: { type: "number" as const } - } - } + num: { type: "number" as const }, + }, + }, }, { name: "tool2", @@ -22,10 +22,10 @@ describe("ToolsTab", () => { inputSchema: { type: "object" as const, properties: { - num: { type: "number" as const } - } - } - } + num: { type: "number" as const }, + }, + }, + }, ]; const defaultProps = { @@ -37,20 +37,20 @@ describe("ToolsTab", () => { setSelectedTool: jest.fn(), toolResult: null, nextCursor: "", - error: null + error: null, }; const renderToolsTab = (props = {}) => { return render( - + , ); }; it("should reset input values when switching tools", () => { const { rerender } = renderToolsTab({ - selectedTool: mockTools[0] + selectedTool: mockTools[0], }); // Enter a value in the first tool's input @@ -62,7 +62,7 @@ describe("ToolsTab", () => { rerender( - + , ); // Verify input is reset From 379486b5ea4d4ab2e2faae5a96c79276e374234f Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 24 Mar 2025 09:43:48 -0700 Subject: [PATCH 19/22] Add failing test for pull/206 --- .../src/components/__tests__/Sidebar.test.tsx | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/client/src/components/__tests__/Sidebar.test.tsx b/client/src/components/__tests__/Sidebar.test.tsx index 8c0b313..fc8a5f4 100644 --- a/client/src/components/__tests__/Sidebar.test.tsx +++ b/client/src/components/__tests__/Sidebar.test.tsx @@ -161,6 +161,31 @@ describe("Sidebar Environment Variables", () => { NEW_THIRD_KEY: "third_value", }); }); + + it("should maintain order during key editing", () => { + const setEnv = jest.fn(); + const initialEnv = { + KEY1: "value1", + KEY2: "value2" + }; + renderSidebar({ env: initialEnv, setEnv }); + + openEnvVarsSection(); + + // Type "NEW_" one character at a time + const key1Input = screen.getByDisplayValue("KEY1"); + "NEW_".split("").forEach((char) => { + fireEvent.change(key1Input, { target: { value: char + "KEY1".slice(1) } }); + }); + + // Verify the last setEnv call maintains the order + const lastCall = setEnv.mock.calls[setEnv.mock.calls.length - 1][0] as Record; + const entries = Object.entries(lastCall); + + // The values should stay with their original keys + expect(entries[0][1]).toBe("value1"); // First entry should still have value1 + expect(entries[1][1]).toBe("value2"); // Second entry should still have value2 + }); }); describe("Multiple Operations", () => { From 65a0d46816aca50e87643e325d80160a4743b33b Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 24 Mar 2025 09:47:34 -0700 Subject: [PATCH 20/22] Fix formatting --- client/src/components/__tests__/Sidebar.test.tsx | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/client/src/components/__tests__/Sidebar.test.tsx b/client/src/components/__tests__/Sidebar.test.tsx index fc8a5f4..710c80d 100644 --- a/client/src/components/__tests__/Sidebar.test.tsx +++ b/client/src/components/__tests__/Sidebar.test.tsx @@ -166,7 +166,7 @@ describe("Sidebar Environment Variables", () => { const setEnv = jest.fn(); const initialEnv = { KEY1: "value1", - KEY2: "value2" + KEY2: "value2", }; renderSidebar({ env: initialEnv, setEnv }); @@ -175,13 +175,17 @@ describe("Sidebar Environment Variables", () => { // Type "NEW_" one character at a time const key1Input = screen.getByDisplayValue("KEY1"); "NEW_".split("").forEach((char) => { - fireEvent.change(key1Input, { target: { value: char + "KEY1".slice(1) } }); + fireEvent.change(key1Input, { + target: { value: char + "KEY1".slice(1) }, + }); }); // Verify the last setEnv call maintains the order - const lastCall = setEnv.mock.calls[setEnv.mock.calls.length - 1][0] as Record; + const lastCall = setEnv.mock.calls[ + setEnv.mock.calls.length - 1 + ][0] as Record; const entries = Object.entries(lastCall); - + // The values should stay with their original keys expect(entries[0][1]).toBe("value1"); // First entry should still have value1 expect(entries[1][1]).toBe("value2"); // Second entry should still have value2 From f2f209dbd3ded70d76a181557a1c598a6721dfe5 Mon Sep 17 00:00:00 2001 From: Mark Anthony Cianfrani Date: Sat, 22 Mar 2025 12:52:56 -0400 Subject: [PATCH 21/22] fix(sidebar): maintain order when changing values --- client/src/components/Sidebar.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/client/src/components/Sidebar.tsx b/client/src/components/Sidebar.tsx index aa930e5..4f60a77 100644 --- a/client/src/components/Sidebar.tsx +++ b/client/src/components/Sidebar.tsx @@ -187,9 +187,17 @@ const Sidebar = ({ value={key} onChange={(e) => { const newKey = e.target.value; - const newEnv = { ...env }; - delete newEnv[key]; - newEnv[newKey] = value; + const newEnv = Object.entries(env).reduce( + (acc, [k, v]) => { + if (k === key) { + acc[newKey] = value; + } else { + acc[k] = v; + } + return acc; + }, + {} as Record, + ); setEnv(newEnv); setShownEnvVars((prev) => { const next = new Set(prev); From 16b38071e7554eb5556491f2f04339e58c5a073c Mon Sep 17 00:00:00 2001 From: Ola Hungerford Date: Mon, 24 Mar 2025 12:36:17 -0700 Subject: [PATCH 22/22] Bump version to 0.7.0 --- client/package.json | 2 +- package-lock.json | 12 ++++++------ package.json | 6 +++--- server/package.json | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/client/package.json b/client/package.json index 0471765..30cf37f 100644 --- a/client/package.json +++ b/client/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/inspector-client", - "version": "0.6.0", + "version": "0.7.0", "description": "Client-side application for the Model Context Protocol inspector", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)", diff --git a/package-lock.json b/package-lock.json index 68929c5..eec3c95 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "@modelcontextprotocol/inspector", - "version": "0.6.0", + "version": "0.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@modelcontextprotocol/inspector", - "version": "0.6.0", + "version": "0.7.0", "license": "MIT", "workspaces": [ "client", "server" ], "dependencies": { - "@modelcontextprotocol/inspector-client": "^0.6.0", - "@modelcontextprotocol/inspector-server": "^0.6.0", + "@modelcontextprotocol/inspector-client": "^0.7.0", + "@modelcontextprotocol/inspector-server": "^0.7.0", "concurrently": "^9.0.1", "shell-quote": "^1.8.2", "spawn-rx": "^5.1.2", @@ -32,7 +32,7 @@ }, "client": { "name": "@modelcontextprotocol/inspector-client", - "version": "0.6.0", + "version": "0.7.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.6.1", @@ -11299,7 +11299,7 @@ }, "server": { "name": "@modelcontextprotocol/inspector-server", - "version": "0.6.0", + "version": "0.7.0", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.6.1", diff --git a/package.json b/package.json index e3bce92..66cc1a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/inspector", - "version": "0.6.0", + "version": "0.7.0", "description": "Model Context Protocol inspector", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)", @@ -34,8 +34,8 @@ "publish-all": "npm publish --workspaces --access public && npm publish --access public" }, "dependencies": { - "@modelcontextprotocol/inspector-client": "^0.6.0", - "@modelcontextprotocol/inspector-server": "^0.6.0", + "@modelcontextprotocol/inspector-client": "^0.7.0", + "@modelcontextprotocol/inspector-server": "^0.7.0", "concurrently": "^9.0.1", "shell-quote": "^1.8.2", "spawn-rx": "^5.1.2", diff --git a/server/package.json b/server/package.json index 5d8839f..732993f 100644 --- a/server/package.json +++ b/server/package.json @@ -1,6 +1,6 @@ { "name": "@modelcontextprotocol/inspector-server", - "version": "0.6.0", + "version": "0.7.0", "description": "Server-side application for the Model Context Protocol inspector", "license": "MIT", "author": "Anthropic, PBC (https://anthropic.com)",