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

```
├── .env
├── .gitignore
├── .npmignore
├── LICENSE
├── package.json
├── README.md
├── src
│   ├── .DS_Store
│   ├── assets
│   │   ├── aipower_logo.png
│   │   ├── aipower_logo.svg
│   │   ├── user_folder.png
│   │   └── yingdao_logo.svg
│   ├── baseServer.ts
│   ├── httpServer.ts
│   ├── i18n
│   │   └── index.ts
│   ├── index.ts
│   ├── schema
│   │   ├── local.ts
│   │   └── openApi.ts
│   ├── stdioServer.ts
│   ├── types
│   │   └── yingdao.d.ts
│   └── yingdao
│       ├── localService.ts
│       ├── openApiService.ts
│       └── tokenManager.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------

```
1 | src/
2 | .env
3 | .gitignore
4 | tsconfig.json
5 | node_modules/
```

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

```
1 | package-lock.json
2 | node_modules
3 | 
4 | # production
5 | /dist
```

--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------

```
1 | RPA_MODEL=local
2 | ACCESS_KEY_ID=
3 | ACCESS_KEY_SECRET=
4 | SHADOWBOT_PATH=
5 | USER_FOLDER=
6 | SERVER_PORT=3000
```

--------------------------------------------------------------------------------
/src/stdioServer.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
 2 | import { BaseServer } from "./baseServer.js";
 3 | 
 4 | 
 5 | export class StdioServer extends BaseServer { 
 6 |     constructor() {
 7 |         super();
 8 |     }
 9 |     async start(): Promise<void> {
10 |         const transport = new StdioServerTransport();
11 |         await this.server.connect(transport);
12 |     }
13 | }
```

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

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

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

```typescript
 1 | #!/usr/bin/env node
 2 | import { config } from 'dotenv';
 3 | 
 4 | config();
 5 | 
 6 | import { HttpServer } from './httpServer.js';
 7 | import i18n from './i18n/index.js';
 8 | import { StdioServer } from './stdioServer.js';
 9 | export async function startServer():Promise<void> {
10 |   const isServer = process.argv.includes('--server');
11 |   i18n.language = process.env.LANGUAGE || 'zh';
12 |   if (!process.env.RPA_MODEL) {
13 |     process.env.RPA_MODEL = 'local';
14 |   }
15 |   if (isServer) {
16 |     const server = new HttpServer(); 
17 |     await server.start();
18 |   } else {
19 |     const server = new StdioServer();
20 |     await server.start(); 
21 |   }
22 | 
23 | } 
24 | 
25 | if (process.argv[1]) {
26 |   startServer().catch(error => {
27 |     console.error("Failed to start server:", error);
28 |     process.exit(1);
29 |   });
30 |   console.log('yingdao-mcp-server started');
31 | }
```

--------------------------------------------------------------------------------
/src/schema/local.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { z } from "zod";
 2 | import i18n from '../i18n/index.js';
 3 | 
 4 | // Schema for executeRpaApp method
 5 | export const executeRpaAppSchema = {
 6 |     appUuid: z.string().describe(i18n.t('schema.local.executeRpaApp.appUuid')),
 7 |     appParams: z.any().describe(i18n.t('schema.local.executeRpaApp.appParams'))
 8 | } as const;
 9 | 
10 | // Schema for queryRobotParam method
11 | export const queryRobotParamSchema = {
12 |     robotUuid: z.string().optional().describe(i18n.t('schema.local.queryRobotParam.robotUuid'))
13 | } as const;
14 | 
15 | // Schema for queryAppList method - no parameters needed
16 | export const queryAppListSchema = {} as const;
17 | 
18 | // Response types for better type safety
19 | export const AppInfoSchema = z.object({
20 |     uuid: z.string(),
21 |     name: z.string(),
22 |     description: z.string().optional()
23 | });
24 | 
25 | export type AppInfo = z.infer<typeof AppInfoSchema>;
26 | 
27 | // Export all schemas
28 | export const localSchemas = {
29 |     executeRpaApp: executeRpaAppSchema,
30 |     queryRobotParam: queryRobotParamSchema,
31 |     queryAppList: queryAppListSchema
32 | };
```

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

```json
 1 | {
 2 |     "name": "yingdao-mcp-server",
 3 |     "version": "0.0.2",
 4 |     "description": "Mcp server for yingdao rpa, both sse and stdio mode",
 5 |     "license": "MIT",
 6 |     "author": "Automa AI Power(https://www.automa.site/)",
 7 |     "homepage": "https://www.automa.site/",
 8 |     "type": "module",
 9 |     "bugs": "https://github.com/ying-dao/yingdao_mcp_server/issues",
10 |     "repository": {
11 |         "type": "git",
12 |         "url": "https://github.com/ying-dao/yingdao_mcp_server.git"
13 |     },
14 |     "files": [
15 |         "dist"
16 |     ],
17 |     "bin": {
18 |         "yingdao-mcp-server": "dist/index.js"
19 |     },
20 |     "main": "dist/index.js",
21 |     "scripts": {
22 |         "build": "tsc && shx chmod +x dist/*.js",
23 |         "start": "node dist/index.js",
24 |         "start:server": "node dist/index.js --server",
25 |         "prepublishOnly": "npm run build"
26 |     },
27 |     "keywords": [
28 |         "yingdao",
29 |         "ai power",
30 |         "rpa",
31 |         "mcp"
32 |     ],
33 |     "dependencies": {
34 |         "@modelcontextprotocol/sdk": "^1.10.1",
35 |         "axios": "^1.8.4",
36 |         "cors": "^2.8.5",
37 |         "dotenv": "^16.5.0",
38 |         "express": "^4.18.2",
39 |         "i18next": "^23.7.11",
40 |         "react-i18next": "^13.5.0",
41 |         "uuid": "^9.0.0",
42 |         "zod": "^3.24.3",
43 |         "shx": "^0.3.4"
44 |     },
45 |     "devDependencies": {
46 |         "@types/express": "^5.0.1",
47 |         "nodemon": "^2.0.22"
48 |     }
49 | }
```

--------------------------------------------------------------------------------
/src/yingdao/tokenManager.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import axios, { AxiosInstance } from 'axios';
 2 | 
 3 | interface TokenResponse {
 4 |   data: {
 5 |     accessToken: string;
 6 |     expiresIn: number;
 7 |   };
 8 |   code: number;
 9 |   success: boolean;
10 |   requestId: string;
11 | }
12 | 
13 | export class TokenManager {
14 |   private token: string | null = null;
15 |   private tokenExpireTime: number = 0;
16 |   private readonly client: AxiosInstance;
17 |   private readonly baseURL: string = 'https://api.yingdao.com';
18 |   private readonly accessKeyId: string;
19 |   private readonly accessKeySecret: string;
20 | 
21 |   constructor(accessKeyId: string, accessKeySecret: string) {
22 |     this.accessKeyId = accessKeyId;
23 |     this.accessKeySecret = accessKeySecret;
24 |     this.client = axios.create({
25 |       baseURL: this.baseURL,
26 |       timeout: 10000,
27 |     });
28 |   }
29 | 
30 |   private isTokenExpired(): boolean {
31 |     return Date.now() >= this.tokenExpireTime;
32 |   }
33 | 
34 |   async getToken(): Promise<string> {
35 |     if (this.token && !this.isTokenExpired()) {
36 |       return this.token;
37 |     }
38 | 
39 |     try {
40 |       const response = await this.client.get<TokenResponse>('/oapi/token/v2/token/create', {
41 |         params: {
42 |           accessKeyId: this.accessKeyId,
43 |           accessKeySecret: this.accessKeySecret,
44 |         },
45 |       });
46 | 
47 |       if (!response.data.success || response.data.code !== 200) {
48 |         throw new Error('Failed to get token');
49 |       }
50 | 
51 |       this.token = response.data.data.accessToken;
52 |       // Convert expiresIn from seconds to milliseconds and subtract 5 minutes as buffer
53 |       this.tokenExpireTime = Date.now() + (response.data.data.expiresIn * 1000) - 300000;
54 | 
55 |       return this.token;
56 |     } catch (error:any) {
57 |       throw new Error(`Failed to get token: ${error.message}`);
58 |     }
59 |   }
60 | }
```

