From 613c650c40b055231300fe99eaf04bc9f869de43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:26:54 +0000 Subject: [PATCH 1/9] Bump actions/setup-node from 4 to 6 Bumps [actions/setup-node](https://github.com/actions/setup-node) from 4 to 6. - [Release notes](https://github.com/actions/setup-node/releases) - [Commits](https://github.com/actions/setup-node/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/setup-node dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/cli_tests.yml | 2 +- .github/workflows/e2e_tests.yml | 2 +- .github/workflows/main.yml | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/cli_tests.yml b/.github/workflows/cli_tests.yml index ede7643e..3b46b431 100644 --- a/.github/workflows/cli_tests.yml +++ b/.github/workflows/cli_tests.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version-file: package.json cache: npm diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml index 378905b4..abf260c3 100644 --- a/.github/workflows/e2e_tests.yml +++ b/.github/workflows/e2e_tests.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 id: setup_node with: node-version-file: package.json diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d01f7175..fbc4cf42 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -17,7 +17,7 @@ jobs: - name: Check formatting run: npx prettier --check . - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version-file: package.json cache: npm @@ -51,7 +51,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version-file: package.json cache: npm From bde1d59e8fcf1c7739ba5d25184d35d73bbfa60c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:27:00 +0000 Subject: [PATCH 2/9] Bump actions/upload-artifact from 4 to 6 Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 6. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/v4...v6) --- updated-dependencies: - dependency-name: actions/upload-artifact dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/e2e_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml index 378905b4..fa12b62c 100644 --- a/.github/workflows/e2e_tests.yml +++ b/.github/workflows/e2e_tests.yml @@ -51,7 +51,7 @@ jobs: run: npm run test:e2e - name: Upload Playwright Report and Screenshots - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 if: steps.playwright-tests.conclusion != 'skipped' with: name: playwright-report From 742f27dc7abf31cc85d1658593ecf97c6b183d14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:27:04 +0000 Subject: [PATCH 3/9] Bump actions/attest-build-provenance from 2 to 3 Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 2 to 3. - [Release notes](https://github.com/actions/attest-build-provenance/releases) - [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md) - [Commits](https://github.com/actions/attest-build-provenance/compare/v2...v3) --- updated-dependencies: - dependency-name: actions/attest-build-provenance dependency-version: '3' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d01f7175..7c5bb174 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -109,7 +109,7 @@ jobs: labels: ${{ steps.meta.outputs.labels }} - name: Generate artifact attestation - uses: actions/attest-build-provenance@v2 + uses: actions/attest-build-provenance@v3 with: subject-name: ghcr.io/${{ github.repository }} subject-digest: ${{ steps.push.outputs.digest }} From a68211b3e40520bca80cb8810e7d47bd222669e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:27:09 +0000 Subject: [PATCH 4/9] Bump actions/cache from 4 to 5 Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/e2e_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e_tests.yml b/.github/workflows/e2e_tests.yml index 378905b4..c3f27a4a 100644 --- a/.github/workflows/e2e_tests.yml +++ b/.github/workflows/e2e_tests.yml @@ -29,7 +29,7 @@ jobs: # Cache Playwright browsers - name: Cache Playwright browsers id: cache-playwright - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ~/.cache/ms-playwright # The default Playwright cache path key: ${{ runner.os }}-playwright-${{ hashFiles('package-lock.json') }} # Cache key based on OS and package-lock.json From 0f253927d1cc52d036ca6c1d06913b5a99560723 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 13:27:13 +0000 Subject: [PATCH 5/9] Bump actions/github-script from 7 to 8 Bumps [actions/github-script](https://github.com/actions/github-script) from 7 to 8. - [Release notes](https://github.com/actions/github-script/releases) - [Commits](https://github.com/actions/github-script/compare/v7...v8) --- updated-dependencies: - dependency-name: actions/github-script dependency-version: '8' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/claude.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index 23a66f86..52ca5ce4 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -33,7 +33,7 @@ jobs: github.event_name == 'pull_request_review_comment' || github.event_name == 'pull_request_review' id: pr - uses: actions/github-script@v7 + uses: actions/github-script@v8 with: script: | let prNumber; From f97725760a6cc626d4f4ed775ebf4093503ea56b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Feb 2026 20:42:32 -0700 Subject: [PATCH 6/9] Bump the npm_and_yarn group across 1 directory with 3 updates (#1065) Bumps the npm_and_yarn group with 2 updates in the / directory: [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) and [diff](https://github.com/kpdecker/jsdiff). Updates `@modelcontextprotocol/sdk` from 1.25.2 to 1.26.0 - [Release notes](https://github.com/modelcontextprotocol/typescript-sdk/releases) - [Commits](https://github.com/modelcontextprotocol/typescript-sdk/compare/v1.25.2...v1.26.0) Updates `diff` from 4.0.2 to 4.0.4 - [Changelog](https://github.com/kpdecker/jsdiff/blob/master/release-notes.md) - [Commits](https://github.com/kpdecker/jsdiff/compare/v4.0.2...v4.0.4) Updates `hono` from 4.11.3 to 4.11.7 - [Release notes](https://github.com/honojs/hono/releases) - [Commits](https://github.com/honojs/hono/compare/v4.11.3...v4.11.7) --- updated-dependencies: - dependency-name: "@modelcontextprotocol/sdk" dependency-version: 1.26.0 dependency-type: direct:production dependency-group: npm_and_yarn - dependency-name: diff dependency-version: 4.0.4 dependency-type: indirect dependency-group: npm_and_yarn - dependency-name: hono dependency-version: 4.11.7 dependency-type: indirect dependency-group: npm_and_yarn ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 167 ++++++++++++++++++++++++++++++---------------- 1 file changed, 109 insertions(+), 58 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0055b433..0cecbf66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -227,6 +227,7 @@ "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", @@ -240,7 +241,8 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "license": "ISC" + "license": "ISC", + "peer": true }, "node_modules/@babel/code-frame": { "version": "7.27.1", @@ -273,7 +275,6 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -819,6 +820,7 @@ } ], "license": "MIT-0", + "peer": true, "engines": { "node": ">=18" } @@ -839,6 +841,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -863,6 +866,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "@csstools/color-helpers": "^5.1.0", "@csstools/css-calc": "^2.1.4" @@ -1558,9 +1562,9 @@ "license": "MIT" }, "node_modules/@hono/node-server": { - "version": "1.19.7", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.7.tgz", - "integrity": "sha512-vUcD0uauS7EU2caukW8z5lJKtoGMokxNbJtBiwHgpqxEXokaHCBkQUmCHhjFB1VUTWdqj25QoMkMKzgjq+uhrw==", + "version": "1.19.9", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", + "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", "license": "MIT", "engines": { "node": ">=18.14.1" @@ -1884,6 +1888,7 @@ "integrity": "sha512-kazxw2L9IPuZpQ0mEt9lu9Z98SqR74xcagANmMBU16X0lS23yPc0+S6hGLUz8kVRlomZEs/5S/Zlpqwf5yu6OQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/environment": "30.2.0", "@jest/fake-timers": "30.2.0", @@ -1912,6 +1917,7 @@ "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/fake-timers": "30.2.0", "@jest/types": "30.2.0", @@ -1928,6 +1934,7 @@ "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "30.2.0", "@sinonjs/fake-timers": "^13.0.0", @@ -1946,6 +1953,7 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -1959,6 +1967,7 @@ "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -1977,7 +1986,8 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@jest/environment-jsdom-abstract/node_modules/@sinonjs/fake-timers": { "version": "13.0.5", @@ -1985,6 +1995,7 @@ "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@sinonjs/commons": "^3.0.1" } @@ -1995,6 +2006,7 @@ "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", @@ -2007,6 +2019,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -2026,6 +2039,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -2036,6 +2050,7 @@ "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@jest/types": "30.2.0", @@ -2057,6 +2072,7 @@ "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", @@ -2072,6 +2088,7 @@ "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", @@ -2090,6 +2107,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -2103,6 +2121,7 @@ "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -2117,7 +2136,8 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@jest/expect": { "version": "29.7.0", @@ -2186,6 +2206,7 @@ "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "jest-regex-util": "30.0.1" @@ -2200,6 +2221,7 @@ "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" } @@ -2414,12 +2436,12 @@ "link": true }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.25.2", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.25.2.tgz", - "integrity": "sha512-LZFeo4F9M5qOhC/Uc1aQSrBHxMrvxett+9KLHt7OhcExtoiRN9DKgbZffMP/nxjutWDQpfMDfP3nkHI4X9ijww==", + "version": "1.26.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.26.0.tgz", + "integrity": "sha512-Y5RmPncpiDtTXDbLKswIJzTqu2hyBKxTNsgKqKclDbhIgg1wgtf1fRuvxgTnRfcnxtvvgbIEcqUOzZrJ6iSReg==", "license": "MIT", "dependencies": { - "@hono/node-server": "^1.19.7", + "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", @@ -2427,14 +2449,15 @@ "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "jose": "^6.1.1", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", - "zod-to-json-schema": "^3.25.0" + "zod-to-json-schema": "^3.25.1" }, "engines": { "node": ">=18" @@ -3945,7 +3968,8 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -4195,7 +4219,6 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.2.tgz", "integrity": "sha512-LPM2G3Syo1GLzXLGJAKdqoU35XvrWzGJ21/7sgZTUpbkBaOasTj8tjwn6w+hCkqaa1TfJ/w67rJSwYItlJ2mYw==", "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -4234,7 +4257,6 @@ "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.2.2" @@ -4246,7 +4268,6 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", - "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -4387,7 +4408,6 @@ "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.49.0", "@typescript-eslint/types": "8.49.0", @@ -4770,7 +4790,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -5243,7 +5262,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6053,9 +6071,9 @@ "license": "Apache-2.0" }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -6083,7 +6101,8 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/domexception": { "version": "4.0.0", @@ -6341,7 +6360,6 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -6639,7 +6657,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -6679,10 +6696,13 @@ } }, "node_modules/express-rate-limit": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz", - "integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==", + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", + "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", "license": "MIT", + "dependencies": { + "ip-address": "10.0.1" + }, "engines": { "node": ">= 16" }, @@ -7254,11 +7274,10 @@ } }, "node_modules/hono": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.3.tgz", - "integrity": "sha512-PmQi306+M/ct/m5s66Hrg+adPnkD5jiO6IjA7WhWw0gSBSo1EcRegwuI1deZ+wd5pzCGynCcn2DprnE4/yEV4w==", + "version": "4.11.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.11.7.tgz", + "integrity": "sha512-l7qMiNee7t82bH3SeyUCt9UF15EVmaBvsppY2zQtrbIhl/yzBTny+YUxsVjSjQ6gaqaeVtZmGocom8TzBlA4Yw==", "license": "MIT", - "peer": true, "engines": { "node": ">=16.9.0" } @@ -7459,6 +7478,15 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -7733,7 +7761,6 @@ "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -8074,6 +8101,7 @@ "integrity": "sha512-zbBTiqr2Vl78pKp/laGBREYzbZx9ZtqPjOK4++lL4BNDhxRnahg51HtoDrk9/VjIy9IthNEWdKVd7H5bqBhiWQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/environment": "30.2.0", "@jest/environment-jsdom-abstract": "30.2.0", @@ -8099,6 +8127,7 @@ "integrity": "sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/fake-timers": "30.2.0", "@jest/types": "30.2.0", @@ -8115,6 +8144,7 @@ "integrity": "sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "30.2.0", "@sinonjs/fake-timers": "^13.0.0", @@ -8133,6 +8163,7 @@ "integrity": "sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@sinclair/typebox": "^0.34.0" }, @@ -8146,6 +8177,7 @@ "integrity": "sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/pattern": "30.0.1", "@jest/schemas": "30.0.5", @@ -8164,7 +8196,8 @@ "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/jest-environment-jsdom/node_modules/@sinonjs/fake-timers": { "version": "13.0.5", @@ -8172,6 +8205,7 @@ "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "@sinonjs/commons": "^3.0.1" } @@ -8182,6 +8216,7 @@ "integrity": "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", @@ -8194,6 +8229,7 @@ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">= 14" } @@ -8204,6 +8240,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -8223,6 +8260,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -8233,6 +8271,7 @@ "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@asamuzakjp/css-color": "^3.2.0", "rrweb-cssom": "^0.8.0" @@ -8247,6 +8286,7 @@ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "whatwg-mimetype": "^4.0.0", "whatwg-url": "^14.0.0" @@ -8261,6 +8301,7 @@ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "whatwg-encoding": "^3.1.1" }, @@ -8274,6 +8315,7 @@ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -8288,6 +8330,7 @@ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" @@ -8302,6 +8345,7 @@ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -8315,6 +8359,7 @@ "integrity": "sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@jest/types": "30.2.0", @@ -8336,6 +8381,7 @@ "integrity": "sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", @@ -8351,6 +8397,7 @@ "integrity": "sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/types": "30.2.0", "@types/node": "*", @@ -8369,6 +8416,7 @@ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", @@ -8409,6 +8457,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8422,6 +8471,7 @@ "integrity": "sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@jest/schemas": "30.0.5", "ansi-styles": "^5.2.0", @@ -8436,7 +8486,8 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/jest-environment-jsdom/node_modules/tough-cookie": { "version": "5.1.2", @@ -8444,6 +8495,7 @@ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", "dev": true, "license": "BSD-3-Clause", + "peer": true, "dependencies": { "tldts": "^6.1.32" }, @@ -8457,6 +8509,7 @@ "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "punycode": "^2.3.1" }, @@ -8470,6 +8523,7 @@ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "xml-name-validator": "^5.0.0" }, @@ -8483,6 +8537,7 @@ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "iconv-lite": "0.6.3" }, @@ -8496,6 +8551,7 @@ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -8506,6 +8562,7 @@ "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" @@ -8520,6 +8577,7 @@ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, "license": "Apache-2.0", + "peer": true, "engines": { "node": ">=18" } @@ -9114,7 +9172,6 @@ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -9153,7 +9210,6 @@ "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "abab": "^2.0.6", "acorn": "^8.8.1", @@ -9492,6 +9548,7 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -10369,7 +10426,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -10545,6 +10601,7 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -10560,6 +10617,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -10714,7 +10772,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -10727,7 +10784,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", - "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -10741,7 +10797,8 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/react-refresh": { "version": "0.18.0", @@ -11149,7 +11206,8 @@ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/run-applescript": { "version": "7.1.0", @@ -11815,7 +11873,6 @@ "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -11954,7 +12011,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -11978,6 +12034,7 @@ "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "tldts-core": "^6.1.86" }, @@ -11990,7 +12047,8 @@ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tmpl": { "version": "1.0.5", @@ -12163,7 +12221,6 @@ "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "license": "MIT", - "peer": true, "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -12220,7 +12277,6 @@ "integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "~0.27.0", "get-tsconfig": "^4.7.5" @@ -12774,7 +12830,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -12983,7 +13038,6 @@ "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -13077,7 +13131,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -13497,7 +13550,6 @@ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "dev": true, "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -13585,15 +13637,14 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zod-to-json-schema": { - "version": "3.25.0", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.0.tgz", - "integrity": "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==", + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", + "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", "license": "ISC", "peerDependencies": { "zod": "^3.25 || ^4" From ee29c34bd78eb7862446b7dda07cb05af21cb43e Mon Sep 17 00:00:00 2001 From: Bob Dickinson Date: Thu, 5 Feb 2026 16:34:25 -0800 Subject: [PATCH 7/9] Fixed test failures induced by MCP SDK update (added session id generator to Streamable test server). Cleanup up some port usage to avoid potential issues (always use port 0 for servers). Added stdout/stderr to cli status assertion failures to make CLI test failures easier to debug from test output. --- cli/__tests__/helpers/assertions.ts | 16 ++++++++-- cli/__tests__/helpers/fixtures.ts | 8 ++--- cli/__tests__/helpers/test-server-http.ts | 37 ++++++++++++++--------- 3 files changed, 41 insertions(+), 20 deletions(-) diff --git a/cli/__tests__/helpers/assertions.ts b/cli/__tests__/helpers/assertions.ts index e3ed9d02..3631c00b 100644 --- a/cli/__tests__/helpers/assertions.ts +++ b/cli/__tests__/helpers/assertions.ts @@ -1,18 +1,30 @@ import { expect } from "vitest"; import type { CliResult } from "./cli-runner.js"; +function formatCliOutput(result: CliResult): string { + const out = result.stdout?.trim() || "(empty)"; + const err = result.stderr?.trim() || "(empty)"; + return `stdout: ${out}\nstderr: ${err}`; +} + /** * Assert that CLI command succeeded (exit code 0) */ export function expectCliSuccess(result: CliResult) { - expect(result.exitCode).toBe(0); + expect( + result.exitCode, + `CLI exited with code ${result.exitCode}. ${formatCliOutput(result)}`, + ).toBe(0); } /** * Assert that CLI command failed (non-zero exit code) */ export function expectCliFailure(result: CliResult) { - expect(result.exitCode).not.toBe(0); + expect( + result.exitCode, + `CLI unexpectedly exited with code ${result.exitCode}. ${formatCliOutput(result)}`, + ).not.toBe(0); } /** diff --git a/cli/__tests__/helpers/fixtures.ts b/cli/__tests__/helpers/fixtures.ts index 5914f485..c7f10fc7 100644 --- a/cli/__tests__/helpers/fixtures.ts +++ b/cli/__tests__/helpers/fixtures.ts @@ -1,7 +1,7 @@ -import fs from "fs"; -import path from "path"; -import os from "os"; -import crypto from "crypto"; +import * as fs from "fs"; +import * as path from "path"; +import * as os from "os"; +import * as crypto from "crypto"; import { getTestMcpServerCommand } from "./test-server-stdio.js"; /** diff --git a/cli/__tests__/helpers/test-server-http.ts b/cli/__tests__/helpers/test-server-http.ts index 4626ef51..d5eadc3f 100644 --- a/cli/__tests__/helpers/test-server-http.ts +++ b/cli/__tests__/helpers/test-server-http.ts @@ -6,6 +6,7 @@ import type { Request, Response } from "express"; import express from "express"; import { createServer as createHttpServer, Server as HttpServer } from "http"; import { createServer as createNetServer } from "net"; +import { randomUUID } from "crypto"; import * as z from "zod/v4"; import type { ServerConfig } from "./test-fixtures.js"; @@ -187,22 +188,24 @@ export class TestServerHttp { } /** - * Start the server with the specified transport + * Start the server with the specified transport. + * When requestedPort is omitted, uses port 0 so the OS assigns a unique port (avoids EADDRINUSE when tests run in parallel). */ async start( transport: "http" | "sse", requestedPort?: number, ): Promise { - const port = requestedPort - ? await findAvailablePort(requestedPort) - : await findAvailablePort(transport === "http" ? 3001 : 3000); - - this.url = `http://localhost:${port}`; + const port = + requestedPort !== undefined ? await findAvailablePort(requestedPort) : 0; if (transport === "http") { - return this.startHttp(port); + const actualPort = await this.startHttp(port); + this.url = `http://localhost:${actualPort}`; + return actualPort; } else { - return this.startSse(port); + const actualPort = await this.startSse(port); + this.url = `http://localhost:${actualPort}`; + return actualPort; } } @@ -213,8 +216,10 @@ export class TestServerHttp { // Create HTTP server this.httpServer = createHttpServer(app); - // Create StreamableHTTP transport - this.transport = new StreamableHTTPServerTransport({}); + // Create StreamableHTTP transport (stateful so it can handle multiple requests per session) + this.transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + }); // Set up Express route to handle MCP requests app.post("/mcp", async (req: Request, res: Response) => { @@ -292,10 +297,12 @@ export class TestServerHttp { // Connect transport to server await this.mcpServer.connect(this.transport); - // Start listening + // Start listening (port 0 = OS assigns a unique port) return new Promise((resolve, reject) => { this.httpServer!.listen(port, () => { - resolve(port); + const assignedPort = (this.httpServer!.address() as { port: number }) + ?.port; + resolve(assignedPort ?? port); }); this.httpServer!.on("error", reject); }); @@ -371,10 +378,12 @@ export class TestServerHttp { // Note: SSE transport is created per request, so we don't store a single instance this.transport = undefined; - // Start listening + // Start listening (port 0 = OS assigns a unique port) return new Promise((resolve, reject) => { this.httpServer!.listen(port, () => { - resolve(port); + const assignedPort = (this.httpServer!.address() as { port: number }) + ?.port; + resolve(assignedPort ?? port); }); this.httpServer!.on("error", reject); }); From 2af3410b8e41444dc7e1e0f8d46f19ff1e6ec622 Mon Sep 17 00:00:00 2001 From: Cliff Hall Date: Thu, 5 Feb 2026 22:02:20 -0500 Subject: [PATCH 8/9] Add MCP Apps support to Inspector (#1044) * Fix MCP Apps rendering issue and add comprehensive logging - Fixed AppRenderer useEffect dependency array to include resourceContent This ensures the component re-evaluates when resource content arrives - Added detailed console logging throughout the app lifecycle: * Resource fetch and response tracking in App.tsx * Setup conditions and AppBridge creation in AppRenderer.tsx * HTML parsing and iframe rendering steps * PostMessageTransport and AppBridge connection status * App tool filtering and selection in AppsTab.tsx - Refactored AppsTab selectedTool rendering for better tracking The issue was that resourceContent prop updates weren't triggering the AppRenderer setup effect. Now the effect properly responds to both resourceUri and resourceContent changes. Co-authored-by: Cliff Hall * Fix MCP Apps iframe rendering issue The app was getting stuck on 'Loading MCP App...' because the iframe was hidden (display: none) until the oninitialized event fired. However, the PostMessage handshake requires the iframe to be visible to complete. This fix: - Sets loading to false immediately after writing HTML to the iframe - Makes the iframe visible before establishing PostMessage transport - Allows the AppBridge initialization handshake to complete successfully - Removes redundant setLoading(false) from oninitialized callback The iframe is now visible and ready for PostMessage communication before the AppBridge connect() call, enabling proper initialization. Co-authored-by: Cliff Hall * Fix MCP Apps HTML extraction to match spec The AppRenderer was incorrectly checking for a 'type' field in resource contents, but TextResourceContents objects only have uri, mimeType, and text fields according to the MCP specification. Fixed by checking for the presence of the 'text' field directly instead of checking a non-existent 'type' field. This allows the HTML content to be properly extracted and rendered in the iframe. Co-authored-by: Cliff Hall * Add comprehensive tests for MCP Apps support - Add tests for AppsTab component (13 tests) - Add tests for AppRenderer component (17 tests) - Update jest.config.cjs to handle ES modules from @modelcontextprotocol/ext-apps - All 478 tests pass successfully Co-authored-by: Cliff Hall * Add MCP Apps support to Inspector - Add AppsTab component for detecting and listing MCP apps - Add AppRenderer component with full AppBridge integration - Implement UI resource fetching and sandboxed iframe rendering - Add PostMessage transport for bidirectional JSON-RPC communication - Include comprehensive test coverage (30 new tests) - Auto-populate apps when tab becomes active - Support theme awareness and configurable permissions Co-authored-by: Cliff Hall * Prefer structuredContent over content field in tool responses Some MCP servers return different data in structuredContent vs content fields. This change ensures that when structuredContent is present, it is used exclusively for display instead of showing both fields. Changes: - Modified ToolResults.tsx to only show content when structuredContent is absent - Removed unused checkContentCompatibility function - Updated test cases to reflect new behavior Co-authored-by: Cliff Hall * Revert "Prefer structuredContent over content field in tool responses" This reverts commit 3b054b4da08517cce53ef23e60c07bb32b0fa8f5. Claude did not do the right thing. * feat: integrate @mcp-ui/client for MCP application rendering This commit implements the integration of `@mcp-ui/client` to handle the rendering of Model Context Protocol (MCP) applications within the inspector. It includes refactoring the rendering logic, improving resource fetching in the main application state, and setting up a secure sandbox environment. Changes per file: - client/package.json: - Added `@mcp-ui/client` as a dependency. - client/src/components/AppRenderer.tsx: - Refactored to use `McpUiAppRenderer` from `@mcp-ui/client`. - Implemented HTML parsing logic for MCP resource responses. - Configured host context (theme) and sandbox URL for the renderer. - Replaced iframe-based manual rendering with the official component. - client/src/App.tsx: - Added `fetchingResources` state to prevent duplicate concurrent resource requests. - Enhanced `readResource` with better error handling and state tracking. - Optimized resource content mapping to better support application state. - client/src/components/AppsTab.tsx: - Updated to use `getToolUiResourceUri` utility from `@modelcontextprotocol/ext-apps` instead of manual metadata property access. - Simplified tool filtering and selection logic. - client/public/sandbox_proxy.html: - Added a sandbox proxy page to facilitate secure communication between the inspector and the rendered MCP applications. - client/bin/client.js: - Added server rewrites to ensure `sandbox_proxy.html` is served correctly. - Implemented specific `Cache-Control` headers for `sandbox_proxy.html` to prevent stale cached versions. - client/vite.config.ts: - Explicitly configured `publicDir: "public"` to ensure the sandbox proxy is included in the build output. - client/src/components/__tests__/AppRenderer.test.tsx: - Updated tests to mock the new `@mcp-ui/client` component. - Adjusted assertions to verify correct props are passed to the renderer. - Enhanced mock MCP client to include required methods like `getServerCapabilities`. * Potential fix for code scanning alert no. 37: Client-side cross-site scripting Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Update package-lock.json - npm install - npm audit fix * Potential fix for code scanning alert no. 38: Client-side cross-site scripting Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * Potential fix for code scanning alert no. 39: Client-side cross-site scripting Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * prettier * Update client/src/components/AppsTab.tsx Co-authored-by: Ola Hungerford * prettier * In AppsTab.test.tsx, fetch button by aria-label * relative imports * Remove sanitization logic from sandbox_proxy.html. It's breaking the app rendering. Will sort out what best practices for this HTML is later * In sandbox_proxy.html,formatting In AppsTab.tsx - Add app window - when an app is selected, - show input form for apps that have an input schema - button shows app - if app is shown, show maximize button - if maximized, - show minimize button - hide app list * In AppsTab.test.tsx - test new layout and controls * In AppRenderer.tsx - accept toolInput - pass to McpUiAppRenderer * In AppsTab.tsx, if an app has no inputSchema, just show it, otherwise show the form and the "back to form" button when showing the app. * In AppsTab.test.tsx - test new behavior * In ListPane.tsx - remove the Clear button * In ListPane.test.tsx - remove test for clear button * In AppRenderer.tsx, - remove onReadResource and resourceContent, not needed since mcpClient is being passed * In App.tsx and AppsTab.tsx, - remove the passing of these variables * In AppRenderer.test.tsx, and AppsTab.test.tsx - remove tests that fail after having removed onReadResource and resourceContent * Serving the sandbox_proxy.html from a separate port, using the proxy instead of the webclient server. * In App.tsx - pass the path to the sandbox endpoint on the proxy to the AppsTab * In AppsTab.tsx - accept the sandboxPath property and pass it to the AppRenderer * In AppRenderer.tsx - accept the sandboxPath property and pass it to the McpUiAppRenderer * In client.js - remove the rewrite and header config for sandbox_proxy.html * In server/src/index.ts - add a /sandbox endpoint that reads and returns the sandbox_proxy.html file with no-cache header * In server/package.json - add shx for cross-platform copy function - in build script, copy static folder to build folder * Moved sandbox_proxy.html from client/public to server/static * In client.js - return to original state * In vite.config.ts - return to original state * Potential fix for code scanning alert no. 41: Missing rate limiting Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * In AppRenderer.tsx - add open link handler that handles link requests from the UI. Makes sure that the URL starts with http or https * In sandbox_proxy.html - refactor to match [basic-host](https://github.com/modelcontextprotocol/ext-apps/tree/main/examples/basic-host) example - creates an nested iframe for security. - in this implementation, for simplicity the sandbox.ts is inlined in sandbox_proxy.html as javascript * prettier * In AppRenderer.tsx - add support for handling McpUiMessageRequest messages from the UI - show message in a toast * Add logging message handler to AppRenderer In App.tsx - pass an onNotification function to the AppsTab that adds a notification to the notification array In AppsTab.tsx - Accept the onNotification function and pass it to the AppRenderer In AppRenderer.tsx - Accept the onNotification function - added an handleLoggingMessage function that takes a LoggingMessageNotificationParams object, wraps it with a method and params prop to make it a ServerNotification shape, and pass it to the onNotification function - pass handleLoggingMessage to McpUiAppRenderer * In ResourcesTab.test.tsx - fix @ import that wasn't resolving * In AppRenderer.tsx - remove fetch and evaluation of resourceUri, as the mcpClient handles that (review request) * In AppRenderer.test.tsx - remove test for no resourceUri found * Merge branch 'main' into full-apps-support-final Resolved Conflicts: - package-lock.json * Fixed mismatch with sanboxRateLimiter and the express version --------- Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com> Co-authored-by: Cliff Hall Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Ola Hungerford --- cli/package.json | 2 +- client/jest.config.cjs | 7 + client/package.json | 2 + client/src/App.tsx | 89 ++- client/src/components/AppRenderer.tsx | 128 ++++ client/src/components/AppsTab.tsx | 530 ++++++++++++++ client/src/components/ListPane.tsx | 20 +- .../components/__tests__/AppRenderer.test.tsx | 115 +++ .../src/components/__tests__/AppsTab.test.tsx | 481 +++++++++++++ .../__tests__/AuthDebugger.test.tsx | 8 +- .../components/__tests__/ListPane.test.tsx | 8 +- .../__tests__/ResourcesTab.test.tsx | 2 +- package-lock.json | 660 +++++++++++++----- server/package.json | 8 +- server/src/index.ts | 29 + server/static/sandbox_proxy.html | 197 ++++++ 16 files changed, 2090 insertions(+), 196 deletions(-) create mode 100644 client/src/components/AppRenderer.tsx create mode 100644 client/src/components/AppsTab.tsx create mode 100644 client/src/components/__tests__/AppRenderer.test.tsx create mode 100644 client/src/components/__tests__/AppsTab.test.tsx create mode 100644 server/static/sandbox_proxy.html diff --git a/cli/package.json b/cli/package.json index ce521bee..52f66b6f 100644 --- a/cli/package.json +++ b/cli/package.json @@ -25,7 +25,7 @@ "test:cli-metadata": "vitest run metadata.test.ts" }, "devDependencies": { - "@types/express": "^5.0.6", + "@types/express": "^5.0.0", "tsx": "^4.7.0", "vitest": "^4.0.17" }, diff --git a/client/jest.config.cjs b/client/jest.config.cjs index 9b793eae..70485296 100644 --- a/client/jest.config.cjs +++ b/client/jest.config.cjs @@ -13,8 +13,15 @@ module.exports = { tsconfig: "tsconfig.jest.json", }, ], + "^.+\\.m?js$": [ + "ts-jest", + { + tsconfig: "tsconfig.jest.json", + }, + ], }, extensionsToTreatAsEsm: [".ts", ".tsx"], + transformIgnorePatterns: ["node_modules/(?!(@modelcontextprotocol)/)"], testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.(jsx?|tsx?)$", // Exclude directories and files that don't need to be tested testPathIgnorePatterns: [ diff --git a/client/package.json b/client/package.json index 1feec212..1ac32027 100644 --- a/client/package.json +++ b/client/package.json @@ -25,6 +25,8 @@ "cleanup:e2e": "node e2e/global-teardown.js" }, "dependencies": { + "@mcp-ui/client": "^6.0.0", + "@modelcontextprotocol/ext-apps": "^1.0.0", "@modelcontextprotocol/sdk": "^1.25.2", "@radix-ui/react-checkbox": "^1.1.4", "@radix-ui/react-dialog": "^1.1.3", diff --git a/client/src/App.tsx b/client/src/App.tsx index 39fc2812..5a69495b 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -51,6 +51,7 @@ import { import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Button } from "@/components/ui/button"; import { + AppWindow, Bell, Files, FolderTree, @@ -75,6 +76,7 @@ import SamplingTab, { PendingRequest } from "./components/SamplingTab"; import Sidebar from "./components/Sidebar"; import ToolsTab from "./components/ToolsTab"; import TasksTab from "./components/TasksTab"; +import AppsTab from "./components/AppsTab"; import { InspectorConfig } from "./lib/configurationTypes"; import { getMCPProxyAddress, @@ -126,6 +128,9 @@ const App = () => { const [resourceContentMap, setResourceContentMap] = useState< Record >({}); + const [fetchingResources, setFetchingResources] = useState>( + new Set(), + ); const [prompts, setPrompts] = useState([]); const [promptContent, setPromptContent] = useState(""); const [tools, setTools] = useState([]); @@ -308,11 +313,13 @@ const App = () => { ...(serverCapabilities?.prompts ? ["prompts"] : []), ...(serverCapabilities?.tools ? ["tools"] : []), ...(serverCapabilities?.tasks ? ["tasks"] : []), + "apps", "ping", "sampling", "elicitations", "roots", "auth", + "metadata", ]; if (!validTabs.includes(originatingTab)) return; @@ -440,11 +447,13 @@ const App = () => { ...(serverCapabilities?.prompts ? ["prompts"] : []), ...(serverCapabilities?.tools ? ["tools"] : []), ...(serverCapabilities?.tasks ? ["tasks"] : []), + "apps", "ping", "sampling", "elicitations", "roots", "auth", + "metadata", ]; const isValidTab = validTabs.includes(hash); @@ -473,6 +482,13 @@ const App = () => { // eslint-disable-next-line react-hooks/exhaustive-deps }, [mcpClient, activeTab]); + useEffect(() => { + if (mcpClient && activeTab === "apps" && serverCapabilities?.tools) { + void listTools(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [mcpClient, activeTab, serverCapabilities?.tools]); + useEffect(() => { localStorage.setItem("lastCommand", command); }, [command]); @@ -757,11 +773,13 @@ const App = () => { ...(serverCapabilities?.prompts ? ["prompts"] : []), ...(serverCapabilities?.tools ? ["tools"] : []), ...(serverCapabilities?.tasks ? ["tasks"] : []), + "apps", "ping", "sampling", "elicitations", "roots", "auth", + "metadata", ]; if (validTabs.includes(originatingTab)) { @@ -851,22 +869,48 @@ const App = () => { }; const readResource = async (uri: string) => { + if (fetchingResources.has(uri) || resourceContentMap[uri]) { + return; + } + + console.log("[App] Reading resource:", uri); + setFetchingResources((prev) => new Set(prev).add(uri)); lastToolCallOriginTabRef.current = currentTabRef.current; - const response = await sendMCPRequest( - { - method: "resources/read" as const, - params: { uri }, - }, - ReadResourceResultSchema, - "resources", - ); - const content = JSON.stringify(response, null, 2); - setResourceContent(content); - setResourceContentMap((prev) => ({ - ...prev, - [uri]: content, - })); + try { + const response = await sendMCPRequest( + { + method: "resources/read" as const, + params: { uri }, + }, + ReadResourceResultSchema, + "resources", + ); + console.log("[App] Resource read response:", { + uri, + responseLength: JSON.stringify(response).length, + hasContents: !!(response as { contents?: unknown[] }).contents, + }); + const content = JSON.stringify(response, null, 2); + setResourceContent(content); + setResourceContentMap((prev) => ({ + ...prev, + [uri]: content, + })); + } catch (error) { + console.error(`[App] Failed to read resource ${uri}:`, error); + const errorString = (error as Error).message ?? String(error); + setResourceContentMap((prev) => ({ + ...prev, + [uri]: JSON.stringify({ error: errorString }), + })); + } finally { + setFetchingResources((prev) => { + const next = new Set(prev); + next.delete(uri); + return next; + }); + } }; const subscribeToResource = async (uri: string) => { @@ -1308,6 +1352,10 @@ const App = () => { Tasks + + + Apps + Ping @@ -1497,6 +1545,19 @@ const App = () => { error={errors.tasks} nextCursor={nextTaskCursor} /> + { + clearError("tools"); + listTools(); + }} + error={errors.tools} + mcpClient={mcpClient} + onNotification={(notification) => { + setNotifications((prev) => [...prev, notification]); + }} + /> { diff --git a/client/src/components/AppRenderer.tsx b/client/src/components/AppRenderer.tsx new file mode 100644 index 00000000..e0c25980 --- /dev/null +++ b/client/src/components/AppRenderer.tsx @@ -0,0 +1,128 @@ +import { useMemo, useState } from "react"; +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { + Tool, + ContentBlock, + ServerNotification, + LoggingMessageNotificationParams, +} from "@modelcontextprotocol/sdk/types.js"; +import { + AppRenderer as McpUiAppRenderer, + type McpUiHostContext, + type RequestHandlerExtra, +} from "@mcp-ui/client"; +import { + type McpUiMessageRequest, + type McpUiMessageResult, +} from "@modelcontextprotocol/ext-apps/app-bridge"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { AlertCircle } from "lucide-react"; +import { useToast } from "@/lib/hooks/useToast"; + +interface AppRendererProps { + sandboxPath: string; + tool: Tool; + mcpClient: Client | null; + toolInput?: Record; + onNotification?: (notification: ServerNotification) => void; +} + +const AppRenderer = ({ + sandboxPath, + tool, + mcpClient, + toolInput, + onNotification, +}: AppRendererProps) => { + const [error, setError] = useState(null); + const { toast } = useToast(); + + const hostContext: McpUiHostContext = useMemo( + () => ({ + theme: document.documentElement.classList.contains("dark") + ? "dark" + : "light", + }), + [], + ); + + const handleOpenLink = async ({ url }: { url: string }) => { + let isError = true; + if (url.startsWith("https://") || url.startsWith("http://")) { + window.open(url, "_blank"); + isError = false; + } + return { isError }; + }; + + const handleMessage = async ( + params: McpUiMessageRequest["params"], + // eslint-disable-next-line @typescript-eslint/no-unused-vars + _extra: RequestHandlerExtra, + ): Promise => { + const message = params.content + .filter((block): block is ContentBlock & { type: "text" } => + Boolean(block.type === "text"), + ) + .map((block) => block.text) + .join("\n"); + + if (message) { + toast({ + description: message, + }); + } + + return {}; + }; + + const handleLoggingMessage = (params: LoggingMessageNotificationParams) => { + if (onNotification) { + onNotification({ + method: "notifications/message", + params, + } as ServerNotification); + } + }; + + if (!mcpClient) { + return ( + + + Waiting for MCP client... + + ); + } + + return ( +
+ {error && ( + + + {error} + + )} + +
+ setError(err.message)} + /> +
+
+ ); +}; + +export default AppRenderer; diff --git a/client/src/components/AppsTab.tsx b/client/src/components/AppsTab.tsx new file mode 100644 index 00000000..1943c439 --- /dev/null +++ b/client/src/components/AppsTab.tsx @@ -0,0 +1,530 @@ +import { useEffect, useState, useCallback, useRef } from "react"; +import { TabsContent } from "@/components/ui/tabs"; +import { Button } from "@/components/ui/button"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { + AlertCircle, + X, + Play, + ChevronRight, + Maximize2, + Minimize2, +} from "lucide-react"; +import { Tool, ServerNotification } from "@modelcontextprotocol/sdk/types.js"; +import { Client } from "@modelcontextprotocol/sdk/client/index.js"; +import { getToolUiResourceUri } from "@modelcontextprotocol/ext-apps/app-bridge"; +import AppRenderer from "./AppRenderer"; +import ListPane from "./ListPane"; +import IconDisplay, { WithIcons } from "./IconDisplay"; +import { Label } from "@/components/ui/label"; +import { Checkbox } from "@/components/ui/checkbox"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Textarea } from "@/components/ui/textarea"; +import { Input } from "@/components/ui/input"; +import DynamicJsonForm, { DynamicJsonFormRef } from "./DynamicJsonForm"; +import { JsonSchemaType, JsonValue } from "@/utils/jsonUtils"; +import { + generateDefaultValue, + isPropertyRequired, + normalizeUnionType, + resolveRef, +} from "@/utils/schemaUtils"; + +interface AppsTabProps { + sandboxPath: string; + tools: Tool[]; + listTools: () => void; + error: string | null; + mcpClient: Client | null; + onNotification?: (notification: ServerNotification) => void; +} + +// Type guard to check if a tool has UI metadata +const hasUIMetadata = (tool: Tool): boolean => { + return !!getToolUiResourceUri(tool); +}; + +const AppsTab = ({ + sandboxPath, + tools, + listTools, + error, + mcpClient, + onNotification, +}: AppsTabProps) => { + const [appTools, setAppTools] = useState([]); + const [selectedTool, setSelectedTool] = useState(null); + const [params, setParams] = useState>({}); + const [isAppOpen, setIsAppOpen] = useState(false); + const [isMaximized, setIsMaximized] = useState(false); + const [hasValidationErrors, setHasValidationErrors] = useState(false); + const formRefs = useRef>({}); + + // Function to check if any form has validation errors + const checkValidationErrors = useCallback(() => { + const errors = Object.values(formRefs.current).some( + (ref) => ref && !ref.validateJson().isValid, + ); + setHasValidationErrors(errors); + return errors; + }, []); + + // Filter tools that have UI metadata + useEffect(() => { + const filtered = tools.filter(hasUIMetadata); + console.log("[AppsTab] Filtered app tools:", { + totalTools: tools.length, + appTools: filtered.length, + appToolNames: filtered.map((t) => t.name), + }); + setAppTools(filtered); + + // If current selected tool is no longer available, reset selection + if (selectedTool && !filtered.find((t) => t.name === selectedTool.name)) { + setSelectedTool(null); + setIsAppOpen(false); + } + }, [tools, selectedTool]); + + useEffect(() => { + if (selectedTool) { + const initialParams = Object.entries( + selectedTool.inputSchema.properties ?? [], + ).map(([key, value]) => { + // First resolve any $ref references + const resolvedValue = resolveRef( + value as JsonSchemaType, + selectedTool.inputSchema as JsonSchemaType, + ); + return [ + key, + generateDefaultValue( + resolvedValue, + key, + selectedTool.inputSchema as JsonSchemaType, + ), + ]; + }); + setParams(Object.fromEntries(initialParams)); + setHasValidationErrors(false); + formRefs.current = {}; + } else { + setParams({}); + setIsAppOpen(false); + } + }, [selectedTool]); + + const handleRefresh = useCallback(() => { + listTools(); + }, [listTools]); + + const handleCloseApp = useCallback(() => { + setIsAppOpen(false); + }, []); + + const handleOpenApp = useCallback(() => { + if (!checkValidationErrors()) { + setIsAppOpen(true); + } + }, [checkValidationErrors]); + + const handleSelectTool = useCallback((tool: Tool) => { + setSelectedTool(tool); + const hasFields = + tool.inputSchema.properties && + Object.keys(tool.inputSchema.properties).length > 0; + setIsAppOpen(!hasFields); + }, []); + + const handleDeselectTool = useCallback(() => { + setSelectedTool(null); + setIsAppOpen(false); + setIsMaximized(false); + }, []); + + return ( + +
+ {!isMaximized && ( + { + return ( +
+
+ +
+
+ {tool.name} + {tool.description && ( + + {tool.description} + + )} +
+ +
+ ); + }} + title="MCP Apps" + buttonText="Refresh Apps" + /> + )} + +
+
+
+
+ {selectedTool && ( + + )} +

+ {selectedTool ? selectedTool.name : "Select an app"} +

+
+
+ {selectedTool && isAppOpen && ( + + )} + {selectedTool && ( + + )} +
+
+
+ +
+ {error && ( + + + {error} + + )} + + {selectedTool ? ( + (() => { + const hasFields = + selectedTool.inputSchema.properties && + Object.keys(selectedTool.inputSchema.properties).length > 0; + + return ( +
+ {!isAppOpen ? ( +
+ {selectedTool.description && ( +

+ {selectedTool.description} +

+ )} + +
+

App Input

+ {Object.entries( + selectedTool.inputSchema.properties ?? [], + ).map(([key, value]) => { + // First resolve any $ref references + const resolvedValue = resolveRef( + value as JsonSchemaType, + selectedTool.inputSchema as JsonSchemaType, + ); + const prop = normalizeUnionType(resolvedValue); + const inputSchema = + selectedTool.inputSchema as JsonSchemaType; + const required = isPropertyRequired( + key, + inputSchema, + ); + + return ( +
+
+ + {prop.nullable ? ( +
+ + setParams({ + ...params, + [key]: checked + ? null + : prop.type === "array" + ? undefined + : prop.default !== null + ? prop.default + : prop.type === "boolean" + ? false + : prop.type === "string" + ? "" + : undefined, + }) + } + /> + +
+ ) : null} +
+ +
+ {prop.type === "boolean" ? ( +
+ + setParams({ + ...params, + [key]: checked, + }) + } + /> + +
+ ) : prop.type === "string" && prop.enum ? ( + + ) : prop.type === "string" ? ( +