#
tokens: 15791/50000 21/21 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .gitignore
├── assets
│   ├── claude-installed.png
│   ├── claude-open.gif
│   ├── claude-use.png
│   └── cursor.png
├── Dockerfile
├── LICENSE
├── package.json
├── pnpm-lock.yaml
├── README.MD
├── smithery.yaml
├── src
│   ├── constants
│   │   └── api.ts
│   ├── handlers
│   │   ├── application.ts
│   │   ├── automation.ts
│   │   ├── browser.ts
│   │   └── group.ts
│   ├── index.ts
│   ├── types
│   │   ├── application.ts
│   │   ├── browser.ts
│   │   ├── group.ts
│   │   ├── handlerWrapper.ts
│   │   └── schemas.ts
│   └── utils
│       ├── browserBase.ts
│       ├── handlerWrapper.ts
│       ├── requestBuilder.ts
│       └── toolRegister.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Dependencies
 2 | node_modules/
 3 | npm-debug.log*
 4 | yarn-debug.log*
 5 | yarn-error.log*
 6 | pnpm-debug.log*
 7 | .pnpm-store/
 8 | 
 9 | # Build output
10 | dist/
11 | build/
12 | out/
13 | .output/
14 | 
15 | # IDE and editor files
16 | .idea/
17 | .vscode/
18 | *.swp
19 | *.swo
20 | .DS_Store
21 | *.sublime-workspace
22 | *.sublime-project
23 | 
24 | # Environment variables
25 | .env
26 | .env.local
27 | .env.*.local
28 | .env.development
29 | .env.test
30 | .env.production
31 | 
32 | # Cache and temporary files
33 | .cache/
34 | .temp/
35 | .tmp/
36 | coverage/
37 | *.log
38 | .eslintcache
39 | 
40 | # System files
41 | Thumbs.db
42 | .DS_Store
43 | *.pem
44 | 
45 | # Debug
46 | npm-debug.log*
47 | yarn-debug.log*
48 | yarn-error.log*
49 | .pnpm-debug.log*
50 | 
51 | # Local files
52 | *.local
53 | 
54 | # Testing
55 | coverage/
56 | .nyc_output/
57 | 
58 | # Typescript
59 | *.tsbuildinfo 
```

--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------

```markdown
  1 | # AdsPower LocalAPI MCP Server
  2 | 
  3 | A Model Context Protocol server that AdsPower browser LocalAPI. This server enables LLMs to interact with start browser, create browser, update browser fingerprint config ...
  4 | 
  5 | 
  6 | ## Usage with Claude Desktop
  7 | 
  8 | Talk to LLMs to create browser: `Create an Android UA browser using Chrome 134`
  9 | 
 10 | ![Claude desktop](https://github.com/AdsPower/local-api-mcp-typescript/blob/main/assets/claude-use.png)
 11 | 
 12 | Talk to LLMs to create browser: `Help me with random UA, random fingerprint, random cookie generation, create 3 browsers, use 134 cores, and open them`
 13 | 
 14 | ![Claude desktop](https://github.com/AdsPower/local-api-mcp-typescript/blob/main/assets/claude-open.gif)
 15 | 
 16 | ## How to use?
 17 | 
 18 | ### Requirements
 19 | 
 20 | - [AdsPower](https://www.adspower.com/?source=github)
 21 | 
 22 | - Node version 18 or greater
 23 | 
 24 | ### Installation
 25 | 
 26 | To use with Claude Desktop, add the server config:
 27 | 
 28 | On macOS: `~/Library/Application Support/Claude/claude_desktop_config.json` On Windows: `%APPDATA%/Claude/claude_desktop_config.json`
 29 | 
 30 | #### MacOS / Linux
 31 | ```bash
 32 | {
 33 |   "mcpServers": {
 34 |     "adspower-local-api": {
 35 |       "command": "npx",
 36 |       "args": ["-y", "local-api-mcp-typescript"]
 37 |     }
 38 |   }
 39 | }
 40 | ```
 41 | 
 42 | #### Windows
 43 | ```bash
 44 | {
 45 |   "mcpServers": {
 46 |     "adspower-local-api": {
 47 |       "command": "cmd",
 48 |       "args": ["/c", "npx", "-y", "local-api-mcp-typescript"]
 49 |     }
 50 |   }
 51 | }
 52 | ```
 53 | 
 54 | ![Claude desktop MCP](https://github.com/AdsPower/local-api-mcp-typescript/blob/main/assets/claude-installed.png)
 55 | 
 56 | or use in Cursor
 57 | ![Cursor](./assets/cursor.png)
 58 | 
 59 | ## Development
 60 | 
 61 | ```bash
 62 | # git clone 
 63 | git clone https://github.com/AdsPower/local-api-mcp-typescript.git
 64 | 
 65 | # install package
 66 | cd local-api-mcp-typescript && npx pnpm i
 67 | 
 68 | # build
 69 | npm run build
 70 | ```
 71 | 
 72 | ```bash
 73 | # Add the server to your claude_desktop_config.json
 74 | "mcpServers": {
 75 |   "adspower-local-api": {
 76 |         "command": "node",
 77 |         "args": [
 78 |             "<Replace Your Project Path>/local-api-mcp-typescript/build/index.js"
 79 |         ]
 80 |     }
 81 | }
 82 | ```
 83 | 
 84 | ## Components
 85 | 
 86 | ### Tools
 87 | 
 88 | - **open_browser**
 89 |   - Open the browser
 90 |   - Inputs: 
 91 |     - `serialNumber` (string, optional): The serial number of the browser to open
 92 |     - `userId` (string, optional): The browser id of the browser to open
 93 | 
 94 | - **close_browser**
 95 |   - Close the browser
 96 |   - Input: 
 97 |     - `userId` (string): The browser id of the browser to stop
 98 | 
 99 | - **create_browser**
100 |   - Create a browser
101 |   - Inputs:
102 |     - `groupId` (string): The group id for the browser
103 |     - `domainName` (string, optional): The domain name
104 |     - `openUrls` (string[], optional): URLs to open
105 |     - `cookie` (string, optional): Browser cookie
106 |     - `username` (string, optional): Username
107 |     - `password` (string, optional): Password
108 |     - `system` (string, optional): System type
109 |     - `name` (string, optional): Browser name
110 |     - `country` (string, optional): Country
111 |     - `sysAppCateId` (string, optional): System application category id
112 |     - `storageStrategy` (number, optional): Storage strategy
113 |     - `userProxyConfig` (object): Proxy configuration
114 |     - `fingerprintConfig` (object, optional): Browser fingerprint configuration
115 | 
116 | - **update_browser**
117 |   - Update the browser
118 |   - Inputs: Same as create_browser, plus:
119 |     - `userId` (string): The user id of the browser to update
120 | 
121 | - **delete_browser**
122 |   - Delete the browser
123 |   - Input:
124 |     - `userIds` (string[]): The user ids of the browsers to delete
125 | 
126 | - **get_browser_list**
127 |   - Get the list of browsers
128 |   - Inputs:
129 |     - `groupId` (string, optional): The group id of the browser
130 |     - `size` (number, optional): The size of the page
131 |     - `id` (string, optional): The id of the browser
132 |     - `serialNumber` (string, optional): The serial number of the browser
133 |     - `sort` (enum, optional): Sort field ('serial_number' | 'last_open_time' | 'created_time')
134 |     - `order` (enum, optional): Sort order ('asc' | 'desc')
135 | 
136 | - **get-opened_browser**
137 |   - Get the list of opened browsers
138 |   - No inputs required
139 | 
140 | - **move_browser**
141 |   - Move browsers to a group
142 |   - Inputs:
143 |     - `groupId` (string): The target group id
144 |     - `userIds` (string[]): The browser ids to move
145 | 
146 | - **create_group**
147 |   - Create a browser group
148 |   - Inputs:
149 |     - `groupName` (string): The name of the group to create
150 |     - `remark` (string, optional): The remark of the group
151 | 
152 | - **update_group**
153 |   - Update the browser group
154 |   - Inputs:
155 |     - `groupId` (string): The id of the group to update
156 |     - `groupName` (string): The new name of the group
157 |     - `remark` (string | null, optional): The new remark of the group, set null to clear
158 | 
159 | - **get_group_list**
160 |   - Get the list of groups
161 |   - Inputs:
162 |     - `name` (string, optional): The name of the group
163 |     - `size` (number, optional): The size of the page
164 | 
165 | - **get-application_list**
166 |   - Get the list of applications
167 |   - Input:
168 |     - `size` (number, optional): The size of the page
169 | 
170 | ### Advanced Configuration Types
171 | 
172 | #### UserProxyConfig
173 | - `proxy_soft` (enum): The proxy soft type ('brightdata', 'brightauto', 'oxylabsauto', etc.)
174 | - `proxy_type` (enum, optional): Proxy type ('http', 'https', 'socks5', 'no_proxy')
175 | - `proxy_host` (string, optional): Proxy host
176 | - `proxy_port` (string, optional): Proxy port
177 | - `proxy_user` (string, optional): Proxy username
178 | - `proxy_password` (string, optional): Proxy password
179 | - `proxy_url` (string, optional): Proxy URL
180 | - `global_config` (enum, optional): Global config ('0' | '1')
181 | 
182 | #### FingerprintConfig
183 | - `automatic_timezone` (enum, optional): Automatic timezone ('0' | '1')
184 | - `timezone` (string, optional): Timezone
185 | - `language` (string[], optional): Languages
186 | - `flash` (string, optional): Flash version
187 | - `fonts` (string[], optional): Font list
188 | - `webrtc` (enum, optional): WebRTC setting ('disabled' | 'forward' | 'proxy' | 'local')
189 | - `browser_kernel_config` (object, optional):
190 |   - `version` (string, optional): Browser version
191 |   - `type` (enum, optional): Browser type ('chrome' | 'firefox')
192 | - `random_ua` (object, optional):
193 |   - `ua_version` (string[], optional): User agent versions
194 |   - `ua_system_version` (enum[], optional): System versions
195 | - `tls_switch` (enum, optional): TLS switch ('0' | '1')
196 | - `tls` (string, optional): TLS configuration
```

--------------------------------------------------------------------------------
/src/types/application.ts:
--------------------------------------------------------------------------------

```typescript
1 | export interface GetApplicationListParams {
2 |     size?: number;
3 | } 
```

--------------------------------------------------------------------------------
/src/types/group.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export interface CreateGroupParams {
 2 |     groupName: string;
 3 |     remark?: string;
 4 | }
 5 | 
 6 | export interface UpdateGroupParams {
 7 |     groupId: string;
 8 |     groupName: string;
 9 |     remark?: string | null;
10 | }
11 | 
12 | export interface GetGroupListParams {
13 |     groupName?: string;
14 |     size?: number;
15 |     page?: number;
16 | } 
```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |     "compilerOptions": {
 3 |         "target": "ESNext",
 4 |         "module": "CommonJS",
 5 |         "moduleResolution": "Node",
 6 |         "outDir": "./build",
 7 |         "rootDir": "./src",
 8 |         "strict": true,
 9 |         "esModuleInterop": true,
10 |         "skipLibCheck": true,
11 |         "forceConsistentCasingInFileNames": true
12 |     },
13 |     "include": ["src/**/*"],
14 |     "exclude": ["node_modules"]
15 | }
16 | 
```

--------------------------------------------------------------------------------
/smithery.yaml:
--------------------------------------------------------------------------------

```yaml
 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml
 2 | 
 3 | startCommand:
 4 |   type: stdio
 5 |   configSchema:
 6 |     # JSON Schema defining the configuration options for the MCP.
 7 |     type: object
 8 |     properties: {}
 9 |   commandFunction:
10 |     # A JS function that produces the CLI command based on the given config to start the MCP on stdio.
11 |     |-
12 |     (config) => ({
13 |       command: 'node',
14 |       args: ['build/index.js'],
15 |       env: {}
16 |     })
17 |   exampleConfig: {}
18 | 
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile
 2 | FROM node:lts-alpine
 3 | 
 4 | WORKDIR /app
 5 | 
 6 | # Install dependencies
 7 | COPY package.json pnpm-lock.yaml ./
 8 | 
 9 | # Use npm install if pnpm is not available
10 | RUN npm install --ignore-scripts
11 | 
12 | # Copy remaining source files
13 | COPY tsconfig.json ./
14 | COPY src ./src
15 | 
16 | # Build the project (transpiling TypeScript to JavaScript)
17 | RUN npm run build
18 | 
19 | # Expose no ports as MCP runs via stdio; if needed, expose port here
20 | 
21 | # Set entrypoint
22 | ENTRYPOINT ["node", "build/index.js"]
23 | 
```

--------------------------------------------------------------------------------
/src/types/handlerWrapper.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export function wrapHandler(handler: Function) {
 2 |     return async (params: any) => {
 3 |         try {
 4 |             const result = await handler(params);
 5 |             return {
 6 |                 content: [{
 7 |                     type: 'text',
 8 |                     text: result
 9 |                 }]
10 |             };
11 |         } catch (error) {
12 |             return {
13 |                 content: [{
14 |                     type: 'text',
15 |                     text: error instanceof Error ? error.message : String(error)
16 |                 }]
17 |             };
18 |         }
19 |     };
20 | } 
```

--------------------------------------------------------------------------------
/src/handlers/application.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import axios from 'axios';
 2 | import { LOCAL_API_BASE, API_ENDPOINTS } from '../constants/api.js';
 3 | import type { GetApplicationListParams } from '../types/application.js';
 4 | 
 5 | export const applicationHandlers = {
 6 |     async getApplicationList({ size }: GetApplicationListParams) {
 7 |         const params = new URLSearchParams();
 8 |         if (size) {
 9 |             params.set('page_size', size.toString());
10 |         }
11 | 
12 |         const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.GET_APPLICATION_LIST}`, { params });
13 |         return `Application list: ${JSON.stringify(response.data.data.list, null, 2)}`;
14 |     }
15 | }; 
```

--------------------------------------------------------------------------------
/src/constants/api.ts:
--------------------------------------------------------------------------------

```typescript
 1 | export const LOCAL_API_BASE = 'http://127.0.0.1:50325';
 2 | 
 3 | export const API_ENDPOINTS = {
 4 |     START_BROWSER: '/api/v1/browser/start',
 5 |     CLOSE_BROWSER: '/api/v1/browser/stop',
 6 |     CREATE_BROWSER: '/api/v1/user/create',
 7 |     GET_BROWSER_LIST: '/api/v1/user/list',
 8 |     GET_GROUP_LIST: '/api/v1/group/list',
 9 |     GET_APPLICATION_LIST: '/api/v1/application/list',
10 |     UPDATE_BROWSER: '/api/v1/user/update',
11 |     DELETE_BROWSER: '/api/v1/user/delete',
12 |     GET_OPENED_BROWSER: '/api/v1/browser/local-active',
13 |     CREATE_GROUP: '/api/v1/group/create',
14 |     UPDATE_GROUP: '/api/v1/group/update',
15 |     MOVE_BROWSER: '/api/v1/user/regroup'
16 | } as const; 
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
 1 | {
 2 |   "name": "local-api-mcp-typescript",
 3 |   "version": "1.0.6",
 4 |   "main": "index.js",
 5 |   "type": "commonjs",
 6 |   "bin": {
 7 |     "adspower-local-api-mcp": "./build/index.js"
 8 |   },
 9 |   "scripts": {
10 |     "build": "tsc",
11 |     "test": "echo \"Error: no test specified\" && exit 1"
12 |   },
13 |   "files": [
14 |     "build"
15 |   ],
16 |   "keywords": [
17 |     "adspower",
18 |     "local-api",
19 |     "mcp",
20 |     "typescript"
21 |   ],
22 |   "author": "AdsPower",
23 |   "license": "ISC",
24 |   "description": "",
25 |   "dependencies": {
26 |     "@modelcontextprotocol/sdk": "^1.7.0",
27 |     "axios": "^1.8.4",
28 |     "playwright": "^1.51.1",
29 |     "zod": "^3.24.2"
30 |   },
31 |   "devDependencies": {
32 |     "@types/node": "^22.13.13",
33 |     "typescript": "^5.8.2"
34 |   },
35 |   "engines": {
36 |     "node": ">=18.0.0"
37 |   },
38 |   "repository": {
39 |     "type": "git",
40 |     "url": "https://github.com/AdsPower/local-api-mcp-typescript.git"
41 |   },
42 |   "bugs": {
43 |     "url": "https://github.com/AdsPower/local-api-mcp-typescript/issues"
44 |   },
45 |   "homepage": "https://github.com/AdsPower/local-api-mcp-typescript#readme"
46 | }
47 | 
```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
 1 | #!/usr/bin/env node
 2 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
 3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
 4 | import { registerTools } from './utils/toolRegister.js';
 5 | // import { getScreenshot } from './handlers/automation.js';
 6 | // Create server instance
 7 | const server = new McpServer({
 8 |     name: 'adspower-local-api',
 9 |     version: '1.0.6',
10 |     capabilities: {
11 |         resources: {},
12 |         tools: {},
13 |     },
14 | });
15 | 
16 | // Register all tools
17 | registerTools(server);
18 | 
19 | // Resources
20 | // server.resource('get-screenshot', 'Get the screenshot of the page', async (uri, extra) => {
21 | //     const filename = uri.toString().split("://")[1];
22 | //     const screenshot = getScreenshot(filename);
23 | //     if (!screenshot) {
24 | //         return {
25 | //             contents: [],
26 | //             mimeType: 'text/plain',
27 | //         };
28 | //     }
29 | //     return {
30 | //         contents: [{
31 | //             uri: filename,
32 | //             blob: screenshot,
33 | //             mimeType: 'image/png',
34 | //         }],
35 | //     };
36 | // });
37 | 
38 | async function main() {
39 |     const transport = new StdioServerTransport();
40 |     await server.connect(transport);
41 |     console.error('AdsPower Local Api MCP Server running on stdio');
42 | }
43 | 
44 | main().catch((error) => {
45 |     console.error('Fatal error in main():', error);
46 |     process.exit(1);
47 | });
48 | 
```

--------------------------------------------------------------------------------
/src/utils/browserBase.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { Browser, Page, chromium } from 'playwright';
 2 | 
 3 | class BrowserBase {
 4 |     private browser: Browser | null;
 5 |     private page: Page | null;
 6 |     private screenshots: Map<string, string>;
 7 | 
 8 |     constructor() {
 9 |         this.browser = null;
10 |         this.page = null;
11 |         this.screenshots = new Map();
12 |     }
13 | 
14 |     get browserInstance() {
15 |         return this.browser;
16 |     }
17 | 
18 |     get pageInstance() {
19 |         return this.page;
20 |     }
21 | 
22 |     set pageInstance(page: Page | null) {
23 |         this.page = page;
24 |     }
25 | 
26 |     get screenshotsInstance() {
27 |         return this.screenshots;
28 |     }
29 | 
30 |     checkConnected() {
31 |         const error = new Error('Browser not connected, please connect browser first');
32 |         if (!this.browser) {
33 |             throw error;
34 |         }
35 |         if (!this.browser.isConnected()) {
36 |             throw error;
37 |         }
38 |         if (!this.page) {
39 |             throw error;
40 |         }
41 |     }
42 |     
43 |     async connectBrowserWithWs(wsUrl: string) {
44 |         this.browser = await chromium.connectOverCDP(wsUrl);
45 |         const defaultContext = this.browser.contexts()[0];
46 |         this.page = defaultContext.pages()[0];
47 |         await this.page.bringToFront().catch((error) => {
48 |             console.error('Failed to bring page to front', error);
49 |         });
50 |     }
51 | 
52 |     async resetBrowser() {
53 |         this.browser = null;
54 |         this.page = null;
55 |     }
56 | }
57 | 
58 | export default new BrowserBase();
59 | 
```

--------------------------------------------------------------------------------
/src/utils/handlerWrapper.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import type { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
 2 | import browser from './browserBase.js';
 3 | 
 4 | export function wrapHandler(handler: Function) {
 5 |     return async (params: any): Promise<CallToolResult> => {
 6 |         try {
 7 |             const content = await handler(params);
 8 |             if (typeof content === 'string') {
 9 |                 return {
10 |                     content: [{
11 |                         type: 'text' as const,
12 |                         text: content
13 |                     }]
14 |                 };
15 |             }
16 |             return {
17 |                 content
18 |             };
19 |         } catch (error) {
20 |             let errorMessage = error instanceof Error ? error.message : String(error);
21 |             if (
22 |                 errorMessage.includes("Target page, context or browser has been closed") ||
23 |                 errorMessage.includes("Target closed") ||
24 |                 errorMessage.includes("Browser has been disconnected") ||
25 |                 errorMessage.includes("Protocol error") ||
26 |                 errorMessage.includes("Connection closed")
27 |             ) {
28 |                 await browser.resetBrowser();
29 |                 errorMessage = `Browser connection error: ${errorMessage}. Connection has been reset - please retry the operation.`;
30 |             }
31 |             return {
32 |                 content: [{
33 |                     type: 'text' as const,
34 |                     text: errorMessage
35 |                 }]
36 |             };
37 |         }
38 |     };
39 | } 
```

--------------------------------------------------------------------------------
/src/types/browser.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { z } from 'zod';
 2 | import { schemas } from './schemas.js';
 3 | 
 4 | // 从已有的 schema 中导出类型
 5 | export type CreateBrowserParams = z.infer<typeof schemas.createBrowserSchema>;
 6 | export type UpdateBrowserParams = z.infer<typeof schemas.updateBrowserSchema>;
 7 | export type OpenBrowserParams = z.infer<typeof schemas.openBrowserSchema>;
 8 | export type CloseBrowserParams = z.infer<typeof schemas.closeBrowserSchema>;
 9 | export type DeleteBrowserParams = z.infer<typeof schemas.deleteBrowserSchema>;
10 | export type GetBrowserListParams = z.infer<typeof schemas.getBrowserListSchema>;
11 | export type MoveBrowserParams = z.infer<typeof schemas.moveBrowserSchema>;
12 | export type CreateAutomationParams = z.infer<typeof schemas.createAutomationSchema>;
13 | export type NavigateParams = z.infer<typeof schemas.navigateSchema>;
14 | export type ScreenshotParams = z.infer<typeof schemas.screenshotSchema>;
15 | export type ClickElementParams = z.infer<typeof schemas.clickElementSchema>;
16 | export type FillInputParams = z.infer<typeof schemas.fillInputSchema>;
17 | export type SelectOptionParams = z.infer<typeof schemas.selectOptionSchema>;
18 | export type HoverElementParams = z.infer<typeof schemas.hoverElementSchema>;
19 | export type ScrollElementParams = z.infer<typeof schemas.scrollElementSchema>;
20 | export type PressKeyParams = z.infer<typeof schemas.pressKeySchema>;
21 | export type EvaluateScriptParams = z.infer<typeof schemas.evaluateScriptSchema>;
22 | export type DragElementParams = z.infer<typeof schemas.dragElementSchema>;
23 | export type IframeClickElementParams = z.infer<typeof schemas.iframeClickElementSchema>;
```

--------------------------------------------------------------------------------
/src/utils/requestBuilder.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { CreateBrowserParams, UpdateBrowserParams } from '../types/browser.js';
 2 | 
 3 | export function buildRequestBody(params: CreateBrowserParams | UpdateBrowserParams): Record<string, any> {
 4 |     const requestBody: Record<string, any> = {};
 5 | 
 6 |     const basicFields: Record<string, string> = {
 7 |         domainName: 'domain_name',
 8 |         openUrls: 'open_urls',
 9 |         cookie: 'cookie',
10 |         username: 'username',
11 |         password: 'password',
12 |         groupId: 'group_id',
13 |         name: 'name',
14 |         country: 'country',
15 |         sysAppCateId: 'sys_app_cate_id',
16 |         userId: 'user_id'
17 |     };
18 |     Object.entries(basicFields).forEach(([paramKey, key]) => {
19 |         const value = params[paramKey as keyof typeof params];
20 |         if (value !== undefined) {
21 |             requestBody[key] = value;
22 |         }
23 |     });
24 | 
25 |     if (params.userProxyConfig) {
26 |         const proxyConfig = buildNestedConfig(params.userProxyConfig);
27 |         if (Object.keys(proxyConfig).length > 0) {
28 |             requestBody.user_proxy_config = proxyConfig;
29 |         }
30 |     }
31 | 
32 |     if (params.fingerprintConfig) {
33 |         const fpConfig = buildNestedConfig(params.fingerprintConfig);
34 |         if (Object.keys(fpConfig).length > 0) {
35 |             requestBody.fingerprint_config = fpConfig;
36 |         }
37 |     }
38 | 
39 |     if (params.storageStrategy !== undefined) {
40 |         requestBody.storage_strategy = params.storageStrategy;
41 |     }
42 | 
43 |     return requestBody;
44 | }
45 | 
46 | function buildNestedConfig(config: Record<string, any>): Record<string, any> {
47 |     const result: Record<string, any> = {};
48 | 
49 |     Object.entries(config).forEach(([key, value]) => {
50 |         if (value !== undefined) {
51 |             if (typeof value === 'object' && value !== null) {
52 |                 const nestedConfig = buildNestedConfig(value);
53 |                 if (Object.keys(nestedConfig).length > 0) {
54 |                     result[key] = nestedConfig;
55 |                 }
56 |             } else {
57 |                 result[key] = value;
58 |             }
59 |         }
60 |     });
61 | 
62 |     return result;
63 | } 
```

--------------------------------------------------------------------------------
/src/handlers/group.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import axios from 'axios';
 2 | import { LOCAL_API_BASE, API_ENDPOINTS } from '../constants/api.js';
 3 | import type {
 4 |     CreateGroupParams,
 5 |     UpdateGroupParams,
 6 |     GetGroupListParams
 7 | } from '../types/group.js';
 8 | 
 9 | export const groupHandlers = {
10 |     async createGroup({ groupName, remark }: CreateGroupParams) {
11 |         const requestBody: Record<string, string> = {
12 |             group_name: groupName
13 |         };
14 |         
15 |         if (remark !== undefined) {
16 |             requestBody.remark = remark;
17 |         }
18 | 
19 |         const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.CREATE_GROUP}`, requestBody);
20 |         
21 |         if (response.data.code === 0) {
22 |             return `Group created successfully with name: ${groupName}${remark ? `, remark: ${remark}` : ''}`;
23 |         }
24 |         throw new Error(`Failed to create group: ${response.data.msg}`);
25 |     },
26 | 
27 |     async updateGroup({ groupId, groupName, remark }: UpdateGroupParams) {
28 |         const requestBody: Record<string, any> = {
29 |             group_id: groupId,
30 |             group_name: groupName
31 |         };
32 |         
33 |         if (remark !== undefined) {
34 |             requestBody.remark = remark;
35 |         }
36 | 
37 |         const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.UPDATE_GROUP}`, requestBody);
38 |         
39 |         if (response.data.code === 0) {
40 |             return `Group updated successfully with id: ${groupId}, name: ${groupName}${remark !== undefined ? `, remark: ${remark === null ? '(cleared)' : remark}` : ''}`;
41 |         }
42 |         throw new Error(`Failed to update group: ${response.data.msg}`);
43 |     },
44 | 
45 |     async getGroupList({ groupName, size, page }: GetGroupListParams) {
46 |         const params = new URLSearchParams();
47 |         if (groupName) {
48 |             params.set('group_name', groupName);
49 |         }
50 |         if (size) {
51 |             params.set('page_size', size.toString());
52 |         }
53 |         if (page) {
54 |             params.set('page', page.toString());
55 |         }
56 | 
57 |         const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.GET_GROUP_LIST}`, { params });
58 |         return `Group list: ${JSON.stringify(response.data.data.list, null, 2)}`;
59 |     }
60 | }; 
```

--------------------------------------------------------------------------------
/src/utils/toolRegister.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
 2 | import { browserHandlers } from '../handlers/browser.js';
 3 | import { groupHandlers } from '../handlers/group.js';
 4 | import { applicationHandlers } from '../handlers/application.js';
 5 | import { schemas } from '../types/schemas.js';
 6 | import { wrapHandler } from './handlerWrapper.js';
 7 | import { automationHandlers } from '../handlers/automation.js';
 8 | 
 9 | export function registerTools(server: McpServer) {
10 |     // Browser tools
11 |     server.tool('open-browser', 'Open the browser, both environment and profile mean browser', schemas.openBrowserSchema.shape, 
12 |         wrapHandler(browserHandlers.openBrowser));
13 |     
14 |     server.tool('close-browser', 'Close the browser', schemas.closeBrowserSchema.shape,
15 |         wrapHandler(browserHandlers.closeBrowser));
16 |     
17 |     server.tool('create-browser', 'Create a browser', schemas.createBrowserSchema.shape,
18 |         wrapHandler(browserHandlers.createBrowser));
19 |     
20 |     server.tool('update-browser', 'Update the browser', schemas.updateBrowserSchema.shape,
21 |         wrapHandler(browserHandlers.updateBrowser));
22 |     
23 |     server.tool('delete-browser', 'Delete the browser', schemas.deleteBrowserSchema.shape,
24 |         wrapHandler(browserHandlers.deleteBrowser));
25 |     
26 |     server.tool('get-browser-list', 'Get the list of browsers', schemas.getBrowserListSchema.shape,
27 |         wrapHandler(browserHandlers.getBrowserList));
28 |     
29 |     server.tool('get-opened-browser', 'Get the list of opened browsers', schemas.emptySchema.shape,
30 |         wrapHandler(browserHandlers.getOpenedBrowser));
31 |     
32 |     server.tool('move-browser', 'Move browsers to a group', schemas.moveBrowserSchema.shape,
33 |         wrapHandler(browserHandlers.moveBrowser));
34 | 
35 |     // Group tools
36 |     server.tool('create-group', 'Create a browser group', schemas.createGroupSchema.shape,
37 |         wrapHandler(groupHandlers.createGroup));
38 |     
39 |     server.tool('update-group', 'Update the browser group', schemas.updateGroupSchema.shape,
40 |         wrapHandler(groupHandlers.updateGroup));
41 |     
42 |     server.tool('get-group-list', 'Get the list of groups', schemas.getGroupListSchema.shape,
43 |         wrapHandler(groupHandlers.getGroupList));
44 | 
45 |     // Application tools
46 |     server.tool('get-application-list', 'Get the list of applications', schemas.getApplicationListSchema.shape,
47 |         wrapHandler(applicationHandlers.getApplicationList));
48 |     
49 |     // Automation tools
50 |     server.tool('connect-browser-with-ws', 'Connect the browser with the ws url', schemas.createAutomationSchema.shape,
51 |         wrapHandler(automationHandlers.connectBrowserWithWs));
52 |     
53 |     server.tool('open-new-page', 'Open a new page', schemas.emptySchema.shape,
54 |         wrapHandler(automationHandlers.openNewPage));
55 |         
56 |     server.tool('navigate', 'Navigate to the url', schemas.navigateSchema.shape,
57 |         wrapHandler(automationHandlers.navigate));
58 | 
59 |     server.tool('screenshot', 'Get the screenshot of the page', schemas.screenshotSchema.shape,
60 |         wrapHandler(automationHandlers.screenshot));
61 | 
62 |     server.tool('get-page-visible-text', 'Get the visible text content of the page', schemas.emptySchema.shape,
63 |         wrapHandler(automationHandlers.getPageVisibleText));
64 | 
65 |     server.tool('get-page-html', 'Get the html content of the page', schemas.emptySchema.shape,
66 |         wrapHandler(automationHandlers.getPageHtml));
67 | 
68 |     server.tool('click-element', 'Click the element', schemas.clickElementSchema.shape,
69 |         wrapHandler(automationHandlers.clickElement));
70 | 
71 |     server.tool('fill-input', 'Fill the input', schemas.fillInputSchema.shape,
72 |         wrapHandler(automationHandlers.fillInput));
73 | 
74 |     server.tool('select-option', 'Select the option', schemas.selectOptionSchema.shape,
75 |         wrapHandler(automationHandlers.selectOption));
76 | 
77 |     server.tool('hover-element', 'Hover the element', schemas.hoverElementSchema.shape,
78 |         wrapHandler(automationHandlers.hoverElement));
79 | 
80 |     server.tool('scroll-element', 'Scroll the element', schemas.scrollElementSchema.shape,
81 |         wrapHandler(automationHandlers.scrollElement));
82 | 
83 |     server.tool('press-key', 'Press the key', schemas.pressKeySchema.shape,
84 |         wrapHandler(automationHandlers.pressKey));
85 | 
86 |     server.tool('evaluate-script', 'Evaluate the script', schemas.evaluateScriptSchema.shape,
87 |         wrapHandler(automationHandlers.evaluateScript));
88 | 
89 |     server.tool('drag-element', 'Drag the element', schemas.dragElementSchema.shape,
90 |         wrapHandler(automationHandlers.dragElement));
91 | 
92 |     server.tool('iframe-click-element', 'Click the element in the iframe', schemas.iframeClickElementSchema.shape,
93 |         wrapHandler(automationHandlers.iframeClickElement));
94 | } 
```

--------------------------------------------------------------------------------
/src/handlers/browser.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import axios from 'axios';
  2 | import { LOCAL_API_BASE, API_ENDPOINTS } from '../constants/api.js';
  3 | import { buildRequestBody } from '../utils/requestBuilder.js';
  4 | import type { 
  5 |     OpenBrowserParams,
  6 |     CloseBrowserParams,
  7 |     CreateBrowserParams,
  8 |     UpdateBrowserParams,
  9 |     DeleteBrowserParams,
 10 |     GetBrowserListParams,
 11 |     MoveBrowserParams
 12 | } from '../types/browser.js';
 13 | 
 14 | export const browserHandlers = {
 15 |     async openBrowser({ serialNumber, userId, ipTab, launchArgs, clearCacheAfterClosing, cdpMask }: OpenBrowserParams) {
 16 |         const params = new URLSearchParams();
 17 |         if (serialNumber) {
 18 |             params.set('serial_number', serialNumber);
 19 |         }
 20 |         if (userId) {
 21 |             params.set('user_id', userId);
 22 |         }
 23 |         if (ipTab) {
 24 |             params.set('open_tabs', ipTab);
 25 |         }
 26 |         if (launchArgs) {
 27 |             params.set('launch_args', launchArgs);
 28 |         }
 29 |         if (clearCacheAfterClosing) {
 30 |             params.set('clear_cache_after_closing', clearCacheAfterClosing);
 31 |         }
 32 |         if (cdpMask) {
 33 |             params.set('cdp_mask', cdpMask);
 34 |         }
 35 |         params.set('open_tabs', '0');
 36 | 
 37 |         const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.START_BROWSER}`, { params });
 38 |         if (response.data.code === 0) {
 39 |             return `Browser opened successfully with: ${Object.entries(response.data.data).map(([key, value]) => {
 40 |                 if (value && typeof value === 'object') {
 41 |                     return Object.entries(value).map(([key, value]) => `ws.${key}: ${value}`).join('\n');
 42 |                 }
 43 |                 return `${key}: ${value}`;
 44 |             }).join('\n')}`;
 45 |         }
 46 |         throw new Error(`Failed to open browser: ${response.data.msg}`);
 47 |     },
 48 | 
 49 |     async closeBrowser({ userId }: CloseBrowserParams) {
 50 |         const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.CLOSE_BROWSER}`, {
 51 |             params: { user_id: userId }
 52 |         });
 53 |         return 'Browser closed successfully';
 54 |     },
 55 | 
 56 |     async createBrowser(params: CreateBrowserParams) {
 57 |         const requestBody = buildRequestBody(params);
 58 |         const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.CREATE_BROWSER}`, requestBody);
 59 |         
 60 |         if (response.data.code === 0) {
 61 |             return `Browser created successfully with: ${Object.entries(response.data.data).map(([key, value]) => `${key}: ${value}`).join('\n')}`;
 62 |         }
 63 |         throw new Error(`Failed to create browser: ${response.data.msg}`);
 64 |     },
 65 | 
 66 |     async updateBrowser(params: UpdateBrowserParams) {
 67 |         const requestBody = buildRequestBody({
 68 |             ...params
 69 |         });
 70 |         requestBody.user_id = params.userId;
 71 | 
 72 |         const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.UPDATE_BROWSER}`, requestBody);
 73 |         
 74 |         if (response.data.code === 0) {
 75 |             return `Browser updated successfully with: ${Object.entries(response.data.data).map(([key, value]) => `${key}: ${value}`).join('\n')}`;
 76 |         }
 77 |         throw new Error(`Failed to update browser: ${response.data.msg}`);
 78 |     },
 79 | 
 80 |     async deleteBrowser({ userIds }: DeleteBrowserParams) {
 81 |         const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.DELETE_BROWSER}`, {
 82 |             user_ids: userIds
 83 |         });
 84 |         
 85 |         if (response.data.code === 0) {
 86 |             return `Browsers deleted successfully: ${userIds.join(', ')}`;
 87 |         }
 88 |         throw new Error(`Failed to delete browsers: ${response.data.msg}`);
 89 |     },
 90 | 
 91 |     async getBrowserList(params: GetBrowserListParams) {
 92 |         const { groupId, size, id, serialNumber, sort, order, page } = params;
 93 |         const urlParams = new URLSearchParams();
 94 |         if (size) {
 95 |             urlParams.set('page_size', size.toString());
 96 |         }
 97 |         if (page) {
 98 |             urlParams.set('page', page.toString());
 99 |         }
100 |         if (id) {
101 |             urlParams.set('user_id', id);
102 |         }
103 |         if (groupId) {
104 |             urlParams.set('group_id', groupId);
105 |         }
106 |         if (serialNumber) {
107 |             urlParams.set('serial_number', serialNumber);
108 |         }
109 |         if (sort) {
110 |             urlParams.set('user_sort', JSON.stringify({
111 |                 [sort]: order || 'asc',
112 |             }));
113 |         }
114 | 
115 |         const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.GET_BROWSER_LIST}`, { params: urlParams });
116 |         return `Browser list: ${JSON.stringify(response.data.data.list, null, 2)}`;
117 |     },
118 | 
119 |     async getOpenedBrowser() {
120 |         const response = await axios.get(`${LOCAL_API_BASE}${API_ENDPOINTS.GET_OPENED_BROWSER}`);
121 |         
122 |         if (response.data.code === 0) {
123 |             return `Opened browser list: ${JSON.stringify(response.data.data.list, null, 2)}`;
124 |         }
125 |         throw new Error(`Failed to get opened browsers: ${response.data.msg}`);
126 |     },
127 | 
128 |     async moveBrowser({ groupId, userIds }: MoveBrowserParams) {
129 |         const response = await axios.post(`${LOCAL_API_BASE}${API_ENDPOINTS.MOVE_BROWSER}`, {
130 |             group_id: groupId,
131 |             user_ids: userIds
132 |         });
133 | 
134 |         if (response.data.code === 0) {
135 |             return `Browsers moved successfully to group ${groupId}: ${userIds.join(', ')}`;
136 |         }
137 |         throw new Error(`Failed to move browsers: ${response.data.msg}`);
138 |     }
139 | }; 
```

--------------------------------------------------------------------------------
/src/handlers/automation.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import path from 'path';
  2 | import os from 'os';
  3 | import type { CreateAutomationParams, NavigateParams, ScreenshotParams, ClickElementParams, FillInputParams, SelectOptionParams, HoverElementParams, ScrollElementParams, PressKeyParams, EvaluateScriptParams, DragElementParams, IframeClickElementParams } from '../types/browser.js';
  4 | import browser from '../utils/browserBase.js';
  5 | 
  6 | const defaultDownloadsPath = path.join(os.homedir(), 'Downloads');
  7 | 
  8 | export const automationHandlers = {
  9 |     async connectBrowserWithWs({ wsUrl }: CreateAutomationParams) {
 10 |         try {
 11 |             await browser.connectBrowserWithWs(wsUrl);
 12 |             return `Browser connected successfully with: ${wsUrl}`;
 13 |         } catch (error) {
 14 |             return `Failed to connect browser with: ${error?.toString()}`;
 15 |         }
 16 |     },
 17 |     async openNewPage() {
 18 |         browser.checkConnected();
 19 |         const newPage = await browser.pageInstance!.context().newPage();
 20 |         browser.pageInstance = newPage;
 21 |         return `New page opened successfully`;
 22 |     },
 23 |     async navigate({ url }: NavigateParams) {
 24 |         browser.checkConnected();
 25 |         await browser.pageInstance!.goto(url);
 26 |         return `Navigated to ${url} successfully`;
 27 |     },
 28 |     async screenshot({ savePath, isFullPage }: ScreenshotParams) {
 29 |         browser.checkConnected();
 30 |         const filename = `screenshot-${Date.now()}-${Math.random().toString(36).substring(2, 15)}.png`;
 31 |         const outputPath = path.join(savePath || defaultDownloadsPath, filename);
 32 |         const screenshot = await browser.pageInstance!.screenshot({ path: outputPath, fullPage: isFullPage });
 33 |         const screenshotBase64 = screenshot.toString('base64');
 34 |         browser.screenshotsInstance.set(filename, screenshotBase64);
 35 |         return [{
 36 |             type: 'image' as const,
 37 |             data: screenshotBase64,
 38 |             mimeType: 'image/png'
 39 |         }];
 40 |     },
 41 |     async getPageVisibleText() {
 42 |         browser.checkConnected();
 43 |         try {
 44 |             const visibleText = await browser.pageInstance!.evaluate(() => {
 45 |                 const walker = document.createTreeWalker(
 46 |                     document.body,
 47 |                     NodeFilter.SHOW_TEXT,
 48 |                     {
 49 |                         acceptNode: (node) => {
 50 |                             const style = window.getComputedStyle(
 51 |                                 node.parentElement!
 52 |                             );
 53 |                             return style.display !== 'none' &&
 54 |                                 style.visibility !== 'hidden'
 55 |                                 ? NodeFilter.FILTER_ACCEPT
 56 |                                 : NodeFilter.FILTER_REJECT;
 57 |                         },
 58 |                     }
 59 |                 );
 60 |                 let text = '';
 61 |                 let node;
 62 |                 while ((node = walker.nextNode())) {
 63 |                     const trimmedText = node.textContent?.trim();
 64 |                     if (trimmedText) {
 65 |                         text += trimmedText + '\n';
 66 |                     }
 67 |                 }
 68 |                 return text.trim();
 69 |             });
 70 |             return `Visible text content:\n${visibleText}`;
 71 |         } catch (error) {
 72 |             return `Failed to get visible text content: ${(error as Error).message}`;
 73 |         }
 74 |     },
 75 |     async getPageHtml() {
 76 |         browser.checkConnected();
 77 |         const html = await browser.pageInstance!.content();
 78 |         return html;
 79 |     },
 80 |     async clickElement({ selector }: ClickElementParams) {
 81 |         browser.checkConnected();
 82 |         await browser.pageInstance!.click(selector);
 83 |         return `Clicked element with selector: ${selector} successfully`;
 84 |     },
 85 |     async iframeClickElement({ selector, iframeSelector }: IframeClickElementParams) {
 86 |         const frame = browser.pageInstance!.frameLocator(iframeSelector);
 87 |         if (!frame) {
 88 |             return `Iframe not found: ${iframeSelector}`;
 89 |         }
 90 |         
 91 |         await frame.locator(selector).click();
 92 |         return `Clicked element ${selector} inside iframe ${iframeSelector} successfully`;
 93 |     },
 94 |     async fillInput({ selector, text }: FillInputParams) {
 95 |         browser.checkConnected();
 96 |         await browser.pageInstance!.waitForSelector(selector);
 97 |         await browser.pageInstance!.fill(selector, text);
 98 |         return `Filled input with selector: ${selector} with text: ${text} successfully`;
 99 |     },
100 |     async selectOption({ selector, value }: SelectOptionParams) {
101 |         browser.checkConnected();
102 |         await browser.pageInstance!.waitForSelector(selector);
103 |         await browser.pageInstance!.selectOption(selector, value);
104 |         return `Selected option with selector: ${selector} with value: ${value} successfully`;
105 |     },
106 |     async hoverElement({ selector }: HoverElementParams) {
107 |         browser.checkConnected();
108 |         await browser.pageInstance!.waitForSelector(selector);
109 |         await browser.pageInstance!.hover(selector);
110 |         return `Hovered element with selector: ${selector} successfully`;
111 |     },
112 |     async scrollElement({ selector }: ScrollElementParams) {
113 |         browser.checkConnected();
114 |         await browser.pageInstance!.waitForSelector(selector);
115 |         await browser.pageInstance!.evaluate((selector) => {
116 |             const element = document.querySelector(selector);
117 |             if (element) {
118 |                 element.scrollIntoView({ behavior: 'smooth' });
119 |             }
120 |         }, selector);
121 |         return `Scrolled element with selector: ${selector} successfully`;
122 |     },
123 |     async pressKey({ key, selector }: PressKeyParams) {
124 |         browser.checkConnected();
125 |         if (selector) {
126 |             await browser.pageInstance!.waitForSelector(selector);
127 |             await browser.pageInstance!.focus(selector);
128 |         }
129 |         await browser.pageInstance!.keyboard.press(key);
130 |         return `Pressed key: ${key} successfully`;
131 |     },
132 |     async evaluateScript({ script }: EvaluateScriptParams) {
133 |         browser.checkConnected();
134 |         const result = await browser.pageInstance!.evaluate(script);
135 |         return result;
136 |     },
137 |     async dragElement({ selector, targetSelector }: DragElementParams) {
138 |         browser.checkConnected();
139 |         const sourceElement = await browser.pageInstance!.waitForSelector(selector);
140 |         const targetElement = await browser.pageInstance!.waitForSelector(targetSelector);
141 |         
142 |         const sourceBound = await sourceElement.boundingBox();
143 |         const targetBound = await targetElement.boundingBox();
144 |         
145 |         if (!sourceBound || !targetBound) {
146 |             return `Could not get element positions for drag operation`;
147 |         }
148 | 
149 |         await browser.pageInstance!.mouse.move(
150 |             sourceBound.x + sourceBound.width / 2,
151 |             sourceBound.y + sourceBound.height / 2
152 |         );
153 |         await browser.pageInstance!.mouse.down();
154 |         await browser.pageInstance!.mouse.move(
155 |             targetBound.x + targetBound.width / 2,
156 |             targetBound.y + targetBound.height / 2
157 |         );
158 |         await browser.pageInstance!.mouse.up();
159 |         return `Dragged element with selector: ${selector} to ${targetSelector} successfully`;
160 |     }
161 | }
162 | 
163 | export const getScreenshot = (filename: string) => {
164 |     return browser.screenshotsInstance.get(filename);
165 | }
166 | 
```