--------------------------------------------------------------------------------
/src/assets/aipower_logo.svg:
--------------------------------------------------------------------------------

```
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="22px" viewBox="0 0 24 22" version="1.1">
 3 |   <g id="低代码项目" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
 4 |     <g id="AP设计一期-mvp" transform="translate(-521.000000, -513.000000)" fill-rule="nonzero">
 5 |       <g id="编组-39" transform="translate(501.000000, 326.000000)">
 6 |         <g id="编组-19备份" transform="translate(0.000000, 91.000000)">
 7 |           <g id="icon_ap" transform="translate(20.000000, 96.000000)">
 8 |             <path d="M5.176535,10.70872 L13.801935,2.08335 C13.963235,1.92209 14.181935,1.83149 14.410035,1.83149 C14.638035,1.83149 14.856735,1.92209 15.018035,2.08335 L23.035435,10.10072 C23.196635,10.26202 23.287235,10.48072 23.287235,10.70872 C23.287235,10.93682 23.196635,11.15552 23.035435,11.31682 L15.018035,19.33422 C14.856735,19.49542 14.638035,19.58602 14.410035,19.58602 C14.181935,19.58602 13.963235,19.49542 13.801935,19.33422 L5.176535,10.70872 Z" id="路径" fill="#FEC80E"></path>
 9 |             <path d="M10.708735,5.17629 L15.633435,10.10102 C15.794735,10.26222 15.885335,10.48092 15.885335,10.70902 C15.885335,10.93702 15.794735,11.15582 15.633435,11.31702 L10.708735,16.24172 L5.176045,10.70902 L10.708735,5.17629 Z" id="路径" fill="#7208B7"></path>
10 |             <path d="M0.252345,4.5685 L4.568995,0.25185 C4.730255,0.09059 4.948975,0 5.177025,0 C5.405085,0 5.623805,0.09059 5.785065,0.25185 L10.709735,5.17653 L5.177025,10.70922 L0.252346,5.78457 C0.091085,5.62331 0.000489,5.40459 0.000489,5.17653 C0.000489,4.94848 0.091085,4.72976 0.252345,4.5685 Z" id="路径" fill="#FF4A4A"></path>
11 |             <path d="M5.176535,10.70872 L10.709235,16.24152 L5.784575,21.16612 C5.623315,21.32742 5.404595,21.41802 5.176535,21.41802 C4.948485,21.41802 4.729765,21.32742 4.568505,21.16612 L0.251856,16.84952 C0.090596,16.68822 0,16.46952 0,16.24152 C0,16.01342 0.090595,15.79472 0.251855,15.63342 L5.176535,10.70872 Z" id="路径" fill="#04C9FB"></path>
12 |           </g>
13 |         </g>
14 |       </g>
15 |     </g>
16 |   </g>
17 | </svg>
18 | 
```

--------------------------------------------------------------------------------
/src/httpServer.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import express, { Request, Response } from 'express';
 2 | import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
 3 | import { BaseServer } from './baseServer.js';
 4 | import { IncomingMessage, ServerResponse, Server } from "http";
 5 | 
 6 | export class HttpServer extends BaseServer {
 7 |     private app: express.Application;
 8 |     private port: number;
 9 |     private transports: { [sessionId: string]: SSEServerTransport } = {};
10 |     constructor() {
11 |         super();
12 |         this.app = express();
13 | 
14 |         this.port = Number(process.env.SERVER_PORT) || 3000;
15 |     }
16 | 
17 |     async start(): Promise<void> {
18 |        
19 |         // Configure Express routes
20 |         this.app.use(express.json());
21 |         
22 |         // Mount the MCP server on the Express app
23 |         this.app.post('/messages', async (req: Request, res: Response) => { 
24 |             const sessionId = req.query.sessionId as string;
25 |             if (!this.transports[sessionId]) {
26 |                 res.status(400).send(`No transport found for sessionId ${sessionId}`);
27 |                 return;
28 |             }
29 |             console.log(`Received message for sessionId ${sessionId}`,req.body,req.method,req.url);
30 |            
31 |             await this.transports[sessionId].handlePostMessage(req, res,req.body);
32 |         });
33 |         this.app.get('/sse', async (req, res) => {
34 |              console.log(`Received`,req.body,req.method,req.url);
35 |             // Set up the SSE transport for the MCP server
36 |             const transport = new SSEServerTransport("/messages", res as unknown as ServerResponse<IncomingMessage>);
37 |             this.transports[transport.sessionId] = transport;
38 |             res.on("close", () => {
39 |                 delete this.transports[transport.sessionId];
40 |                  });
41 |             await this.server.connect(transport);
42 |         });
43 | 
44 |         // Start the HTTP server
45 |         this.app.listen(this.port, () => {
46 |             console.log(`RPA HTTP Server listening on port ${this.port}`);
47 |             console.log(`SSE endpoint available at http://localhost:${this.port}/sse`);
48 |             console.log(`Message endpoint available at http://localhost:${this.port}/messages`);
49 |         });
50 |        
51 |     }
52 | }
```

--------------------------------------------------------------------------------
/src/yingdao/localService.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { platform } from 'os';
 2 | import path from 'path';
 3 | import { spawn } from 'child_process';
 4 | import { readFileSync,readdirSync, existsSync } from 'fs';
 5 | export class LocalService {
 6 |     appsPath: string;
 7 |     constructor(private  shadowbotPath: string,private readonly userFolder: string) {
 8 |         this.appsPath = path.join(this.userFolder, 'apps');
 9 |         if (platform() === 'darwin') {
10 |             this.shadowbotPath=path.join(this.shadowbotPath, 'Contents/MacOS/影刀');
11 |         } 
12 |     }
13 |     
14 |     async executeRpaApp(appUuid: string, appParams: any) {
15 |         let argv = `shadowbot:Run?robot-uuid=${appUuid}`;
16 |         spawn(this.shadowbotPath, [argv]);
17 |         return 'success';
18 |     }
19 | 
20 |     async queryRobotParam(robotUuid?: string): Promise<any> {
21 |          const mainFlowJsonPath=path.join(this.appsPath, 'xbot_robot','.dev','main.flow.json');
22 |         if (!existsSync(mainFlowJsonPath)) {
23 |             const mainFlow = JSON.parse(readFileSync(mainFlowJsonPath, 'utf8'));
24 |             if (mainFlow.parameters) {
25 |                 return mainFlow.parameters;
26 |             } else {
27 |                 return [];
28 |             }
29 |         }
30 |         return [];
31 |     }
32 | 
33 |     async queryAppList() {
34 |         const result = [];
35 |         
36 |         try {
37 |             if (!existsSync(this.appsPath)) {
38 |                 return [];
39 |             }
40 |             const appFolders = readdirSync(this.appsPath, { withFileTypes: true })
41 |                 .filter(dirent => dirent.isDirectory() && dirent.name.endsWith('_Release'))
42 |                 .map(dirent => dirent.name);
43 |             for (const folder of appFolders) {
44 |                 const packageJsonPath = path.join(this.appsPath, folder, 'xbot_robot', 'package.json');
45 |                 if (existsSync(packageJsonPath)) {
46 |                     try {
47 |                         const packageData = JSON.parse(readFileSync(packageJsonPath, 'utf8'));
48 |                         if (packageData.robot_type === 'app' && packageData.name) {
49 |                             result.push({
50 |                                 uuid: packageData.uuid || '',
51 |                                 name: packageData.name,
52 |                                 description: packageData.description || ''
53 |                             });
54 |                         }
55 |                     } catch (err) {
56 |                         console.error(`Error parsing package.json in ${folder}:`, err);
57 |                     }
58 |                 }
59 |             }
60 |             
61 |             return result;
62 |         } catch (err) {
63 |             console.error('Error reading apps directory:', err);
64 |             return [];
65 |         }
66 |     }
67 | }
68 | 
```

