diff --git a/.gitignore b/.gitignore index 83930d35604..e20ce30f666 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,4 @@ config/*/.npmrc # The update-dependencies workflow does a sparse checkout # and we don't want to include these files in the PRs it creates. existing-branch-checkout/ +.tmp diff --git a/package-lock.json b/package-lock.json index 478ba619fd8..6bff1fdd473 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10027,6 +10027,10 @@ "resolved": "packages/compass-maybe-protect-connection-string", "link": true }, + "node_modules/@mongodb-js/compass-mcp-server": { + "resolved": "packages/compass-mcp-server", + "link": true + }, "node_modules/@mongodb-js/compass-query-bar": { "resolved": "packages/compass-query-bar", "link": true @@ -36867,9 +36871,9 @@ } }, "node_modules/mongodb-build-info": { - "version": "1.9.6", - "resolved": "https://registry.npmjs.org/mongodb-build-info/-/mongodb-build-info-1.9.6.tgz", - "integrity": "sha512-i0ty4tJO2RaLkq9Z+wTE6mVs45R/fnmq33rngO8QCZ+jMmXfAeDTvTxteRPmEg0lXrf/AZP+oR4y59Cl4nyRnQ==", + "version": "1.9.11", + "resolved": "https://registry.npmjs.org/mongodb-build-info/-/mongodb-build-info-1.9.11.tgz", + "integrity": "sha512-QGPUAT8mvkwIAm3FoGnNOU0j1AUNxF6bYyhDBhierHliQlv7lRl8Lwssecs4zA8Raab6wuM0Tbb0WKHQxGCd2Q==", "license": "Apache-2.0", "dependencies": { "debug": "^4.4.0", @@ -50477,6 +50481,7 @@ "@mongodb-js/compass-indexes": "^5.101.1", "@mongodb-js/compass-intercom": "^0.60.1", "@mongodb-js/compass-logging": "^1.14.1", + "@mongodb-js/compass-mcp-server": "^0.1.0", "@mongodb-js/compass-query-bar": "^8.105.1", "@mongodb-js/compass-saved-aggregations-queries": "^1.102.1", "@mongodb-js/compass-schema": "^6.105.1", @@ -53140,16 +53145,6 @@ "url": "https://opencollective.com/express" } }, - "packages/compass-generative-ai/node_modules/mongodb-build-info": { - "version": "1.9.8", - "resolved": "https://registry.npmjs.org/mongodb-build-info/-/mongodb-build-info-1.9.8.tgz", - "integrity": "sha512-WTGZsohmcEe7yXCTN5hocLUhBWhfF/UySDUoM3ihNzrj67IYJ95Kb6Vi+heBKhqCwzkF8ihq0pzooXcyRnv0ZQ==", - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.4.0", - "mongodb-connection-string-url": "^3.0.1 || ^7.0.0" - } - }, "packages/compass-generative-ai/node_modules/mongodb-mcp-server": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/mongodb-mcp-server/-/mongodb-mcp-server-1.10.0.tgz", @@ -53892,6 +53887,534 @@ "node": ">=0.3.1" } }, + "packages/compass-mcp-server": { + "name": "@mongodb-js/compass-mcp-server", + "version": "0.1.0", + "license": "SSPL", + "dependencies": { + "@mongodb-js/compass-logging": "^1.14.1", + "@mongodb-js/connection-info": "^0.27.4", + "@mongosh/service-provider-node-driver": "^5.0.8", + "compass-preferences-model": "^2.76.1", + "electron": "^37.10.3", + "hadron-ipc": "^3.5.32", + "jsonc-parser": "^3.2.0", + "mongodb-build-info": "^1.8.1", + "mongodb-connection-string-url": "^7.0.1", + "mongodb-mcp-server": "^1.10.0" + }, + "devDependencies": { + "@mongodb-js/eslint-config-compass": "^1.4.22", + "@mongodb-js/mocha-config-compass": "^1.8.0", + "@mongodb-js/prettier-config-compass": "^1.2.9", + "@mongodb-js/tsconfig-compass": "^1.3.0", + "@types/chai": "^4.2.21", + "@types/mocha": "^9.0.0", + "@types/sinon-chai": "^3.2.5", + "chai": "^4.3.6", + "depcheck": "^1.4.1", + "mocha": "^10.2.0", + "nyc": "^15.1.0", + "sinon": "^9.2.3", + "typescript": "^5.9.3" + } + }, + "packages/compass-mcp-server/node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "packages/compass-mcp-server/node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "packages/compass-mcp-server/node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "packages/compass-mcp-server/node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "packages/compass-mcp-server/node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "packages/compass-mcp-server/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "packages/compass-mcp-server/node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "packages/compass-mcp-server/node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "packages/compass-mcp-server/node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "packages/compass-mcp-server/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "packages/compass-mcp-server/node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "packages/compass-mcp-server/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "packages/compass-mcp-server/node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "packages/compass-mcp-server/node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "packages/compass-mcp-server/node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "packages/compass-mcp-server/node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "packages/compass-mcp-server/node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" + }, + "packages/compass-mcp-server/node_modules/lru-cache": { + "version": "11.3.6", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.6.tgz", + "integrity": "sha512-Gf/KoL3C/MlI7Bt0PGI9I+TeTC/I6r/csU58N4BSNc4lppLBeKsOdFYkK+dX0ABDUMJNfCHTyPpzwwO21Awd3A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "packages/compass-mcp-server/node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "packages/compass-mcp-server/node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "packages/compass-mcp-server/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "packages/compass-mcp-server/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "packages/compass-mcp-server/node_modules/mongodb-mcp-server": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/mongodb-mcp-server/-/mongodb-mcp-server-1.10.0.tgz", + "integrity": "sha512-ai4605IiaA6BMiVY2danIthgfZaQkyiRW9tNW+866iPcWtZfGHKxhiQoDw1lUDGa28wUoZf616ug8ChlW5e8Tg==", + "license": "Apache-2.0", + "dependencies": { + "@inquirer/prompts": "^8.0.2", + "@inquirer/select": "^5.1.3", + "@mcp-ui/server": "^5.13.1", + "@modelcontextprotocol/sdk": "^1.29.0", + "@mongodb-js/device-id": "^0.4.6", + "@mongodb-js/devtools-proxy-support": "^0.7.8", + "@mongosh/arg-parser": "^5.0.2", + "@mongosh/service-provider-node-driver": "^5.0.2", + "bson": "^7.2.0", + "chalk": "^5.6.2", + "express": "^5.2.1", + "jsonc-parser": "^3.3.1", + "lru-cache": "^11.2.6", + "mongodb-build-info": "^1.9.7", + "mongodb-connection-string-url": "^7.0.1", + "mongodb-log-writer": "^2.5.7", + "mongodb-redact": "^1.4.6", + "mongodb-schema": "^12.7.0", + "node-fetch": "^3.3.2", + "node-machine-id": "1.1.12", + "oauth4webapi": "^3.8.5", + "openapi-fetch": "^0.17.0", + "prom-client": "^15.1.3", + "semver": "^7.7.2", + "ts-levenshtein": "^1.0.7", + "yaml": "^2.8.1", + "zod": "^4.0.0" + }, + "bin": { + "mongodb-mcp-server": "dist/esm/index.js" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >= 24.0.0" + }, + "optionalDependencies": { + "@mongodb-js/atlas-local": "^1.3.0", + "kerberos": "^7.0.0" + }, + "peerDependencies": { + "@types/express": "^5.0.3" + } + }, + "packages/compass-mcp-server/node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "packages/compass-mcp-server/node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "packages/compass-mcp-server/node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "packages/compass-mcp-server/node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "packages/compass-mcp-server/node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "packages/compass-mcp-server/node_modules/sinon": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-9.2.4.tgz", + "integrity": "sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg==", + "deprecated": "16.1.1", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^1.8.1", + "@sinonjs/fake-timers": "^6.0.1", + "@sinonjs/samsam": "^5.3.1", + "diff": "^4.0.2", + "nise": "^4.0.4", + "supports-color": "^7.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" + } + }, + "packages/compass-mcp-server/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "packages/compass-mcp-server/node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "packages/compass-mcp-server/node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "packages/compass-mcp-server/node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "packages/compass-preferences-model": { "version": "2.76.1", "license": "SSPL", @@ -55329,17 +55852,6 @@ "url": "https://opencollective.com/express" } }, - "packages/compass-web/node_modules/mongodb-build-info": { - "version": "1.9.8", - "resolved": "https://registry.npmjs.org/mongodb-build-info/-/mongodb-build-info-1.9.8.tgz", - "integrity": "sha512-WTGZsohmcEe7yXCTN5hocLUhBWhfF/UySDUoM3ihNzrj67IYJ95Kb6Vi+heBKhqCwzkF8ihq0pzooXcyRnv0ZQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "debug": "^4.4.0", - "mongodb-connection-string-url": "^3.0.1 || ^7.0.0" - } - }, "packages/compass-web/node_modules/mongodb-mcp-server": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/mongodb-mcp-server/-/mongodb-mcp-server-1.10.0.tgz", @@ -55959,6 +56471,7 @@ "dependencies": { "@mongodb-js/compass-components": "^1.66.1", "@mongodb-js/compass-editor": "^0.68.1", + "@mongodb-js/compass-mcp-server": "^0.1.0", "@mongodb-js/connection-info": "^0.27.4", "@mongodb-js/shell-bson-parser": "^1.5.12", "lodash": "^4.18.1", diff --git a/packages/compass-aggregations/src/components/aggregations/aggregations.tsx b/packages/compass-aggregations/src/components/aggregations/aggregations.tsx index 7eeb73a3d0e..dc6fbbd74e0 100644 --- a/packages/compass-aggregations/src/components/aggregations/aggregations.tsx +++ b/packages/compass-aggregations/src/components/aggregations/aggregations.tsx @@ -12,6 +12,8 @@ import { } from '../../modules/settings'; import { savingPipelineNameChanged, + savingPipelineDescriptionChanged, + savingPipelineMcpPromptNameChanged, savingPipelineApply, savingPipelineCancel, } from '../../modules/saving-pipeline'; @@ -96,6 +98,8 @@ const MappedAggregations = connect(mapStateToProps, { clonePipeline, applySettings, savingPipelineNameChanged, + savingPipelineDescriptionChanged, + savingPipelineMcpPromptNameChanged, savingPipelineApply, savingPipelineCancel, dismissViewError, diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-menus.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-menus.tsx index f5b0ce5f7d5..397dd706d41 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-menus.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-menus.tsx @@ -7,6 +7,7 @@ import type { RootState } from '../../../modules'; import { saveCurrentPipeline } from '../../../modules/saved-pipeline'; import { openCreateView, + openSaveAsModalForCurrentPipeline, savingPipelineOpen, } from '../../../modules/saving-pipeline'; @@ -94,10 +95,15 @@ const mapSaveMenuDispatch = { onSave: (name: string) => { return name === '' ? savingPipelineOpen() : saveCurrentPipeline(); }, + // Save As: when there's no name yet, the pipeline was never saved — + // just open the empty modal. When there IS a name (we're saving over + // an existing pipeline), seed the modal from the currently-persisted + // description / mcpPromptName via the openSaveAsModalForCurrentPipeline + // thunk, so the user doesn't have to retype those each time. onSaveAs: (name: string) => { return name === '' ? savingPipelineOpen() - : savingPipelineOpen({ name, isSaveAs: true }); + : openSaveAsModalForCurrentPipeline(); }, onCreateView: openCreateView, }; diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-name.spec.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-name.spec.tsx index d3833fab1fd..2ee97d2a30d 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-name.spec.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-name.spec.tsx @@ -5,37 +5,46 @@ import { expect } from 'chai'; import { PipelineName } from './pipeline-name'; describe('PipelineName', function () { - it('renders Untitled as default name', function () { + // The chip is intentionally hidden when the pipeline has no name — + // that's the "scratchpad / never saved" state. We mirror the + // LoadedFavoriteChip in compass-query-bar, which is also hidden when + // no favorite is loaded; consistent absence-as-signal across both + // tabs. + it('renders nothing when name is empty (untitled pipeline)', function () { render(); - expect(screen.getByTestId('pipeline-name')?.textContent?.trim()).to.equal( - 'Untitled' - ); + expect(screen.queryByTestId('pipeline-name')).to.equal(null); }); - it('renders Untitled as default name with modified text - when modified', function () { + it('renders nothing when name is empty even with isModified true', function () { render(); - expect( - screen - .getByTestId('pipeline-name') - ?.textContent?.trim() - .replace(/\u00a0/g, ' ') - ).to.equal('Untitled – modified'); + expect(screen.queryByTestId('pipeline-name')).to.equal(null); }); - it('renders pipeline name', function () { - render(); - expect(screen.getByTestId('pipeline-name')?.textContent?.trim()).to.equal( - 'Hello' - ); + it('renders the name in a chip when set', function () { + render(); + const chip = screen.getByTestId('pipeline-name'); + expect(chip).to.exist; + expect(chip.textContent).to.contain('Q4 customers'); + expect(chip.getAttribute('data-dirty')).to.equal('false'); + expect(screen.queryByTestId('pipeline-name-dirty-dot')).to.equal(null); }); - it('renders pipeline name with modified text - when modified', function () { - render(); - expect( - screen - .getByTestId('pipeline-name') - ?.textContent?.trim() - .replace(/\u00a0/g, ' ') - ).to.equal('Name changed – modified'); + it('shows the dirty dot when isModified', function () { + render(); + const chip = screen.getByTestId('pipeline-name'); + expect(chip.getAttribute('data-dirty')).to.equal('true'); + expect(screen.getByTestId('pipeline-name-dirty-dot')).to.exist; + }); + + it('mentions unsaved changes in the tooltip-bearing title attr when dirty', function () { + render(); + const chip = screen.getByTestId('pipeline-name'); + expect(chip.getAttribute('title')).to.contain('unsaved changes'); + }); + + it('does not mention unsaved changes in title when clean', function () { + render(); + const chip = screen.getByTestId('pipeline-name'); + expect(chip.getAttribute('title')).to.not.contain('unsaved changes'); }); }); diff --git a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-name.tsx b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-name.tsx index 99b30581852..4b80ffd9468 100644 --- a/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-name.tsx +++ b/packages/compass-aggregations/src/components/pipeline-toolbar/pipeline-settings/pipeline-name.tsx @@ -1,23 +1,41 @@ import React from 'react'; import { connect } from 'react-redux'; -import { css, Body, Tooltip } from '@mongodb-js/compass-components'; +import { + Chip, + Icon, + css, + palette, + spacing, +} from '@mongodb-js/compass-components'; import type { RootState } from '../../../modules'; -const containerStyles = css({ - display: 'flex', - alignItems: 'center', -}); +/** + * Identity chip rendered in the pipeline toolbar when the user is + * editing a saved pipeline. Matches the LoadedFavoriteChip in + * compass-query-bar: same `Chip` primitive, same `Favorite` glyph, + * same yellow dirty-dot convention. Without parity across the + * Documents and Aggregations tabs, users would have to learn two + * different "you have unsaved changes" cues. + * + * Hidden when `name` is empty — an unsaved / never-saved pipeline + * doesn't need a chip; the absence of one tells the user they're in + * scratch-pad mode. + */ -const nameStyles = css({ - maxWidth: '100px', - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', +const chipWrapperStyles = css({ + display: 'inline-flex', + alignItems: 'center', + gap: spacing[100], + alignSelf: 'center', }); -const modifiedStyles = css({ - fontStyle: 'italic', - whiteSpace: 'nowrap', +const dirtyDotStyles = css({ + display: 'inline-block', + width: 8, + height: 8, + borderRadius: '50%', + marginLeft: spacing[100], + backgroundColor: palette.yellow.base, }); type PipelineNameProps = { @@ -29,17 +47,31 @@ export const PipelineName: React.FunctionComponent = ({ name, isModified, }) => { + if (name === '') return null; return ( - - {name === '' ? ( - 'Untitled' - ) : ( - {name}}> - {name} - - )} - {isModified &&  – modified} - + + } + label={ + + {name} + {isModified && ( + + )} + + } + /> + ); }; diff --git a/packages/compass-aggregations/src/components/pipeline/pipeline.tsx b/packages/compass-aggregations/src/components/pipeline/pipeline.tsx index 000d5efbb3c..8defbe42a73 100644 --- a/packages/compass-aggregations/src/components/pipeline/pipeline.tsx +++ b/packages/compass-aggregations/src/components/pipeline/pipeline.tsx @@ -42,6 +42,8 @@ export type PipelineProps = Pick< SavingPipelineModalProps, | 'saveCurrentPipeline' | 'savingPipelineNameChanged' + | 'savingPipelineDescriptionChanged' + | 'savingPipelineMcpPromptNameChanged' | 'savingPipelineApply' | 'savingPipelineCancel' | 'clonePipeline' @@ -59,7 +61,7 @@ export type PipelineProps = Pick< > & { savingPipeline: Pick< SavingPipelineModalProps, - 'name' | 'isOpen' | 'isSaveAs' + 'name' | 'description' | 'mcpPromptName' | 'isOpen' | 'isSaveAs' >; updateViewError?: string | null; dismissViewError: () => void; @@ -80,6 +82,8 @@ export type PipelineProps = Pick< const Pipeline: React.FC = ({ saveCurrentPipeline, savingPipelineNameChanged, + savingPipelineDescriptionChanged, + savingPipelineMcpPromptNameChanged, savingPipelineApply, savingPipelineCancel, clonePipeline, @@ -151,10 +155,14 @@ const Pipeline: React.FC = ({ const savingPipelineModal = ( void; savingPipelineApply: () => void; savingPipelineNameChanged: (v: string) => void; + savingPipelineDescriptionChanged: (v: string) => void; + savingPipelineMcpPromptNameChanged: (v: string) => void; saveCurrentPipeline: () => void; clonePipeline: () => void; track: TrackFunction; } /** - * Saving pipeline modal. + * Saving pipeline modal. Mirrors the query-bar Save-as-favorite dialog + * so the metadata users can attach to a saved pipeline (description, + * MCP prompt name) matches what they can attach to a saved find query. + * Without this parity, AI agents would only ever see find queries in + * the MCP catalog — aggregations wouldn't be discoverable. */ class SavingPipelineModal extends PureComponent { static displayName = 'SavingPipelineModalComponent'; @@ -28,55 +45,86 @@ class SavingPipelineModal extends PureComponent { } } - /** - * Handle the value of the input being changed. - * - * @param {Event} evt - * @returns {void} - */ - onNameChanged(evt: ChangeEvent): void { + onNameChanged = (evt: ChangeEvent): void => { this.props.savingPipelineNameChanged(evt.currentTarget.value); - } + }; + + onDescriptionChanged = (evt: ChangeEvent): void => { + this.props.savingPipelineDescriptionChanged(evt.currentTarget.value); + }; + + onMcpPromptNameChanged = (evt: ChangeEvent): void => { + this.props.savingPipelineMcpPromptNameChanged(evt.currentTarget.value); + }; /** * Calls back to action handlers for changing the name and saving it. * * If canceling from `Save As...`, the current pipeline is not cloned. - * @returns {void} */ - save() { + save = (): void => { if (this.props.isSaveAs) { this.props.clonePipeline(); } this.props.savingPipelineApply(); this.props.saveCurrentPipeline(); + }; + + /** Live validation for the MCP prompt name — same rules as query bar. */ + private getPromptNameError(): string | null { + if (this.props.mcpPromptName.length === 0) return null; + return validateMcpPromptName(this.props.mcpPromptName); } - /** - * Render the component. - * - * @returns {React.Component} The component. - */ render() { const title = this.props.isSaveAs ? 'Save Pipeline As...' : 'Save Pipeline'; + const promptNameError = this.getPromptNameError(); return ( - + + + + + +