--------------------------------------------------------------------------------
/src/types/schemas.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { z } from 'zod';
  2 | 
  3 | // Proxy Config Schema
  4 | const userProxyConfigSchema = z.object({
  5 |     proxy_soft: z.enum([
  6 |         'brightdata', 'brightauto', 'oxylabsauto', '922S5auto',
  7 |         'ipideeauto', 'ipfoxyauto', '922S5auth', 'kookauto',
  8 |         'ssh', 'other', 'no_proxy'
  9 |     ]).describe('The proxy soft of the browser'),
 10 |     proxy_type: z.enum(['http', 'https', 'socks5', 'no_proxy']).optional(),
 11 |     proxy_host: z.string().optional().describe('The proxy host of the browser, eg: 127.0.0.1'),
 12 |     proxy_port: z.string().optional().describe('The proxy port of the browser, eg: 8080'),
 13 |     proxy_user: z.string().optional().describe('The proxy user of the browser, eg: user'),
 14 |     proxy_password: z.string().optional().describe('The proxy password of the browser, eg: password'),
 15 |     proxy_url: z.string().optional().describe('The proxy url of the browser, eg: http://127.0.0.1:8080'),
 16 |     global_config: z.enum(['0', '1']).optional().describe('The global config of the browser, default is 0')
 17 | }).describe('The user proxy config of the browser');
 18 | 
 19 | // Browser Kernel Config Schema
 20 | const browserKernelConfigSchema = z.object({
 21 |     version: z.union([
 22 |         z.literal("92"), z.literal("99"), z.literal("102"),
 23 |         z.literal("105"), z.literal("108"), z.literal("111"),
 24 |         z.literal("114"), z.literal("115"), z.literal("116"),
 25 |         z.literal("117"), z.literal("118"), z.literal("119"),
 26 |         z.literal("120"), z.literal("121"), z.literal("122"),
 27 |         z.literal("123"), z.literal("124"), z.literal("125"),
 28 |         z.literal("126"), z.literal("127"), z.literal("128"),
 29 |         z.literal("129"), z.literal("130"), z.literal("131"),
 30 |         z.literal("132"), z.literal("133"), z.literal("134"),
 31 |         z.literal("ua_auto")
 32 |     ]).optional().describe('The version of the browser, default is ua_auto'),
 33 |     type: z.enum(['chrome', 'firefox']).optional().describe('The type of the browser, default is chrome')
 34 | }).optional().describe('The browser kernel config of the browser, default is version: ua_auto, type: chrome');
 35 | 
 36 | // Random UA Config Schema
 37 | const randomUaConfigSchema = z.object({
 38 |     ua_version: z.array(z.string()).optional(),
 39 |     ua_system_version: z.array(
 40 |         z.enum([
 41 |             'Android 9', 'Android 10', 'Android 11', 'Android 12', 'Android 13',
 42 |             'iOS 14', 'iOS 15',
 43 |             'Windows 7', 'Windows 8', 'Windows 10', 'Windows 11',
 44 |             'Mac OS X 10', 'Mac OS X 11', 'Mac OS X 12', 'Mac OS X 13',
 45 |             'Linux'
 46 |         ])
 47 |     ).optional().describe('The ua system version of the browser, eg: ["Android 9", "iOS 14"]')
 48 | }).optional().describe('The random ua config of the browser, default is ua_version: [], ua_system_version: []');
 49 | 
 50 | // Fingerprint Config Schema
 51 | const fingerprintConfigSchema = z.object({
 52 |     automatic_timezone: z.enum(['0', '1']).optional().describe('The automatic timezone of the browser, default is 0'),
 53 |     timezone: z.string().optional().describe('The timezone of the browser, eg: Asia/Shanghai'),
 54 |     language: z.array(z.string()).optional().describe('The language of the browser, eg: ["en-US", "zh-CN"]'),
 55 |     flash: z.enum(['block', 'allow']).optional().describe('The flash of the browser, default is disabled'),
 56 |     fonts: z.array(z.string()).optional().describe('The fonts of the browser, eg: ["Arial", "Times New Roman"]'),
 57 |     webrtc: z.enum(['disabled', 'forward', 'proxy', 'local']).optional().describe('The webrtc of the browser, default is disabled'),
 58 |     browser_kernel_config: browserKernelConfigSchema,
 59 |     random_ua: randomUaConfigSchema,
 60 |     tls_switch: z.enum(['0', '1']).optional().describe('The tls switch of the browser, default is 0'),
 61 |     tls: z.string().optional().describe('The tls of the browser, if tls_switch is 1, you can set the tls of the browser, eg: "0xC02C,0xC030"')
 62 | }).optional().describe('The fingerprint config of the browser, default is automatic_timezone: 0, timezone: "", language: [], flash: "", fonts: [], webrtc: disabled, browser_kernel_config: ua_auto, random_ua: ua_version: [], ua_system_version: [], tls_switch: 0, tls: ""');
 63 | 
 64 | export const schemas = {
 65 |     // Browser Related Schema
 66 |     createBrowserSchema: z.object({
 67 |         domainName: z.string().optional().describe('The domain name of the browser, eg: facebook.com'),
 68 |         openUrls: z.array(z.string()).optional().describe('The open urls of the browser, eg: ["https://www.google.com"]'),
 69 |         cookie: z.string().optional().describe('The cookie of the browser, eg: "[{\"domain\":\".baidu.com\",\"expirationDate\":\"\",\"name\":\"\",\"path\":\"/\",\"sameSite\":\"unspecified\",\"secure\":true,\"value\":\"\",\"id\":1}]"'),
 70 |         username: z.string().optional().describe('The username of the browser, eg: "user"'),
 71 |         password: z.string().optional().describe('The password of the browser, eg: "password"'),
 72 |         groupId: z.string()
 73 |             .regex(/^\d+$/, "Group ID must be a numeric string")
 74 |             .describe('The group id of the browser, must be a numeric string (e.g., "123"). You can use the get-group-list tool to get the group list or create a new group, or default is 0'),
 75 |         name: z.string().optional().describe('The name of the browser, eg: "My Browser"'),
 76 |         country: z.string().optional().describe('The country of the browser, eg: "CN"'),
 77 |         sysAppCateId: z.string().optional().describe('The sys app cate id of the browser, you can use the get-application-list tool to get the application list'),
 78 |         userProxyConfig: userProxyConfigSchema,
 79 |         fingerprintConfig: fingerprintConfigSchema,
 80 |         storageStrategy: z.number().optional().describe('The storage strategy of the browser, default is 0')
 81 |     }),
 82 | 
 83 |     updateBrowserSchema: z.object({
 84 |         domainName: z.string().optional().describe('The domain name of the browser, eg: facebook.com'),
 85 |         openUrls: z.array(z.string()).optional().describe('The open urls of the browser, eg: ["https://www.google.com"]'),
 86 |         cookie: z.string().optional().describe('The cookie of the browser, eg: "[{\"domain\":\".baidu.com\",\"expirationDate\":\"\",\"name\":\"\",\"path\":\"/\",\"sameSite\":\"unspecified\",\"secure\":true,\"value\":\"\",\"id\":1}]"'),
 87 |         username: z.string().optional().describe('The username of the browser, eg: "user"'),
 88 |         password: z.string().optional().describe('The password of the browser, eg: "password"'),
 89 |         groupId: z.string().optional().describe('The group id of the browser, must be a numeric string (e.g., "123"). You can use the get-group-list tool to get the group list or create a new group'),
 90 |         name: z.string().optional().describe('The name of the browser, eg: "My Browser"'),
 91 |         country: z.string().optional().describe('The country of the browser, eg: "CN"'),
 92 |         sysAppCateId: z.string().optional().describe('The sys app cate id of the browser, you can use the get-application-list tool to get the application list'),
 93 |         userProxyConfig: userProxyConfigSchema.optional(),
 94 |         fingerprintConfig: fingerprintConfigSchema.optional(),
 95 |         storageStrategy: z.number().optional().describe('The storage strategy of the browser, default is 0'),
 96 |         userId: z.string().describe('The user id of the browser to update, it is required when you want to update the browser')
 97 |     }),
 98 | 
 99 |     openBrowserSchema: z.object({
100 |         serialNumber: z.string().optional().describe('The serial number of the browser to open'),
101 |         userId: z.string().optional().describe('The browser id of the browser to open'),
102 |         ipTab: z.enum(['0', '1']).optional().describe('The ip tab of the browser, 0 is not use ip tab, 1 is use ip tab, default is 0'),
103 |         launchArgs: z.string().optional().describe(`The launch args of the browser, use chrome launch args, eg: ${JSON.stringify(["--blink-settings=imagesEnabled=false", "--disable-notifications"])}, or vista url, eg: ${JSON.stringify(["https://www.adspower.net"])}`),
104 |         clearCacheAfterClosing: z.enum(['0', '1']).optional().describe('The clear cache after closing of the browser, 0 is not clear cache after closing, 1 is clear cache after closing, default is 0'),
105 |         cdpMask: z.enum(['0', '1']).optional().describe('The cdp mask of the browser, 0 is not use cdp mask, 1 is use cdp mask, default is 0'),
106 |     }).strict(),
107 | 
108 |     closeBrowserSchema: z.object({
109 |         userId: z.string().describe('The browser id of the browser to stop, it is required when you want to stop the browser')
110 |     }).strict(),
111 | 
112 |     deleteBrowserSchema: z.object({
113 |         userIds: z.array(z.string()).describe('The user ids of the browsers to delete, it is required when you want to delete the browser')
114 |     }).strict(),
115 | 
116 |     getBrowserListSchema: z.object({
117 |         groupId: z.string()
118 |             .regex(/^\d+$/, "Group ID must be a numeric string")
119 |             .optional()
120 |             .describe('The group id of the browser, must be a numeric string (e.g., "123"). You can use the get-group-list tool to get the group list'),
121 |         size: z.number().optional().describe('The size of the page, max is 100, if get more than 100, you need to use the page to get the next page, default is 10'),
122 |         page: z.number().optional().describe('The page of the browser, default is 1'),
123 |         id: z.string().optional().describe('The id of the browser'),
124 |         serialNumber: z.string().optional().describe('The serial number of the browser'),
125 |         sort: z.enum(['serial_number', 'last_open_time', 'created_time']).optional()
126 |             .describe('The sort of the browser'),
127 |         order: z.enum(['asc', 'desc']).optional()
128 |             .describe('The order of the browser')
129 |     }).strict(),
130 | 
131 |     moveBrowserSchema: z.object({
132 |         groupId: z.string()
133 |             .regex(/^\d+$/, "Group ID must be a numeric string")
134 |             .describe('The target group id, must be a numeric string (e.g., "123"). You can use the get-group-list tool to get the group list'),
135 |         userIds: z.array(z.string()).describe('The browser ids to move')
136 |     }).strict(),
137 | 
138 |     // Group Related Schema
139 |     createGroupSchema: z.object({
140 |         groupName: z.string().describe('The name of the group to create'),
141 |         remark: z.string().optional().describe('The remark of the group')
142 |     }).strict(),
143 | 
144 |     updateGroupSchema: z.object({
145 |         groupId: z.string()
146 |             .regex(/^\d+$/, "Group ID must be a numeric string")
147 |             .describe('The id of the group to update, must be a numeric string (e.g., "123"). You can use the get-group-list tool to get the group list'),
148 |         groupName: z.string().describe('The new name of the group'),
149 |         remark: z.string().nullable().optional().describe('The new remark of the group')
150 |     }).strict(),
151 | 
152 |     getGroupListSchema: z.object({
153 |         groupName: z.string().optional().describe('The name of the group to search, use like to search, often used group name to find the group id, so eg: "test" will search "test" and "test1"'),
154 |         size: z.number().optional().describe('The size of the page, max is 100, if get more than 100, you need to use the page to get the next page, default is 10'),
155 |         page: z.number().optional().describe('The page of the group, default is 1')
156 |     }).strict(),
157 | 
158 |     // Application Related Schema
159 |     getApplicationListSchema: z.object({
160 |         size: z.number().optional().describe('The size of the page')
161 |     }).strict(),
162 | 
163 |     // Empty Schema
164 |     emptySchema: z.object({}).strict(),
165 | 
166 |     // Automation Related Schema
167 |     createAutomationSchema: z.object({
168 |         userId: z.string().optional().describe('The browser id of the browser to connect'),
169 |         serialNumber: z.string().optional().describe('The serial number of the browser to connect'),
170 |         wsUrl: z.string().describe('The ws url of the browser, get from the open-browser tool content `ws.puppeteer`')
171 |     }).strict(),
172 | 
173 |     navigateSchema: z.object({
174 |         url: z.string().describe('The url to navigate to')
175 |     }).strict(),
176 | 
177 |     screenshotSchema: z.object({
178 |         savePath: z.string().optional().describe('The path to save the screenshot'),
179 |         isFullPage: z.boolean().optional().describe('The is full page of the screenshot')
180 |     }).strict(),
181 | 
182 |     clickElementSchema: z.object({
183 |         selector: z.string().describe('The selector of the element to click, find from the page source code')
184 |     }).strict(),
185 | 
186 |     fillInputSchema: z.object({
187 |         selector: z.string().describe('The selector of the input to fill, find from the page source code'),
188 |         text: z.string().describe('The text to fill in the input')
189 |     }).strict(),
190 | 
191 |     selectOptionSchema: z.object({
192 |         selector: z.string().describe('The selector of the option to select, find from the page source code'),
193 |         value: z.string().describe('The value of the option to select')
194 |     }).strict(),
195 | 
196 |     hoverElementSchema: z.object({
197 |         selector: z.string().describe('The selector of the element to hover, find from the page source code')
198 |     }).strict(),
199 | 
200 |     scrollElementSchema: z.object({
201 |         selector: z.string().describe('The selector of the element to scroll, find from the page source code, Simulates a user navigating page by scrolling, usually finding element in the bottom of the page')
202 |     }).strict(),
203 | 
204 |     pressKeySchema: z.object({
205 |         key: z.string().describe('The key to press, eg: "Enter"'),
206 |         selector: z.string().optional().describe('The selector of the element to press the key, find from the page source code')
207 |     }).strict(),
208 | 
209 |     evaluateScriptSchema: z.object({
210 |         script: z.string().describe('The script to evaluate, eg: "document.querySelector(\'#username\').value = \'test\'"')
211 |     }).strict(),
212 | 
213 |     dragElementSchema: z.object({
214 |         selector: z.string().describe('The selector of the element to drag, find from the page source code'),
215 |         targetSelector: z.string().describe('The selector of the element to drag to, find from the page source code'),
216 |     }).strict(),
217 | 
218 |     iframeClickElementSchema: z.object({
219 |         selector: z.string().describe('The selector of the element to click, find from the page source code'),
220 |         iframeSelector: z.string().describe('The selector of the iframe to click, find from the page source code')
221 |     }).strict(),
222 | };
```