--------------------------------------------------------------------------------
/src/types/yingdao.d.ts:
--------------------------------------------------------------------------------

```typescript
  1 | interface AppListResponse {
  2 |   total: number;
  3 |   list: Array<{
  4 |     id: string;
  5 |     name: string;
  6 |     ownerUser: string;
  7 |     createTime: string;
  8 |   }>;
  9 | }
 10 | 
 11 | interface RobotParamResponse {
 12 |   code: number;
 13 |   success: boolean;
 14 |   msg: string;
 15 |   data: {
 16 |     inputParams: Array<{
 17 |       name: string;
 18 |       direction: string;
 19 |       type: string;
 20 |       value: string;
 21 |       description: string;
 22 |       kind: string;
 23 |     }>;
 24 |     outputParams: Array<{
 25 |       name: string;
 26 |       direction: string;
 27 |       type: string;
 28 |       value: string;
 29 |       description: string;
 30 |       kind: string;
 31 |     }>;
 32 |   };
 33 | }
 34 | 
 35 | interface FileUploadResponse {
 36 |   code: number;
 37 |   success: boolean;
 38 |   msg: string;
 39 |   data: {
 40 |     fileKey: string;
 41 |   };
 42 | }
 43 | 
 44 | interface JobStartRequest {
 45 |   accountName?: string;
 46 |   robotClientGroupUuid?: string;
 47 |   robotUuid: string;
 48 |   idempotentUuid?: string;
 49 |   waitTimeout?: string;
 50 |   waitTimeoutSeconds?: number;
 51 |   runTimeout?: number;
 52 |   priority?: string;
 53 |   executeScope?: string;
 54 |   params?: Array<{
 55 |     name: string;
 56 |     value: string;
 57 |     type: string;
 58 |   }>;
 59 | }
 60 | 
 61 | interface JobStartResponse {
 62 |   code: number;
 63 |   success: boolean;
 64 |   msg: string;
 65 |   data: {
 66 |     jobUuid: string;
 67 |     idempotentFlag: boolean;
 68 |   };
 69 | }
 70 | 
 71 | interface JobQueryRequest {
 72 |   jobUuid: string;
 73 | }
 74 | 
 75 | interface JobQueryResponse {
 76 |   code: number;
 77 |   success: boolean;
 78 |   msg: string;
 79 |   data: {
 80 |     jobUuid: string;
 81 |     status: string;
 82 |     statusName: string;
 83 |     remark?: string;
 84 |     robotClientUuid?: string;
 85 |     robotClientName?: string;
 86 |     startTime?: string;
 87 |     endTime?: string;
 88 |     robotUuid: string;
 89 |     robotName: string;
 90 |     screenshotUrl?: string;
 91 |     robotParams?: {
 92 |       inputs?: Array<{
 93 |         name: string;
 94 |         value: string;
 95 |         type: string;
 96 |       }>;
 97 |       outputs?: Array<{
 98 |         name: string;
 99 |         value: string;
100 |         type: string;
101 |       }>;
102 |     };
103 |   };
104 | }
105 | 
106 | interface ClientListRequest {
107 |   status?: string;
108 |   key?: string;
109 |   robotClientGroupUuid?: string;
110 |   page: number;
111 |   size: number;
112 | }
113 | 
114 | interface ClientListResponse {
115 |   code: number;
116 |   success: boolean;
117 |   msg: string;
118 |   data: Array<{
119 |     robotClientUuid: string;
120 |     robotClientName: string;
121 |     status: string;
122 |     description?: string;
123 |     windowsUserName?: string;
124 |     clientIp?: string;
125 |     machineName?: string;
126 |   }>;
127 |   page: {
128 |     total: number;
129 |     size: number;
130 |     page: number;
131 |     pages: number;
132 |   };
133 | }
134 | 
135 | interface JobQueryResponse {
136 |   code: number;
137 |   success: boolean;
138 |   msg: string;
139 |   data: {
140 |     jobUuid: string;
141 |     status: string;
142 |     statusName: string;
143 |     remark?: string;
144 |     robotClientUuid?: string;
145 |     robotClientName?: string;
146 |     startTime?: string;
147 |     endTime?: string;
148 |     robotUuid: string;
149 |     robotName: string;
150 |     screenshotUrl?: string;
151 |     robotParams?: {
152 |       inputs?: Array<{
153 |         name: string;
154 |         value: string;
155 |         type: string;
156 |       }>;
157 |       outputs?: Array<{
158 |         name: string;
159 |         value: string;
160 |         type: string;
161 |       }>;
162 |     };
163 |   };
164 | }
165 | 
```

--------------------------------------------------------------------------------
/src/schema/openApi.ts:
--------------------------------------------------------------------------------

