commit f94ce14868964445936ce073930f6ff9a0de4c80 Author: AdsPower Date: Thu Mar 27 19:41:01 2025 +0800 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9bb7080 --- /dev/null +++ b/.gitignore @@ -0,0 +1,59 @@ +# Dependencies +node_modules/ +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +.pnpm-store/ + +# Build output +dist/ +build/ +out/ +.output/ + +# IDE and editor files +.idea/ +.vscode/ +*.swp +*.swo +.DS_Store +*.sublime-workspace +*.sublime-project + +# Environment variables +.env +.env.local +.env.*.local +.env.development +.env.test +.env.production + +# Cache and temporary files +.cache/ +.temp/ +.tmp/ +coverage/ +*.log +.eslintcache + +# System files +Thumbs.db +.DS_Store +*.pem + +# Debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# Local files +*.local + +# Testing +coverage/ +.nyc_output/ + +# Typescript +*.tsbuildinfo \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..66440b8 --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "adspower-local-api-mcp", + "version": "1.0.0", + "main": "index.js", + "type": "module", + "bin": { + "adspower": "./build/index.js" + }, + "scripts": { + "build": "tsc && chmod 755 ./build/index.js", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "files": [ + "build" + ], + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@modelcontextprotocol/sdk": "^1.7.0", + "axios": "^1.8.4", + "zod": "^3.24.2" + }, + "devDependencies": { + "@types/node": "^22.13.13", + "typescript": "^5.8.2" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..d899f0b --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,829 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@modelcontextprotocol/sdk': + specifier: ^1.7.0 + version: 1.7.0 + axios: + specifier: ^1.8.4 + version: 1.8.4 + zod: + specifier: ^3.24.2 + version: 3.24.2 + devDependencies: + '@types/node': + specifier: ^22.13.13 + version: 22.13.13 + typescript: + specifier: ^5.8.2 + version: 5.8.2 + +packages: + + '@modelcontextprotocol/sdk@1.7.0': + resolution: {integrity: sha512-IYPe/FLpvF3IZrd/f5p5ffmWhMc3aEMuM2wGJASDqC2Ge7qatVCdbfPx3n/5xFeb19xN0j/911M2AaFuircsWA==} + engines: {node: '>=18'} + + '@types/node@22.13.13': + resolution: {integrity: sha512-ClsL5nMwKaBRwPcCvH8E7+nU4GxHVx1axNvMZTFHMEfNI7oahimt26P5zjVCRrjiIWj6YFXfE1v3dEp94wLcGQ==} + + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.8.4: + resolution: {integrity: sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==} + + body-parser@2.1.0: + resolution: {integrity: sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ==} + engines: {node: '>=18'} + + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + content-disposition@1.0.0: + resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==} + engines: {node: '>= 0.6'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.1: + resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} + engines: {node: '>= 0.6'} + + cors@2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.0: + resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + eventsource-parser@3.0.0: + resolution: {integrity: sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.5: + resolution: {integrity: sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==} + engines: {node: '>=18.0.0'} + + express-rate-limit@7.5.0: + resolution: {integrity: sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==} + engines: {node: '>= 16'} + peerDependencies: + express: ^4.11 || 5 || ^5.0.0-beta.1 + + express@5.0.1: + resolution: {integrity: sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ==} + engines: {node: '>= 18'} + + finalhandler@2.1.0: + resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==} + engines: {node: '>= 0.8'} + + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data@4.0.2: + resolution: {integrity: sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==} + engines: {node: '>= 6'} + + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + iconv-lite@0.5.2: + resolution: {integrity: sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + + methods@1.1.2: + resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} + engines: {node: '>= 0.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime-types@3.0.0: + resolution: {integrity: sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w==} + engines: {node: '>= 0.6'} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + path-to-regexp@8.2.0: + resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==} + engines: {node: '>=16'} + + pkce-challenge@4.1.0: + resolution: {integrity: sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ==} + engines: {node: '>=16.20.0'} + + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + qs@6.13.0: + resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} + engines: {node: '>=0.6'} + + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.0: + resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==} + engines: {node: '>= 0.8'} + + router@2.1.0: + resolution: {integrity: sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA==} + engines: {node: '>= 18'} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + send@1.1.0: + resolution: {integrity: sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA==} + engines: {node: '>= 18'} + + serve-static@2.1.0: + resolution: {integrity: sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA==} + engines: {node: '>= 18'} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + type-is@2.0.0: + resolution: {integrity: sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw==} + engines: {node: '>= 0.6'} + + typescript@5.8.2: + resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.20.0: + resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + zod-to-json-schema@3.24.5: + resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + peerDependencies: + zod: ^3.24.1 + + zod@3.24.2: + resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + +snapshots: + + '@modelcontextprotocol/sdk@1.7.0': + dependencies: + content-type: 1.0.5 + cors: 2.8.5 + eventsource: 3.0.5 + express: 5.0.1 + express-rate-limit: 7.5.0(express@5.0.1) + pkce-challenge: 4.1.0 + raw-body: 3.0.0 + zod: 3.24.2 + zod-to-json-schema: 3.24.5(zod@3.24.2) + transitivePeerDependencies: + - supports-color + + '@types/node@22.13.13': + dependencies: + undici-types: 6.20.0 + + accepts@2.0.0: + dependencies: + mime-types: 3.0.0 + negotiator: 1.0.0 + + asynckit@0.4.0: {} + + axios@1.8.4: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.2 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + body-parser@2.1.0: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.0 + http-errors: 2.0.0 + iconv-lite: 0.5.2 + on-finished: 2.4.1 + qs: 6.14.0 + raw-body: 3.0.0 + type-is: 2.0.0 + transitivePeerDependencies: + - supports-color + + bytes@3.1.2: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + content-disposition@1.0.0: + dependencies: + safe-buffer: 5.2.1 + + content-type@1.0.5: {} + + cookie-signature@1.2.2: {} + + cookie@0.7.1: {} + + cors@2.8.5: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + + debug@4.3.6: + dependencies: + ms: 2.1.2 + + debug@4.4.0: + dependencies: + ms: 2.1.3 + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + destroy@1.2.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + ee-first@1.1.1: {} + + encodeurl@2.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + escape-html@1.0.3: {} + + etag@1.8.1: {} + + eventsource-parser@3.0.0: {} + + eventsource@3.0.5: + dependencies: + eventsource-parser: 3.0.0 + + express-rate-limit@7.5.0(express@5.0.1): + dependencies: + express: 5.0.1 + + express@5.0.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.1.0 + content-disposition: 1.0.0 + content-type: 1.0.5 + cookie: 0.7.1 + cookie-signature: 1.2.2 + debug: 4.3.6 + 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 + methods: 1.1.2 + 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.13.0 + range-parser: 1.2.1 + router: 2.1.0 + safe-buffer: 5.2.1 + send: 1.1.0 + serve-static: 2.1.0 + setprototypeof: 1.2.0 + statuses: 2.0.1 + type-is: 2.0.0 + utils-merge: 1.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + finalhandler@2.1.0: + 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 + transitivePeerDependencies: + - supports-color + + follow-redirects@1.15.9: {} + + form-data@4.0.2: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + mime-types: 2.1.35 + + forwarded@0.2.0: {} + + fresh@0.5.2: {} + + fresh@2.0.0: {} + + function-bind@1.1.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + http-errors@2.0.0: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.1 + toidentifier: 1.0.1 + + iconv-lite@0.5.2: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + + inherits@2.0.4: {} + + ipaddr.js@1.9.1: {} + + is-promise@4.0.0: {} + + math-intrinsics@1.1.0: {} + + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + + methods@1.1.2: {} + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mime-types@3.0.0: + dependencies: + mime-db: 1.54.0 + + ms@2.1.2: {} + + ms@2.1.3: {} + + negotiator@1.0.0: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + parseurl@1.3.3: {} + + path-to-regexp@8.2.0: {} + + pkce-challenge@4.1.0: {} + + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + + proxy-from-env@1.1.0: {} + + qs@6.13.0: + dependencies: + side-channel: 1.1.0 + + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + + range-parser@1.2.1: {} + + raw-body@3.0.0: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.6.3 + unpipe: 1.0.0 + + router@2.1.0: + dependencies: + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.2.0 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + send@1.1.0: + dependencies: + debug: 4.3.6 + destroy: 1.2.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 0.5.2 + http-errors: 2.0.0 + mime-types: 2.1.35 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.1 + transitivePeerDependencies: + - supports-color + + serve-static@2.1.0: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.1.0 + transitivePeerDependencies: + - supports-color + + setprototypeof@1.2.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + statuses@2.0.1: {} + + toidentifier@1.0.1: {} + + type-is@2.0.0: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.0 + + typescript@5.8.2: {} + + undici-types@6.20.0: {} + + unpipe@1.0.0: {} + + utils-merge@1.0.1: {} + + vary@1.1.2: {} + + wrappy@1.0.2: {} + + zod-to-json-schema@3.24.5(zod@3.24.2): + dependencies: + zod: 3.24.2 + + zod@3.24.2: {} diff --git a/src/constants/api.ts b/src/constants/api.ts new file mode 100644 index 0000000..b8d85e1 --- /dev/null +++ b/src/constants/api.ts @@ -0,0 +1,16 @@ +export const LOCAL_API_BASE = 'http://127.0.0.1:50325'; + +export const API_ENDPOINTS = { + START_BROWSER: '/api/v1/browser/start', + CLOSE_BROWSER: '/api/v1/browser/stop', + CREATE_BROWSER: '/api/v1/user/create', + GET_BROWSER_LIST: '/api/v1/user/list', + GET_GROUP_LIST: '/api/v1/group/list', + GET_APPLICATION_LIST: '/api/v1/application/list', + UPDATE_BROWSER: '/api/v1/user/update', + DELETE_BROWSER: '/api/v1/user/delete', + GET_OPENED_BROWSER: '/api/v1/browser/local-active', + CREATE_GROUP: '/api/v1/group/create', + UPDATE_GROUP: '/api/v1/group/update', + MOVE_BROWSER: '/api/v1/user/regroup' +} as const; \ No newline at end of file diff --git a/src/handlers/application.ts b/src/handlers/application.ts new file mode 100644 index 0000000..1fb99ab --- /dev/null +++ b/src/handlers/application.ts @@ -0,0 +1,15 @@ +import axios from 'axios'; +import { LOCAL_API_BASE, API_ENDPOINTS } from '../constants/api.js'; +import type { GetApplicationListParams } from '../types/application.js'; + +export const applicationHandlers = { + async getApplicationList({ size }: GetApplicationListParams) { + const params = new URLSearchParams(); + if (size) { + params.set('page_size', size.toString()); + } + + const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.GET_APPLICATION_LIST}`, { params }); + return `Application list: ${JSON.stringify(response.data.data.list, null, 2)}`; + } +}; \ No newline at end of file diff --git a/src/handlers/browser.ts b/src/handlers/browser.ts new file mode 100644 index 0000000..ab2cc91 --- /dev/null +++ b/src/handlers/browser.ts @@ -0,0 +1,120 @@ +import axios from 'axios'; +import { LOCAL_API_BASE, API_ENDPOINTS } from '../constants/api.js'; +import { buildRequestBody } from '../utils/requestBuilder.js'; +import type { + OpenBrowserParams, + CloseBrowserParams, + CreateBrowserParams, + UpdateBrowserParams, + DeleteBrowserParams, + GetBrowserListParams, + MoveBrowserParams +} from '../types/browser.js'; + +export const browserHandlers = { + async openBrowser({ serialNumber, userId }: OpenBrowserParams) { + const params = new URLSearchParams(); + if (serialNumber) { + params.set('serial_number', serialNumber); + } + if (userId) { + params.set('user_id', userId); + } + params.set('open_tabs', '0'); + + const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.START_BROWSER}`, { params }); + if (response.data.code === 0) { + return `Browser opened successfully with: ${Object.entries(response.data.data).map(([key, value]) => `${key}: ${value}`).join('\n')}`; + } + throw new Error(`Failed to open browser: ${response.data.message}`); + }, + + async closeBrowser({ userId }: CloseBrowserParams) { + const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.CLOSE_BROWSER}`, { + params: { user_id: userId } + }); + return 'Browser closed successfully'; + }, + + async createBrowser(params: CreateBrowserParams) { + const requestBody = buildRequestBody(params); + const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.CREATE_BROWSER}`, requestBody); + + if (response.data.code === 0) { + return `Browser created successfully with: ${Object.entries(response.data.data).map(([key, value]) => `${key}: ${value}`).join('\n')}`; + } + throw new Error(`Failed to create browser: ${response.data.message}`); + }, + + async updateBrowser(params: UpdateBrowserParams) { + const requestBody = buildRequestBody({ + ...params, + userId: undefined + }); + requestBody.user_id = params.userId; + + const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.UPDATE_BROWSER}`, requestBody); + + if (response.data.code === 0) { + return `Browser updated successfully with: ${Object.entries(response.data.data).map(([key, value]) => `${key}: ${value}`).join('\n')}`; + } + throw new Error(`Failed to update browser: ${response.data.message}`); + }, + + async deleteBrowser({ userIds }: DeleteBrowserParams) { + const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.DELETE_BROWSER}`, { + user_ids: userIds + }); + + if (response.data.code === 0) { + return `Browsers deleted successfully: ${userIds.join(', ')}`; + } + throw new Error(`Failed to delete browsers: ${response.data.message}`); + }, + + async getBrowserList(params: GetBrowserListParams) { + const { groupId, size, id, serialNumber, sort, order } = params; + const urlParams = new URLSearchParams(); + if (size) { + urlParams.set('page_size', size.toString()); + } + if (id) { + urlParams.set('user_id', id); + } + if (groupId) { + urlParams.set('group_id', groupId); + } + if (serialNumber) { + urlParams.set('serial_number', serialNumber); + } + if (sort) { + urlParams.set('user_sort', JSON.stringify({ + [sort]: order || 'asc', + })); + } + + const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.GET_BROWSER_LIST}`, { params: urlParams }); + return `Browser list: ${JSON.stringify(response.data.data.list, null, 2)}`; + }, + + async getOpenedBrowser() { + const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.GET_OPENED_BROWSER}`); + + if (response.data.code === 0) { + return `Opened browser list: ${JSON.stringify(response.data.data.list, null, 2)}`; + } + throw new Error(`Failed to get opened browsers: ${response.data.message}`); + }, + + async moveBrowser({ groupId, userIds }: MoveBrowserParams) { + const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.MOVE_BROWSER}`, { + group_id: groupId, + user_ids: userIds + }); + + if (response.data.code === 0) { + return `Browsers moved successfully to group ${groupId}: ${userIds.join(', ')}`; + } + throw new Error(`Failed to move browsers: ${response.data.message}`); + } +}; \ No newline at end of file diff --git a/src/handlers/group.ts b/src/handlers/group.ts new file mode 100644 index 0000000..5250261 --- /dev/null +++ b/src/handlers/group.ts @@ -0,0 +1,57 @@ +import axios from 'axios'; +import { LOCAL_API_BASE, API_ENDPOINTS } from '../constants/api.js'; +import type { + CreateGroupParams, + UpdateGroupParams, + GetGroupListParams +} from '../types/group.js'; + +export const groupHandlers = { + async createGroup({ groupName, remark }: CreateGroupParams) { + const requestBody: Record = { + group_name: groupName + }; + + if (remark !== undefined) { + requestBody.remark = remark; + } + + const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.CREATE_GROUP}`, requestBody); + + if (response.data.code === 0) { + return `Group created successfully with name: ${groupName}${remark ? `, remark: ${remark}` : ''}`; + } + throw new Error(`Failed to create group: ${response.data.message}`); + }, + + async updateGroup({ groupId, groupName, remark }: UpdateGroupParams) { + const requestBody: Record = { + group_id: groupId, + group_name: groupName + }; + + if (remark !== undefined) { + requestBody.remark = remark; + } + + const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.UPDATE_GROUP}`, requestBody); + + if (response.data.code === 0) { + return `Group updated successfully with id: ${groupId}, name: ${groupName}${remark !== undefined ? `, remark: ${remark === null ? '(cleared)' : remark}` : ''}`; + } + throw new Error(`Failed to update group: ${response.data.message}`); + }, + + async getGroupList({ name, size }: GetGroupListParams) { + const params = new URLSearchParams(); + if (name) { + params.set('group_name', name); + } + if (size) { + params.set('page_size', size.toString()); + } + + const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.GET_GROUP_LIST}`, { params }); + return `Group list: ${JSON.stringify(response.data.data.list, null, 2)}`; + } +}; \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..4a0889a --- /dev/null +++ b/src/index.ts @@ -0,0 +1,27 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; +import { registerTools } from './utils/toolRegister.js'; + +// Create server instance +const server = new McpServer({ + name: 'adspower-local-api', + version: '1.0.0', + capabilities: { + resources: {}, + tools: {}, + }, +}); + +// Register all tools +registerTools(server); + +async function main() { + const transport = new StdioServerTransport(); + await server.connect(transport); + console.error('AdsPower Local Api MCP Server running on stdio'); +} + +main().catch((error) => { + console.error('Fatal error in main():', error); + process.exit(1); +}); diff --git a/src/types/application.ts b/src/types/application.ts new file mode 100644 index 0000000..f97406c --- /dev/null +++ b/src/types/application.ts @@ -0,0 +1,3 @@ +export interface GetApplicationListParams { + size?: number; +} \ No newline at end of file diff --git a/src/types/browser.ts b/src/types/browser.ts new file mode 100644 index 0000000..b32b685 --- /dev/null +++ b/src/types/browser.ts @@ -0,0 +1,89 @@ +import { z } from 'zod'; +import { schemas } from './schemas.js'; + +// 代理配置的 Schema +export const userProxyConfigSchema = z.object({ + proxy_soft: z.enum([ + 'brightdata', 'brightauto', 'oxylabsauto', '922S5auto', + 'ipideeauto', 'ipfoxyauto', '922S5auth', 'kookauto', + 'ssh', 'other', 'no_proxy' + ]).describe('The proxy soft of the browser'), + proxy_type: z.enum(['http', 'https', 'socks5', 'no_proxy']).optional(), + proxy_host: z.string().optional(), + proxy_port: z.string().optional(), + proxy_user: z.string().optional(), + proxy_password: z.string().optional(), + proxy_url: z.string().optional(), + global_config: z.enum(['0', '1']).optional() +}).describe('The user proxy config of the browser'); + +// 浏览器内核配置的 Schema +export const browserKernelConfigSchema = z.object({ + version: z.union([ + z.literal("92"), z.literal("99"), z.literal("102"), + z.literal("105"), z.literal("108"), z.literal("110"), + z.literal("113"), z.literal("116"), z.literal("120"), + z.literal("126"), z.literal("130"), z.literal("134"), + z.literal("ua_auto") + ]).optional(), + type: z.enum(['chrome', 'firefox']).optional() +}).optional(); + +// 随机 UA 配置的 Schema +export const randomUaConfigSchema = z.object({ + ua_version: z.array(z.string()).optional(), + ua_system_version: z.array( + z.enum([ + 'Android 9', 'Android 10', 'Android 11', 'Android 12', 'Android 13', + 'iOS 14', 'iOS 15', + 'Windows 7', 'Windows 8', 'Windows 10', 'Windows 11', + 'Mac OS X 10', 'Mac OS X 11', 'Mac OS X 12', 'Mac OS X 13', + 'Linux' + ]) + ).optional() +}).optional(); + +// 指纹配置的 Schema +export const fingerprintConfigSchema = z.object({ + automatic_timezone: z.enum(['0', '1']).optional(), + timezone: z.string().optional(), + language: z.array(z.string()).optional(), + flash: z.string().optional(), + fonts: z.array(z.string()).optional(), + webrtc: z.enum(['disabled', 'forward', 'proxy', 'local']).optional(), + browser_kernel_config: browserKernelConfigSchema, + random_ua: randomUaConfigSchema, + tls_switch: z.enum(['0', '1']).optional(), + tls: z.string().optional() +}).optional(); + +// 创建浏览器的参数 Schema +export const createBrowserParamsSchema = z.object({ + domainName: z.string().optional(), + openUrls: z.array(z.string()).optional(), + cookie: z.string().optional(), + username: z.string().optional(), + password: z.string().optional(), + system: z.string().optional(), + groupId: z.string(), + name: z.string().optional(), + country: z.string().optional(), + sysAppCateId: z.string().optional(), + userProxyConfig: userProxyConfigSchema, + fingerprintConfig: fingerprintConfigSchema, + storageStrategy: z.number().optional() +}); + +// 更新浏览器的参数 Schema +export const updateBrowserParamsSchema = createBrowserParamsSchema.extend({ + userId: z.string().describe('The user id of the browser to update'), +}); + +// 从已有的 schema 中导出类型 +export type CreateBrowserParams = z.infer; +export type UpdateBrowserParams = z.infer; +export type OpenBrowserParams = z.infer; +export type CloseBrowserParams = z.infer; +export type DeleteBrowserParams = z.infer; +export type GetBrowserListParams = z.infer; +export type MoveBrowserParams = z.infer; \ No newline at end of file diff --git a/src/types/group.ts b/src/types/group.ts new file mode 100644 index 0000000..87bc7f6 --- /dev/null +++ b/src/types/group.ts @@ -0,0 +1,15 @@ +export interface CreateGroupParams { + groupName: string; + remark?: string; +} + +export interface UpdateGroupParams { + groupId: string; + groupName: string; + remark?: string | null; +} + +export interface GetGroupListParams { + name?: string; + size?: number; +} \ No newline at end of file diff --git a/src/types/handlerWrapper.ts b/src/types/handlerWrapper.ts new file mode 100644 index 0000000..662aee4 --- /dev/null +++ b/src/types/handlerWrapper.ts @@ -0,0 +1,20 @@ +export function wrapHandler(handler: Function) { + return async (params: any) => { + try { + const result = await handler(params); + return { + content: [{ + type: 'text', + text: result + }] + }; + } catch (error) { + return { + content: [{ + type: 'text', + text: error instanceof Error ? error.message : String(error) + }] + }; + } + }; +} \ No newline at end of file diff --git a/src/types/schemas.ts b/src/types/schemas.ts new file mode 100644 index 0000000..03d4a1c --- /dev/null +++ b/src/types/schemas.ts @@ -0,0 +1,167 @@ +import { z } from 'zod'; + +// Proxy Config Schema +const userProxyConfigSchema = z.object({ + proxy_soft: z.enum([ + 'brightdata', 'brightauto', 'oxylabsauto', '922S5auto', + 'ipideeauto', 'ipfoxyauto', '922S5auth', 'kookauto', + 'ssh', 'other', 'no_proxy' + ]).describe('The proxy soft of the browser'), + proxy_type: z.enum(['http', 'https', 'socks5', 'no_proxy']).optional(), + proxy_host: z.string().optional(), + proxy_port: z.string().optional(), + proxy_user: z.string().optional(), + proxy_password: z.string().optional(), + proxy_url: z.string().optional(), + global_config: z.enum(['0', '1']).optional() +}).describe('The user proxy config of the browser'); + +// Browser Kernel Config Schema +const browserKernelConfigSchema = z.object({ + version: z.union([ + z.literal("92"), z.literal("99"), z.literal("102"), + z.literal("105"), z.literal("108"), z.literal("110"), + z.literal("113"), z.literal("116"), z.literal("120"), + z.literal("126"), z.literal("130"), z.literal("134"), + z.literal("ua_auto") + ]).optional(), + type: z.enum(['chrome', 'firefox']).optional() +}).optional(); + +// Random UA Config Schema +const randomUaConfigSchema = z.object({ + ua_version: z.array(z.string()).optional(), + ua_system_version: z.array( + z.enum([ + 'Android 9', 'Android 10', 'Android 11', 'Android 12', 'Android 13', + 'iOS 14', 'iOS 15', + 'Windows 7', 'Windows 8', 'Windows 10', 'Windows 11', + 'Mac OS X 10', 'Mac OS X 11', 'Mac OS X 12', 'Mac OS X 13', + 'Linux' + ]) + ).optional() +}).optional(); + +// Fingerprint Config Schema +const fingerprintConfigSchema = z.object({ + automatic_timezone: z.enum(['0', '1']).optional(), + timezone: z.string().optional(), + language: z.array(z.string()).optional(), + flash: z.string().optional(), + fonts: z.array(z.string()).optional(), + webrtc: z.enum(['disabled', 'forward', 'proxy', 'local']).optional(), + browser_kernel_config: browserKernelConfigSchema, + random_ua: randomUaConfigSchema, + tls_switch: z.enum(['0', '1']).optional(), + tls: z.string().optional() +}).optional(); + +export const schemas = { + // Browser Related Schema + createBrowserSchema: z.object({ + domainName: z.string().optional(), + openUrls: z.array(z.string()).optional(), + cookie: z.string().optional(), + username: z.string().optional(), + password: z.string().optional(), + system: z.string().optional(), + groupId: z.string(), + name: z.string().optional(), + country: z.string().optional(), + sysAppCateId: z.string().optional(), + userProxyConfig: userProxyConfigSchema, + fingerprintConfig: fingerprintConfigSchema, + storageStrategy: z.number().optional() + }), + + updateBrowserSchema: z.object({ + domainName: z.string().optional(), + openUrls: z.array(z.string()).optional(), + cookie: z.string().optional(), + username: z.string().optional(), + password: z.string().optional(), + system: z.string().optional(), + groupId: z.string(), + name: z.string().optional(), + country: z.string().optional(), + sysAppCateId: z.string().optional(), + userProxyConfig: userProxyConfigSchema, + fingerprintConfig: fingerprintConfigSchema, + storageStrategy: z.number().optional(), + userId: z.string().describe('The user id of the browser to update') + }), + + openBrowserSchema: z.object({ + serialNumber: z.string().optional().describe('The serial number of the browser to open'), + userId: z.string().optional().describe('The browser id of the browser to open') + }).strict(), + + closeBrowserSchema: z.object({ + userId: z.string().describe('The browser id of the browser to stop') + }).strict(), + + deleteBrowserSchema: z.object({ + userIds: z.array(z.string()).describe('The user ids of the browsers to delete') + }).strict(), + + getBrowserListSchema: z.object({ + groupId: z.string().optional().describe('The group id of the browser'), + size: z.number().optional().describe('The size of the page'), + id: z.string().optional().describe('The id of the browser'), + serialNumber: z.string().optional().describe('The serial number of the browser'), + sort: z.enum(['serial_number', 'last_open_time', 'created_time']).optional() + .describe('The sort of the browser'), + order: z.enum(['asc', 'desc']).optional() + .describe('The order of the browser') + }).strict(), + + moveBrowserSchema: z.object({ + groupId: z.string().describe('The target group id'), + userIds: z.array(z.string()).describe('The browser ids to move') + }).strict(), + + // Group Related Schema + createGroupSchema: z.object({ + groupName: z.string().describe('The name of the group to create'), + remark: z.string().optional().describe('The remark of the group') + }).strict(), + + updateGroupSchema: z.object({ + groupId: z.string().describe('The id of the group to update'), + groupName: z.string().describe('The new name of the group'), + remark: z.string().nullable().optional().describe('The new remark of the group') + }).strict(), + + getGroupListSchema: z.object({ + name: z.string().optional().describe('The name of the group'), + size: z.number().optional().describe('The size of the page') + }).strict(), + + // Application Related Schema + getApplicationListSchema: z.object({ + size: z.number().optional().describe('The size of the page') + }).strict(), + + // Empty Schema + emptySchema: z.object({}).strict() +}; + +export type { + CreateBrowserParams, + UpdateBrowserParams, + OpenBrowserParams, + CloseBrowserParams, + DeleteBrowserParams, + GetBrowserListParams, + MoveBrowserParams +} from './browser.js'; + +export type { + CreateGroupParams, + UpdateGroupParams, + GetGroupListParams +} from './group.js'; + +export type { + GetApplicationListParams +} from './application.js'; \ No newline at end of file diff --git a/src/utils/handlerWrapper.ts b/src/utils/handlerWrapper.ts new file mode 100644 index 0000000..6f03f61 --- /dev/null +++ b/src/utils/handlerWrapper.ts @@ -0,0 +1,22 @@ +import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js'; + +export function wrapHandler(handler: Function) { + return async (params: any): Promise => { + try { + const result = await handler(params); + return { + content: [{ + type: 'text' as const, + text: result + }] + }; + } catch (error) { + return { + content: [{ + type: 'text' as const, + text: error instanceof Error ? error.message : String(error) + }] + }; + } + }; +} \ No newline at end of file diff --git a/src/utils/requestBuilder.ts b/src/utils/requestBuilder.ts new file mode 100644 index 0000000..cf82cba --- /dev/null +++ b/src/utils/requestBuilder.ts @@ -0,0 +1,64 @@ +import { CreateBrowserParams, UpdateBrowserParams } from '../types/browser.js'; + +export function buildRequestBody(params: CreateBrowserParams | UpdateBrowserParams): Record { + const requestBody: Record = {}; + + const basicFields: Record = { + domainName: 'domain_name', + openUrls: 'open_urls', + cookie: 'cookie', + username: 'username', + password: 'password', + system: 'system', + groupId: 'group_id', + name: 'name', + country: 'country', + sysAppCateId: 'sys_app_cate_id', + userId: 'user_id' + }; + Object.entries(basicFields).forEach(([paramKey, key]) => { + const value = params[paramKey as keyof typeof params]; + if (value !== undefined) { + requestBody[key] = value; + } + }); + + if (params.userProxyConfig) { + const proxyConfig = buildNestedConfig(params.userProxyConfig); + if (Object.keys(proxyConfig).length > 0) { + requestBody.user_proxy_config = proxyConfig; + } + } + + if (params.fingerprintConfig) { + const fpConfig = buildNestedConfig(params.fingerprintConfig); + if (Object.keys(fpConfig).length > 0) { + requestBody.fingerprint_config = fpConfig; + } + } + + if (params.storageStrategy !== undefined) { + requestBody.storage_strategy = params.storageStrategy; + } + + return requestBody; +} + +function buildNestedConfig(config: Record): Record { + const result: Record = {}; + + Object.entries(config).forEach(([key, value]) => { + if (value !== undefined) { + if (typeof value === 'object' && value !== null) { + const nestedConfig = buildNestedConfig(value); + if (Object.keys(nestedConfig).length > 0) { + result[key] = nestedConfig; + } + } else { + result[key] = value; + } + } + }); + + return result; +} \ No newline at end of file diff --git a/src/utils/toolRegister.ts b/src/utils/toolRegister.ts new file mode 100644 index 0000000..dd9627d --- /dev/null +++ b/src/utils/toolRegister.ts @@ -0,0 +1,47 @@ +import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; +import { browserHandlers } from '../handlers/browser.js'; +import { groupHandlers } from '../handlers/group.js'; +import { applicationHandlers } from '../handlers/application.js'; +import { schemas } from '../types/schemas.js'; +import { wrapHandler } from './handlerWrapper.js'; + +export function registerTools(server: McpServer) { + // Browser tools + server.tool('open-browser', 'Open the browser', schemas.openBrowserSchema.shape, + wrapHandler(browserHandlers.openBrowser)); + + server.tool('close-browser', 'Close the browser', schemas.closeBrowserSchema.shape, + wrapHandler(browserHandlers.closeBrowser)); + + server.tool('create-browser', 'Create a browser', schemas.createBrowserSchema.shape, + wrapHandler(browserHandlers.createBrowser)); + + server.tool('update-browser', 'Update the browser', schemas.updateBrowserSchema.shape, + wrapHandler(browserHandlers.updateBrowser)); + + server.tool('delete-browser', 'Delete the browser', schemas.deleteBrowserSchema.shape, + wrapHandler(browserHandlers.deleteBrowser)); + + server.tool('get-browser-list', 'Get the list of browsers', schemas.getBrowserListSchema.shape, + wrapHandler(browserHandlers.getBrowserList)); + + server.tool('get-opened-browser', 'Get the list of opened browsers', schemas.emptySchema.shape, + wrapHandler(browserHandlers.getOpenedBrowser)); + + server.tool('move-browser', 'Move browsers to a group', schemas.moveBrowserSchema.shape, + wrapHandler(browserHandlers.moveBrowser)); + + // Group tools + server.tool('create-group', 'Create a browser group', schemas.createGroupSchema.shape, + wrapHandler(groupHandlers.createGroup)); + + server.tool('update-group', 'Update the browser group', schemas.updateGroupSchema.shape, + wrapHandler(groupHandlers.updateGroup)); + + server.tool('get-group-list', 'Get the list of groups', schemas.getGroupListSchema.shape, + wrapHandler(groupHandlers.getGroupList)); + + // Application tools + server.tool('get-application-list', 'Get the list of applications', schemas.getApplicationListSchema.shape, + wrapHandler(applicationHandlers.getApplicationList)); +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..c7439e4 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "Node16", + "moduleResolution": "Node16", + "outDir": "./build", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +}