# 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 | 
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 | 
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 | 
55 |
56 | or use in Cursor
57 | 
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 | };
```