```typescript
 1 | import { z } from "zod";
 2 | import i18n from '../i18n/index.js';
 3 | 
 4 | // Schema for executeRpaApp method
 5 | export const executeRpaAppSchema = {
 6 |     appUuid: z.string().describe(i18n.t('schema.local.executeRpaApp.appUuid')),
 7 |     appParams: z.any().describe(i18n.t('schema.local.executeRpaApp.appParams'))
 8 | } as const;
 9 | 
10 | // Schema for queryRobotParam method
11 | export const queryRobotParamSchema = {
12 |     robotUuid: z.string().optional().describe(i18n.t('schema.local.queryRobotParam.robotUuid'))
13 | } as const;
14 | 
15 | // Schema for queryAppList method - no parameters needed
16 | export const queryAppListSchema = {} as const;
17 | 
18 | // Response types for better type safety
19 | export const AppInfoSchema = z.object({
20 |     uuid: z.string(),
21 |     name: z.string(),
22 |     description: z.string().optional()
23 | });
24 | 
25 | export type AppInfo = z.infer<typeof AppInfoSchema>;
26 | 
27 | // Export all schemas
28 | export const localSchemas = {
29 |     executeRpaApp: executeRpaAppSchema,
30 |     queryRobotParam: queryRobotParamSchema,
31 |     queryAppList: queryAppListSchema
32 | };
33 | 
34 | export const uploadFileSchema = {
35 |     file: z.any().describe(i18n.t('schema.uploadFile.file')),
36 |     fileName: z.string().max(100).describe(i18n.t('schema.uploadFile.fileName'))
37 | } as const;
38 | 
39 | export const robotParamSchema = {
40 |     robotUuid: z.string().optional().describe(i18n.t('schema.robotParam.robotUuid')),
41 |     accurateRobotName: z.string().optional().describe(i18n.t('schema.robotParam.accurateRobotName'))
42 | } as const;
43 | 
44 | export const querySchema = {
45 |     appId: z.string().optional().describe(i18n.t('schema.query.appId')),
46 |     size: z.string()
47 |     .optional()
48 |     .default('30')
49 |     .transform(Number)
50 |     .describe(i18n.t('schema.query.size')),
51 |     page: z.string()
52 |     .optional()
53 |     .default('1')
54 |     .transform(Number)
55 |     .describe(i18n.t('schema.query.page')),
56 |     ownerUserSearchKey: z.string().optional().describe(i18n.t('schema.query.ownerUserSearchKey')),
57 |     appName: z.string().optional().describe(i18n.t('schema.query.appName'))
58 | } as const;
59 | 
60 | export const startJobSchema = {
61 |     robotUuid: z.string().describe(i18n.t('schema.startJob.robotUuid')),
62 |     accountName: z.string().optional().describe(i18n.t('schema.startJob.accountName')),
63 |     robotClientGroupUuid: z.string().optional().describe(i18n.t('schema.startJob.robotClientGroupUuid')),
64 |     waitTimeoutSeconds: z.number()
65 |         .optional()
66 |         .describe(i18n.t('schema.startJob.waitTimeoutSeconds')),
67 |     runTimeout: z.number()
68 |         .optional()
69 |         .describe(i18n.t('schema.startJob.runTimeout')),
70 |     params: z.record(z.any()).optional()
71 |         .describe(i18n.t('schema.startJob.params'))
72 | } as const;
73 | 
74 | export const queryJobSchema = {
75 |     jobUuid: z.string().describe(i18n.t('schema.queryJob.jobUuid'))
76 | } as const;
77 | 
78 | export const clientListSchema = {
79 |     status: z.string().optional().describe(i18n.t('schema.clientList.status')),
80 |     key: z.string().optional().describe(i18n.t('schema.clientList.key')),
81 |     robotClientGroupUuid: z.string().optional().describe(i18n.t('schema.clientList.robotClientGroupUuid')),
82 |     page: z.string()
83 |         .default('1')
84 |         .transform(Number)
85 |         .describe(i18n.t('schema.clientList.page')),
86 |     size: z.string()
87 |         .default('30')
88 |         .transform(Number)
89 |         .describe(i18n.t('schema.clientList.size'))
90 | } as const;
```

--------------------------------------------------------------------------------
/src/yingdao/openApiService.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import axios from 'axios';
  2 | import { AxiosInstance } from 'axios';
  3 | import { TokenManager } from './tokenManager.js';
  4 | import i18n from '../i18n/index.js';
  5 | 
  6 | export class OpenApiService {
  7 |   private readonly client: AxiosInstance;
  8 |   private readonly baseURL: string = "https://api.yingdao.com/oapi/";
  9 |   private readonly tokenManager: TokenManager;
 10 | 
 11 |   constructor(accessKeyId: string, accessKeySecret: string) {
 12 |     this.tokenManager = new TokenManager(accessKeyId, accessKeySecret);
 13 |     this.client = axios.create({
 14 |       baseURL: this.baseURL,
 15 |       timeout: 10000,
 16 |     });
 17 | 
 18 |     // Add request interceptor to automatically add token
 19 |     this.client.interceptors.request.use(async (config) => {
 20 |       const token = await this.tokenManager.getToken();
 21 |       config.headers.Authorization = `Bearer ${token}`;
 22 |       return config;
 23 |     });
 24 |   }
 25 | 
 26 |   async queryAppList(params: {
 27 |     appId?: string;
 28 |     size?: number;
 29 |     page?: number;
 30 |     ownerUserSearchKey?: string;
 31 |     appName?: string;
 32 |   }): Promise<AppListResponse> {
 33 |     try {
 34 |       const response = await this.client.post('/app/open/query/list', params);
 35 |       console.log("response",response.data);
 36 |       return response.data.code === 200
 37 |         ? response.data.data
 38 |         :  response.data.msg ;
 39 |     } catch (error: any) {
 40 |       throw new Error(`Failed to fetch app list: ${error.message}`);
 41 |     }
 42 |   }
 43 | 
 44 |   async queryRobotParam(params: {
 45 |     robotUuid?: string;
 46 |     accurateRobotName?: string;
 47 |   }): Promise<RobotParamResponse> {
 48 |     try {
 49 |       const response = await this.client.post('/robot/v2/queryRobotParam', params);
 50 |       console.log("response",response.data);
 51 |        return response.data.code === 200
 52 |         ? response.data.data
 53 |         :  response.data.msg ;
 54 |     } catch (error: any) {
 55 |       throw new Error(`Failed to fetch robot parameters: ${error.message}`);
 56 |     }
 57 |   }
 58 | 
 59 |   async uploadFile(file: Buffer | string, fileName: string): Promise<FileUploadResponse> {
 60 |     // Validate filename length
 61 |     if (fileName.length > 100) {
 62 |       throw new Error(i18n.t('rpaService.error.fileNameTooLong'));
 63 |     }
 64 | 
 65 |     // Validate file type
 66 |     const allowedExtensions = ['txt', 'csv', 'xlsx'];
 67 |     const fileExtension = fileName.split('.').pop()?.toLowerCase();
 68 |     if (!fileExtension || !allowedExtensions.includes(fileExtension)) {
 69 |       throw new Error(i18n.t('rpaService.error.unsupportedFileType'));
 70 |     }
 71 | 
 72 |     // Create FormData and convert file to Blob
 73 |     const formData = new FormData();
 74 |     const blob = typeof file === 'string' ? new Blob([file]) : new Blob([file]);
 75 |     formData.append('file', blob, fileName);
 76 | 
 77 |     try {
 78 |       const response = await this.client.post('/dispatch/v2/file/upload', formData, {
 79 |         headers: {
 80 |           'Content-Type': 'multipart/form-data'
 81 |         },
 82 |         maxBodyLength: 10 * 1024 * 1024 // 10MB limit
 83 |       });
 84 | 
 85 |       if (response.data.code !== 200) {
 86 |         throw new Error(response.data.msg || i18n.t('rpaService.error.uploadFailed'));
 87 |       }
 88 | 
 89 |       return response.data;
 90 |     } catch (error: any) {
 91 |       throw new Error(`${i18n.t('rpaService.error.uploadFailed')}: ${error.message}`);
 92 |     }
 93 |   }
 94 | 
 95 |   async startJob(params: JobStartRequest): Promise<JobStartResponse> {
 96 |     // Validate required parameters
 97 |     if (!params.robotUuid) {
 98 |       throw new Error(i18n.t('rpaService.error.robotUuidRequired'));
 99 |     }
100 |     try {
101 |       const response = await this.client.post<JobStartResponse>('/dispatch/v2/job/start', params);
102 |       console.log("response",response.data);
103 |       if (!response.data.success || response.data.code !== 200) {
104 |         throw new Error(response.data.msg || i18n.t('rpaService.error.startJobFailed'));
105 |       }
106 |       return response.data;
107 |     } catch (error: any) {
108 |       throw new Error(`${i18n.t('rpaService.error.startJobFailed')}: ${error.message}`);
109 |     }
110 |   }
111 | 
112 |   async queryJob(params: JobQueryRequest): Promise<JobQueryResponse> {
113 |     if (!params.jobUuid) {
114 |       throw new Error(i18n.t('rpaService.error.jobUuidRequired'));
115 |     }
116 | 
117 |     try {
118 |       const response = await this.client.post<JobQueryResponse>('/dispatch/v2/job/query', params);
119 |       if (!response.data.success || response.data.code !== 200) {
120 |         throw new Error(response.data.msg || i18n.t('rpaService.error.queryJobFailed'));
121 |       }
122 |       return response.data;
123 |     } catch (error: any) {
124 |       throw new Error(`${i18n.t('rpaService.error.queryJobFailed')}: ${error.message}`);
125 |     }
126 |   }
127 | 
128 |   async queryClientList(params: ClientListRequest): Promise<ClientListResponse> {
129 |     // Validate required parameters
130 |     if (!params.page || !params.size) {
131 |       throw new Error(i18n.t('rpaService.error.pageSizeRequired'));
132 |     }
133 | 
134 |     try {
135 |       const response = await this.client.post<ClientListResponse>('/dispatch/v2/client/list', params);
136 |       console.log("response",response.data);
137 |       if (!response.data.success || response.data.code !== 200) {
138 |         throw new Error(response.data.msg || i18n.t('rpaService.error.queryClientListFailed'));
139 |       }
140 |       return response.data;
141 |     } catch (error: any) {
142 |       throw new Error(`${i18n.t('rpaService.error.queryClientListFailed')}: ${error.message}`);
143 |     }
144 |   }
145 | }
```

--------------------------------------------------------------------------------
/src/assets/yingdao_logo.svg:
--------------------------------------------------------------------------------

```
 1 | <svg width="75" height="26" viewBox="0 0 75 26" fill="none" xmlns="http://www.w3.org/2000/svg">
 2 | <path d="M47.9039 9.61529L47.9351 9.64533L47.9671 9.61529C50.1013 8.31481 51.8281 6.80245 53.1473 5.04781L53.1785 4.98752L51.0747 4.14062L51.0435 4.17066C49.9764 5.68322 48.4371 7.01374 46.4278 8.07267L46.3652 8.10274L46.4278 8.13297L47.904 9.61529H47.9039Z" fill="black"/>
 3 | <path d="M37.0801 5.79648H43.2651V8.76049H37.3939V8.00468H42.4484V6.55245H35.3223V10.1827H44.1128C44.8661 10.1827 45.4632 9.60738 45.4632 8.88161V4.34422H36.1067L35.3223 3.89062L35.3535 5.79642H37.0801V5.79648Z" fill="black"/>
 4 | <path d="M57.6239 4.53131V7.13281H63.5262V7.34409C63.4004 10.3389 62.9609 12.7284 62.2388 14.453C61.1717 16.9026 59.2874 18.8084 56.6191 20.1096L58.2834 21.9544C61.1405 20.442 63.2123 18.2939 64.4052 15.4811C65.284 13.4849 65.818 10.7324 66.0069 7.34409V7.16287H71.7834V7.37489C71.501 17.0237 71.6891 18.5663 71.2809 19.1108C71.0294 19.4137 70.6219 19.5343 70.1192 19.5343C68.8951 19.5343 67.9217 19.5041 67.1365 19.474L67.8898 21.7423C68.5181 21.7731 69.1146 21.7731 69.6166 21.7731C70.3393 21.7731 70.967 21.8032 71.3752 21.7423C72.3168 21.5911 73.102 21.2587 73.5726 20.563C74.0434 19.8673 73.9491 16.9333 74.2947 6.225L74.3261 5.16663L58.4715 5.10577L57.6239 4.53125L57.6239 4.53131Z" fill="black"/>
 5 | <path d="M46.4654 11.12H41.4109L40.9713 10.4844H38.7739L39.2127 11.12H34.6288L34.0645 12.6926H44.8018C45.7439 12.7234 46.4654 11.9969 46.4654 11.12Z" fill="black"/>
 6 | <path d="M51.5741 9.58311C50.507 11.1257 48.7802 12.5171 46.5196 13.7573L46.457 13.7873L46.5196 13.8176L47.9645 15.2398L47.995 15.2698L48.0263 15.2398C50.2869 13.9085 52.1707 12.275 53.6154 10.3999L53.6466 10.3391L51.6061 9.49219L51.5741 9.58311H51.5741Z" fill="black"/>
 7 | <path d="M51.953 15.1462C50.7283 17.2634 48.7509 18.988 46.0819 20.2582L46.0195 20.2884L46.0819 20.3493L47.5261 21.952L47.5575 21.9822L47.5894 21.952C50.352 20.4396 52.5182 18.4134 54.0255 15.9028L54.0569 15.8419L51.985 15.0859L51.953 15.1462Z" fill="black"/>
 8 | <path d="M45.6814 20.3828L45.65 20.2919C45.273 19.5662 44.7705 18.7193 44.1739 17.8115L44.1427 17.7812L42.3535 18.538L42.3841 18.5981C43.0436 19.657 43.5776 20.6552 43.9234 21.5321L43.9546 21.5929L45.8695 20.7152L45.7445 20.4431L45.6814 20.3828H45.6814Z" fill="black"/>
 9 | <path d="M43.2631 15.8465C43.2631 15.9676 43.1686 16.0585 43.043 16.0585H37.3914V15.0599H43.2632V15.8465H43.2631ZM45.3668 16.1797V13.5781H35.3516V17.48H39.1812V20.1115C39.1812 20.323 39.0563 20.4442 38.805 20.4442H37.5802H37.4858L37.5178 20.505C37.7683 21.0495 37.9566 21.5632 38.0829 22.0777V22.1077H38.1141C39.3069 22.1077 40.1234 22.0174 40.5316 21.8053C41.0655 21.594 41.3163 21.0187 41.3163 20.1418V17.4499H43.9539C44.7697 17.48 45.3668 16.9054 45.3668 16.1797L45.3668 16.1797Z" fill="black"/>
10 | <path d="M37.863 18.3257L35.8857 17.7812L35.8537 17.8113C35.3829 18.8102 34.8178 19.657 34.1269 20.3828L34.0957 20.4128L34.347 20.564L35.6975 21.4718L35.728 21.502L35.7593 21.4718C36.5446 20.564 37.2353 19.5059 37.8006 18.3558L37.863 18.3257Z" fill="black"/>
11 | <path fill-rule="evenodd" clip-rule="evenodd" d="M12.9979 0C20.1774 0 25.9979 5.8206 25.9979 13C25.9979 20.1794 20.1774 26 12.9979 26C5.81866 26 -0.00195312 20.1794 -0.00195312 13C-0.00195312 5.8206 5.81866 0 12.9979 0Z" fill="#E7323B"/>
12 | <path fill-rule="evenodd" clip-rule="evenodd" d="M14.986 8.28662C15.95 8.3355 16.7187 9.13215 16.7187 10.1099C16.7187 11.0877 15.95 11.8869 14.986 11.9356V11.9378H14.8931H11.135V11.9356C11.121 11.9356 11.1093 11.9378 11.0955 11.9378C10.0874 11.9378 9.26984 11.1204 9.26984 10.1099C9.26984 9.10181 10.0874 8.28433 11.0955 8.28433C11.1093 8.28433 11.121 8.28433 11.135 8.28433H14.8931H14.986V8.28662ZM15.3368 6.41933C17.3273 6.47498 18.9229 8.10773 18.9229 10.1099C18.9229 12.1144 17.3273 13.7449 15.3368 13.803V13.8052H15.2299H10.7356C8.69386 13.8052 7.04242 12.1516 7.04242 10.1099C7.04242 8.07062 8.69386 6.41688 10.7356 6.41688H15.2299H15.3368V6.41933ZM12.8725 21.2981C13.0142 21.4305 13.1022 21.6211 13.1022 21.8298C13.1022 22.2341 12.7748 22.5592 12.3729 22.5592C12.1454 22.5592 11.9409 22.4571 11.8085 22.2945C10.803 21.2075 10.1896 19.7558 10.1896 18.1601C10.1896 17.0245 10.5009 15.9584 11.0445 15.0479H10.5312H10.5264C8.62182 15.0479 6.98677 13.9051 6.26674 12.27C5.44679 12.1748 4.81032 11.485 4.80111 10.6417H4.79883V10.6208V9.6013V9.58961H4.80111C4.80576 8.76745 5.40741 8.08925 6.19236 7.96153C6.8521 6.32633 8.45468 5.17188 10.3244 5.17188C10.394 5.17188 10.4614 5.17416 10.5312 5.17661V5.17188H15.3785V5.17661C15.4459 5.17416 15.5132 5.17188 15.5806 5.17188C17.448 5.17188 19.0461 6.32173 19.7081 7.94984C20.5325 8.04268 21.176 8.73953 21.1829 9.58961V9.6013V10.6208V10.6417H21.1804C21.1712 11.5105 20.493 12.2189 19.636 12.277C18.9136 13.9098 17.2785 15.0479 15.3786 15.0479H15.0905C15.5806 15.719 15.8734 16.5436 15.8734 17.4402C15.8734 18.5969 15.3855 19.6398 14.6076 20.3759L14.6051 20.3736C14.4727 20.499 14.2938 20.5759 14.0965 20.5759C13.6899 20.5759 13.3577 20.2459 13.3577 19.8371C13.3577 19.6071 13.4625 19.4028 13.6273 19.2681C14.0987 18.8036 14.3938 18.1554 14.3938 17.4402C14.3938 17.1963 14.3589 16.9593 14.2962 16.7364C14.18 16.33 13.892 15.8236 13.6041 15.5101C13.5181 15.4149 13.4275 15.3288 13.3276 15.2475C13.2394 15.1757 13.1464 15.1083 13.0489 15.0479H12.8259C12.0897 15.8701 11.6391 16.9547 11.6391 18.1462C11.6391 19.3634 12.1083 20.4689 12.8726 21.2958V21.2981H12.8725Z" fill="white"/>
13 | </svg>
14 | 
```

--------------------------------------------------------------------------------
/src/baseServer.ts:
--------------------------------------------------------------------------------

```typescript
  1 | import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
  2 | import i18n from './i18n/index.js';
  3 | import { OpenApiService } from './yingdao/openApiService.js';
  4 | import {LocalService} from './yingdao/localService.js';
  5 | import { querySchema, robotParamSchema, uploadFileSchema, startJobSchema, queryJobSchema, clientListSchema } from './schema/openApi.js';
  6 | import { executeRpaAppSchema, queryRobotParamSchema } from './schema/local.js';
  7 | export abstract class BaseServer{
  8 |     public readonly server: McpServer;
  9 |     protected readonly openApiService?: OpenApiService;
 10 |     protected readonly localService?: LocalService;
 11 | 
 12 |     constructor() {
 13 |         this.server = new McpServer({
 14 |             name: 'Yingdao RPA Mcp Server',
 15 |             version: '0.0.1',
 16 |         },{
 17 |         capabilities: {
 18 |             logging: {},
 19 |             tools: {},
 20 |             },
 21 |         });        
 22 |         if (process.env.RPA_MODEL !== 'local') {
 23 |             if(!process.env.ACCESS_KEY_ID || !process.env.ACCESS_KEY_SECRET) {
 24 |                 throw new Error('Missing ACCESS_KEY_ID or ACCESS_KEY_SECRET environment variables');
 25 |             }
 26 |             this.openApiService = new OpenApiService(process.env.ACCESS_KEY_ID, process.env.ACCESS_KEY_SECRET);
 27 |              this.registerTools();
 28 |         } else {
 29 |             if(!process.env.SHADOWBOT_PATH || !process.env.USER_FOLDER){
 30 |                     throw new Error('Missing SHADOWBOT_PATH or USER_FOLDER environment variables');
 31 |                 }
 32 |             this.localService = new LocalService(process.env.SHADOWBOT_PATH, process.env.USER_FOLDER);
 33 |            
 34 |             this.registerLocalTools();
 35 |         }
 36 |         
 37 |     }
 38 | 
 39 |     abstract start(): Promise<void>;
 40 | 
 41 |     registerLocalTools(): void{
 42 |         this.server.tool('queryApplist', i18n.t('tool.queryApplist.description'), querySchema, async ({ appId, size, page, ownerUserSearchKey, appName }) => {
 43 |             try {
 44 |                 const result = await this.localService?.queryAppList();
 45 |                 return { content: [{ type: 'text', text: JSON.stringify(result) }]};
 46 |             } catch (error) {
 47 |                 throw new Error(i18n.t('tool.queryApplist.error'));
 48 |             }
 49 |         });
 50 |         this.server.tool('runApp', i18n.t('tool.runApp.description'), executeRpaAppSchema, async ({ appUuid, appParams }) => {
 51 |             try {
 52 |                 const result = await this.localService?.executeRpaApp(appUuid, appParams);
 53 |                 return { content: [{ type: 'text', text: JSON.stringify(result) }]};
 54 |             } catch (error) {
 55 |                 throw new Error(i18n.t('tool.runApp.error'));
 56 |             }
 57 |         });
 58 |         this.server.tool('queryRobotParam', i18n.t('tool.uploadFile.description'), queryRobotParamSchema, async ({ robotUuid }) => {
 59 |             try {
 60 |                 const result = await this.localService?.queryRobotParam(robotUuid);
 61 |                 return { content: [{ type: 'text', text: JSON.stringify(result) }]};
 62 |             } catch (error) {
 63 |                 throw new Error(i18n.t('tool.uploadFile.error'));
 64 |             }
 65 |         });
 66 |     }
 67 |     
 68 |     registerTools(): void{
 69 |         this.server.tool('uploadFile', i18n.t('tool.uploadFile.description'), uploadFileSchema, async ({ file, fileName }) => {
 70 |             try {
 71 |                 const result = await this.openApiService?.uploadFile(file, fileName);
 72 |                 return { content: [{ type: 'text', text: JSON.stringify(result) }]};
 73 |             } catch (error) {
 74 |                 throw new Error(i18n.t('tool.uploadFile.error'));
 75 |             }
 76 |         });
 77 |         this.server.tool('queryRobotParam', i18n.t('tool.queryRobotParam.description'), robotParamSchema, async ({ robotUuid, accurateRobotName }) => {
 78 |             try {
 79 |                 const result = await this.openApiService?.queryRobotParam({
 80 |                     robotUuid,
 81 |                     accurateRobotName
 82 |                 });
 83 |                 return { content: [{ type: 'text', text: JSON.stringify(result) }]};
 84 |             } catch (error) {
 85 |                 throw new Error(i18n.t('tool.queryRobotParam.error'));
 86 |             }
 87 |         });
 88 |         this.server.tool('queryApplist', i18n.t('tool.queryApplist.description'), querySchema, async ({ appId, size, page, ownerUserSearchKey, appName }) => {
 89 |             try {
 90 |                 const result = await this.openApiService?.queryAppList({
 91 |                     appId,
 92 |                     size,
 93 |                     page,
 94 |                     ownerUserSearchKey,
 95 |                     appName
 96 |                 });
 97 |                 return { content: [{ type: 'text', text: JSON.stringify(result) }]};
 98 |             } catch (error) {
 99 |                 throw new Error(i18n.t('tool.queryApplist.error'));
100 |             }
101 |         });
102 |         this.server.tool('startJob', i18n.t('tool.startJob.description'), startJobSchema, async ({ robotUuid, accountName,params }) => {
103 |             try {
104 |                 // Transform params from Record<string, any> to the expected array format
105 |                 const transformedParams = params ? Object.entries(params).map(([name, value]) => ({
106 |                     name,
107 |                     value: String(value), // Convert value to string
108 |                     type: 'string' // Default type as string
109 |                 })) : undefined;
110 |                 
111 |                 const result = await this.openApiService?.startJob({
112 |                     robotUuid,
113 |                     accountName,
114 |                     params: transformedParams
115 |                 });
116 |                 return { content: [{ type: 'text', text: JSON.stringify(result) }]};
117 |             } catch (error) {
118 |                 console.error(error);
119 |                 throw new Error(i18n.t('tool.startJob.error'));
120 |             }
121 |         });
122 |         this.server.tool('queryJob', i18n.t('tool.queryJob.description'), queryJobSchema, async ({ jobUuid }) => {
123 |             try {
124 |                 const result = await this.openApiService?.queryJob({
125 |                     jobUuid
126 |                 });
127 |                 return { content: [{ type: 'text', text: JSON.stringify(result) }]};
128 |             } catch (error) {
129 |                 throw new Error(i18n.t('tool.queryJob.error'));
130 |             }
131 |         });
132 |         this.server.tool('queryClientList', i18n.t('tool.queryClientList.description'), clientListSchema, async ({ status, key, robotClientGroupUuid, page, size }) => {
133 |             try {
134 |                 const result = await this.openApiService?.queryClientList({
135 |                     status,
136 |                     key,
137 |                     robotClientGroupUuid,
138 |                     page,
139 |                     size
140 |                 });
141 |                 return { content: [{ type: 'text', text: JSON.stringify(result) }]};
142 |             } catch (error) {
143 |                 throw new Error(i18n.t('tool.queryClientList.error'));
144 |             }
145 |         });
146 |     }
147 | 
148 | }
```

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

```typescript
  1 | import i18n from 'i18next';
  2 | import { initReactI18next } from 'react-i18next';
  3 | 
  4 | const resources = {
  5 |   en: {
  6 |     translation: {
  7 |       // Tool descriptions
  8 |       'tool.uploadFile.description': 'This interface is used to upload files to the RPA platform.',
  9 |       'tool.queryRobotParam.description': 'This interface is used to query RPA robot parameters.',
 10 |       'tool.queryApplist.description': 'This interface is used to paginate and get the RPA application list.',
 11 |       'tool.startJob.description': 'This interface is used to start an RPA job.',
 12 |       'tool.queryJob.description': 'This interface is used to query the status of an RPA job.',
 13 |       'tool.queryClientList.description': 'This interface is used to query the list of RPA clients.',
 14 |       'tool.runApp.description': 'This interface is used to run an RPA application.',
 15 |       
 16 |       // Tool errors
 17 |       'tool.uploadFile.error': 'Failed to upload file',
 18 |       'tool.queryRobotParam.error': 'Failed to query robot parameters',
 19 |       'tool.queryApplist.error': 'Failed to get application list',
 20 |       'tool.startJob.error': 'Failed to start job',
 21 |       'tool.queryJob.error': 'Failed to query job status',
 22 |       'tool.queryClientList.error': 'Failed to query client list',
 23 |       'tool.runApp.error': 'Failed to run application',
 24 |       
 25 |       // RpaService validation errors
 26 |       'rpaService.error.fileNameTooLong': 'File name length cannot exceed 100 characters',
 27 |       'rpaService.error.unsupportedFileType': 'Only txt, csv, xlsx file types are supported',
 28 |       'rpaService.error.uploadFailed': 'File upload failed',
 29 |       'rpaService.error.robotUuidRequired': 'robotUuid is a required parameter',
 30 |       'rpaService.error.accountNameAndGroupConflict': 'accountName and robotClientGroupUuid can only choose one of them',
 31 |       'rpaService.error.waitTimeoutRange': 'waitTimeoutSeconds must be between 60 and 950400 seconds',
 32 |       'rpaService.error.runTimeoutRange': 'runTimeout must be between 60 and 950400 seconds',
 33 |       'rpaService.error.paramsLengthExceeded': 'The total length of params cannot exceed 8000',
 34 |       'rpaService.error.startJobFailed': 'Failed to start application',
 35 |       'rpaService.error.jobUuidRequired': 'jobUuid is a required parameter',
 36 |       'rpaService.error.queryJobFailed': 'Failed to query application execution result',
 37 |       'rpaService.error.pageSizeRequired': 'page and size are required parameters',
 38 |       'rpaService.error.queryClientListFailed': 'Failed to query robot list',
 39 |       
 40 |       // Schema descriptions
 41 |       'schema.uploadFile.file': 'File content',
 42 |       'schema.uploadFile.fileName': 'File name, supports txt, csv, xlsx formats, length not exceeding 100 characters',
 43 |       'schema.robotParam.robotUuid': 'RPA application uuid',
 44 |       'schema.robotParam.accurateRobotName': 'Exact match of RPA application name',
 45 |       'schema.query.appId': 'RPA application ID',
 46 |       'schema.query.size': 'Page size',
 47 |       'schema.query.sizeRefine': 'Maximum 100 items per page',
 48 |       'schema.query.page': 'Page number',
 49 |       'schema.query.ownerUserSearchKey': 'Exact match of user account',
 50 |       'schema.query.appName': 'Fuzzy match of RPA application name',
 51 |       'schema.startJob.robotUuid': 'RPA application uuid, required',
 52 |       'schema.startJob.accountName': 'Account name, the client status must be idle,mutually exclusive with robotClientGroupUuid, choose only one',
 53 |       'schema.startJob.robotClientGroupUuid': 'RPA robot group uuid, mutually exclusive with accountName, choose only one',
 54 |       'schema.startJob.waitTimeoutSeconds': 'Wait timeout (seconds)',
 55 |       'schema.startJob.waitTimeoutRefine': 'Wait timeout must be between 60 and 950400 seconds',
 56 |       'schema.startJob.runTimeout': 'Run timeout (seconds)',
 57 |       'schema.startJob.runTimeoutRefine': 'Run timeout must be between 60 and 950400 seconds',
 58 |       'schema.startJob.params': 'Run parameters',
 59 |       'schema.startJob.paramsRefine': 'Total length of params cannot exceed 8000',
 60 |       'schema.queryJob.jobUuid': 'RPA application execution uuid, required',
 61 |       'schema.clientList.status': 'Status, generally no keyword is specified to query idle robots',
 62 |       'schema.clientList.key': 'Keyword',
 63 |       'schema.clientList.robotClientGroupUuid': 'RPA robot group uuid',
 64 |       'schema.clientList.page': 'Page number',
 65 |       'schema.clientList.size': 'Page size',
 66 |       'schema.clientList.sizeRefine': 'Maximum 100 items per page'
 67 |     }
 68 |   },
 69 |   zh: {
 70 |     translation: {
 71 |       // Tool descriptions
 72 |       'tool.uploadFile.description': '该接口用于上传文件到RPA平台。',
 73 |       'tool.queryRobotParam.description': '该接口用于查询RPA机器人参数。',
 74 |       'tool.queryApplist.description': '该接口用于分页获取RPA应用列表。',
 75 |       'tool.startJob.description': '该接口用于启动RPA应用JOB。',
 76 |       'tool.queryJob.description': '该接口用于查询RPA应用JOB状态。',
 77 |       'tool.queryClientList.description': '该接口用于查询RPA机器人列表。',
 78 |       'tool.runApp.description': '该接口用于运行RPA应用。',
 79 |       
 80 |       // Tool errors
 81 |       'tool.uploadFile.error': '上传文件失败',
 82 |       'tool.queryRobotParam.error': '查询机器人参数失败',
 83 |       'tool.queryApplist.error': '获取RPA应用列表失败',
 84 |       'tool.startJob.error': '启动RPA应用JOB失败',
 85 |       'tool.queryJob.error': '查询RPA应用JOB状态失败',
 86 |       'tool.queryClientList.error': '查询RPA机器人列表失败',
 87 |       'tool.runApp.error': '运行RPA应用失败',
 88 |       
 89 |       // RpaService validation errors
 90 |       'rpaService.error.fileNameTooLong': '文件名长度不能超过100',
 91 |       'rpaService.error.unsupportedFileType': '仅支持txt、csv、xlsx文件类型',
 92 |       'rpaService.error.uploadFailed': '文件上传失败',
 93 |       'rpaService.error.robotUuidRequired': 'robotUuid是必填参数',
 94 |       'rpaService.error.accountNameAndGroupConflict': 'accountName和robotClientGroupUuid只能选择其中一个',
 95 |       'rpaService.error.waitTimeoutRange': 'waitTimeoutSeconds必须在60到950400秒之间',
 96 |       'rpaService.error.runTimeoutRange': 'runTimeout必须在60到950400秒之间',
 97 |       'rpaService.error.paramsLengthExceeded': 'params参数总长度不能超过8000',
 98 |       'rpaService.error.startJobFailed': '启动RPA应用JOB失败',
 99 |       'rpaService.error.jobUuidRequired': 'jobUuid是必填参数',
100 |       'rpaService.error.queryJobFailed': '查询RPA应用JOB状态失败',
101 |       'rpaService.error.pageSizeRequired': 'page和size是必填参数',
102 |       'rpaService.error.queryClientListFailed': '查询RPA机器人列表失败',
103 |       
104 |       // Schema descriptions
105 |       'schema.uploadFile.file': '文件内容',
106 |       'schema.uploadFile.fileName': '文件名,支持txt、csv、xlsx格式,长度不超过100字符',
107 |       'schema.robotParam.robotUuid': 'RPA应用UUID',
108 |       'schema.robotParam.accurateRobotName': '精确匹配的RPA应用名称',
109 |       'schema.query.appId': 'RPA应用UUID',
110 |       'schema.query.size': '一页大小',
111 |       'schema.query.sizeRefine': '每页最大100条',
112 |       'schema.query.page': '页码',
113 |       'schema.query.ownerUserSearchKey': '用户账号精确匹配',
114 |       'schema.query.appName': 'RPA应用名称模糊匹配',
115 |       'schema.startJob.robotUuid': 'RPA应用uuid,必填',
116 |       'schema.startJob.accountName': 'RPA机器人账号名称,要求机器人的状态为idle,和robotClientGroupUuid互斥,二选一即可',
117 |       'schema.startJob.robotClientGroupUuid': 'RPA机器人组uuid,和accountName互斥,二选一即可',
118 |       'schema.startJob.waitTimeoutSeconds': '等待超时时间(秒)',
119 |       'schema.startJob.waitTimeoutRefine': '等待超时时间必须在60到950400秒之间',
120 |       'schema.startJob.runTimeout': '运行超时时间(秒)',
121 |       'schema.startJob.runTimeoutRefine': '运行超时时间必须在60到950400秒之间',
122 |       'schema.startJob.params': '运行参数',
123 |       'schema.startJob.paramsRefine': 'params参数总长度不能超过8000',
124 |       'schema.queryJob.jobUuid': 'RPA应用运行uuid,必填',
125 |       'schema.clientList.status': '状态,idle表示空闲,一般不指定关键字的情况下都是要查询空闲的机器人',
126 |       'schema.clientList.key': '关键字',
127 |       'schema.clientList.robotClientGroupUuid': 'RPA机器人组uuid',
128 |       'schema.clientList.page': '页码',
129 |       'schema.clientList.size': '一页大小',
130 |       'schema.clientList.sizeRefine': '每页最大100条'
131 |     }
132 |   }
133 | };
134 | 
135 | i18n
136 |   .use(initReactI18next)
137 |   .init({
138 |     resources,
139 |     lng: 'zh',
140 |     fallbackLng: 'en',
141 |     interpolation: {
142 |       escapeValue: false
143 |     }
144 |   });
145 | 
146 | export default i18n;
```