mirror of
https://github.com/bytebot-ai/bytebot.git
synced 2026-09-01 15:30:04 +08:00
Handle errors in MCP tools
This commit is contained in:
@@ -262,6 +262,9 @@ print(status.json())
|
||||
|
||||
Use the computer control API for precise automation:
|
||||
|
||||
The core container also exposes an [MCP](https://github.com/rekog-labs/MCP-Nest) endpoint.
|
||||
Connect your MCP client to `http://localhost:9990/sse` to invoke these tools over SSE.
|
||||
|
||||
```javascript
|
||||
// Take screenshot
|
||||
POST http://localhost:9990/computer-use
|
||||
|
||||
@@ -31,6 +31,7 @@ The foundation of the system - a containerized Linux desktop that provides:
|
||||
- Consistent environment across different platforms
|
||||
- Can be customized with additional software
|
||||
- Accessible via REST API on port 9990
|
||||
- MCP SSE endpoint available at `/sse`
|
||||
|
||||
### 2. AI Agent Service
|
||||
The brain of the system - orchestrates tasks using an LLM:
|
||||
@@ -82,7 +83,7 @@ Persistent storage for the agent system:
|
||||
Claude AI analyzes the task and generates a plan of computer actions
|
||||
</Step>
|
||||
<Step title="Action Execution">
|
||||
Agent sends computer actions to bytebotd daemon via REST API
|
||||
Agent sends computer actions to bytebotd via REST API or MCP
|
||||
</Step>
|
||||
<Step title="Desktop Automation">
|
||||
bytebotd executes actions (mouse, keyboard, screenshots) on the desktop
|
||||
@@ -101,7 +102,7 @@ Persistent storage for the agent system:
|
||||
graph LR
|
||||
A[Tasks UI] -->|WebSocket| B[Agent Service]
|
||||
A -->|HTTP Proxy| C[Desktop VNC]
|
||||
B -->|REST API| D[Desktop API]
|
||||
B -->|REST/MCP| D[Desktop API]
|
||||
B -->|SQL| E[PostgreSQL]
|
||||
B -->|HTTPS| F[Claude AI]
|
||||
D -->|IPC| G[bytebotd]
|
||||
@@ -128,7 +129,7 @@ graph LR
|
||||
|
||||
### API Security
|
||||
|
||||
- **Desktop API**: No authentication by default (localhost only)
|
||||
- **Desktop API**: No authentication by default (localhost only). Supports REST and MCP.
|
||||
- **Agent API**: Can be secured with API keys
|
||||
- **Database**: Password protected, not exposed externally
|
||||
- **VNC Access**: Optional password protection
|
||||
|
||||
@@ -114,6 +114,7 @@ Watch it work in real-time through the embedded VNC viewer in the tasks interfac
|
||||
| **Tasks UI** | [http://localhost:9992](http://localhost:9992) | Main interface for interacting with the agent |
|
||||
| **Agent API** | [http://localhost:9991/tasks](http://localhost:9991/tasks) | REST API for programmatic task creation |
|
||||
| **Computer API** | [http://localhost:9990/computer-use](http://localhost:9990/computer-use) | Low-level desktop control API |
|
||||
| **MCP SSE** | [http://localhost:9990/sse](http://localhost:9990/sse) | Connect MCP clients for tool access |
|
||||
|
||||
## Alternative Deployment Options
|
||||
|
||||
|
||||
Generated
+111
-11
@@ -17,6 +17,7 @@
|
||||
"@nestjs/serve-static": "^5.0.3",
|
||||
"@nestjs/websockets": "^11.1.2",
|
||||
"@nut-tree-fork/nut-js": "^4.2.6",
|
||||
"@rekog/mcp-nest": "^1.6.2",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.2",
|
||||
"http-proxy-middleware": "^3.0.5",
|
||||
@@ -2423,6 +2424,29 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@modelcontextprotocol/sdk": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.13.2.tgz",
|
||||
"integrity": "sha512-Vx7qOcmoKkR3qhaQ9qf3GxiVKCEu+zfJddHv6x3dY/9P6+uIwJnmuAur5aB+4FDXf41rRrDnOEGkviX5oYZ67w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ajv": "^6.12.6",
|
||||
"content-type": "^1.0.5",
|
||||
"cors": "^2.8.5",
|
||||
"cross-spawn": "^7.0.5",
|
||||
"eventsource": "^3.0.2",
|
||||
"express": "^5.0.1",
|
||||
"express-rate-limit": "^7.5.0",
|
||||
"pkce-challenge": "^5.0.0",
|
||||
"raw-body": "^3.0.0",
|
||||
"zod": "^3.23.8",
|
||||
"zod-to-json-schema": "^3.24.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@napi-rs/nice": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.0.1.tgz",
|
||||
@@ -3105,6 +3129,24 @@
|
||||
"url": "https://opencollective.com/unts"
|
||||
}
|
||||
},
|
||||
"node_modules/@rekog/mcp-nest": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@rekog/mcp-nest/-/mcp-nest-1.6.2.tgz",
|
||||
"integrity": "sha512-swcu99a/woQ5T1f4E/YJct1w/xzszH83bhzEuZwXh+cYRnQUJKhaJd5Aey/XyOtt/D9lQTKSdnoKdpija8kuWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-to-regexp": "^8.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@modelcontextprotocol/sdk": ">=1.10.0",
|
||||
"@nestjs/common": ">=9.0.0",
|
||||
"@nestjs/core": ">=9.0.0",
|
||||
"express": ">=4.0.0",
|
||||
"reflect-metadata": ">=0.1.14",
|
||||
"zod": ">=3.0.0",
|
||||
"zod-to-json-schema": ">=3.23.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@sec-ant/readable-stream": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz",
|
||||
@@ -4299,7 +4341,6 @@
|
||||
"version": "6.12.6",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
@@ -5688,7 +5729,6 @@
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
@@ -6421,6 +6461,29 @@
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/eventsource": {
|
||||
"version": "3.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz",
|
||||
"integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"eventsource-parser": "^3.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eventsource-parser": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.3.tgz",
|
||||
"integrity": "sha512-nVpZkTMM9rF6AQ9gPJpFsNAMt48wIzB5TQgiTLdHiuO8XEDhUgZEhqKlZWXbIzo9VmJ/HvysHqEaVeD5v9TPvA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/execa": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz",
|
||||
@@ -6525,6 +6588,22 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/express-rate-limit": {
|
||||
"version": "7.5.1",
|
||||
"resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.1.tgz",
|
||||
"integrity": "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/express-rate-limit"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"express": ">= 4.11"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/content-disposition": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
|
||||
@@ -6596,7 +6675,6 @@
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-diff": {
|
||||
@@ -6647,7 +6725,6 @@
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fast-levenshtein": {
|
||||
@@ -8773,7 +8850,6 @@
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-stable-stringify-without-jsonify": {
|
||||
@@ -9765,7 +9841,6 @@
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -9918,6 +9993,16 @@
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pkce-challenge": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz",
|
||||
"integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=16.20.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pkg-dir": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||
@@ -10123,7 +10208,6 @@
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
|
||||
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -10735,7 +10819,6 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
@@ -10748,7 +10831,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -12138,7 +12220,6 @@
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
|
||||
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
|
||||
"dev": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.0"
|
||||
@@ -12461,7 +12542,6 @@
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"dev": true,
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
@@ -12748,6 +12828,26 @@
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/zod": {
|
||||
"version": "3.25.67",
|
||||
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz",
|
||||
"integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/colinhacks"
|
||||
}
|
||||
},
|
||||
"node_modules/zod-to-json-schema": {
|
||||
"version": "3.24.6",
|
||||
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz",
|
||||
"integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==",
|
||||
"license": "ISC",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"zod": "^3.24.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.1",
|
||||
"socket.io": "^4.8.1",
|
||||
"uiohook-napi": "^1.5.4"
|
||||
"uiohook-napi": "^1.5.4",
|
||||
"@rekog/mcp-nest": "^1.6.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ConfigModule } from '@nestjs/config';
|
||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { BytebotMcpModule } from './mcp';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -17,6 +18,7 @@ import { AppService } from './app.service';
|
||||
}),
|
||||
ComputerUseModule,
|
||||
InputTrackingModule,
|
||||
BytebotMcpModule,
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { McpModule } from '@rekog/mcp-nest';
|
||||
import { ComputerUseModule } from '../computer-use/computer-use.module';
|
||||
import { ComputerUseTools } from './computer-use.tools';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ComputerUseModule,
|
||||
McpModule.forRoot({
|
||||
name: 'bytebotd',
|
||||
version: '0.0.1',
|
||||
sseEndpoint: '/sse',
|
||||
}),
|
||||
],
|
||||
providers: [ComputerUseTools],
|
||||
})
|
||||
export class BytebotMcpModule {}
|
||||
@@ -0,0 +1,386 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Tool } from '@rekog/mcp-nest';
|
||||
import { z } from 'zod';
|
||||
import { ComputerUseService } from '../computer-use/computer-use.service';
|
||||
import { MessageContentType } from '@bytebot/shared';
|
||||
|
||||
@Injectable()
|
||||
export class ComputerUseTools {
|
||||
constructor(private readonly computerUse: ComputerUseService) {}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_move_mouse',
|
||||
description: 'Move the mouse to specific coordinates.',
|
||||
parameters: z.object({
|
||||
coordinates: z.object({ x: z.number(), y: z.number() }),
|
||||
}),
|
||||
})
|
||||
async moveMouse({ coordinates }: { coordinates: { x: number; y: number } }) {
|
||||
try {
|
||||
await this.computerUse.action({ action: 'move_mouse', coordinates });
|
||||
return { content: [{ type: MessageContentType.Text, text: 'mouse moved' }] };
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error moving mouse: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_trace_mouse',
|
||||
description: 'Trace a path with the mouse.',
|
||||
parameters: z.object({
|
||||
path: z.array(z.object({ x: z.number(), y: z.number() })),
|
||||
holdKeys: z.array(z.string()).optional(),
|
||||
}),
|
||||
})
|
||||
async traceMouse({
|
||||
path,
|
||||
holdKeys,
|
||||
}: {
|
||||
path: { x: number; y: number }[];
|
||||
holdKeys?: string[];
|
||||
}) {
|
||||
try {
|
||||
await this.computerUse.action({ action: 'trace_mouse', path, holdKeys });
|
||||
return {
|
||||
content: [{ type: MessageContentType.Text, text: 'mouse traced' }],
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error tracing mouse: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_click_mouse',
|
||||
description: 'Perform mouse clicks.',
|
||||
parameters: z.object({
|
||||
coordinates: z.object({ x: z.number(), y: z.number() }).optional(),
|
||||
button: z.enum(['left', 'right', 'middle']),
|
||||
holdKeys: z.array(z.string()).optional(),
|
||||
numClicks: z.number(),
|
||||
}),
|
||||
})
|
||||
async clickMouse({
|
||||
coordinates,
|
||||
button,
|
||||
holdKeys,
|
||||
numClicks,
|
||||
}: {
|
||||
coordinates?: { x: number; y: number };
|
||||
button: 'left' | 'right' | 'middle';
|
||||
holdKeys?: string[];
|
||||
numClicks: number;
|
||||
}) {
|
||||
try {
|
||||
await this.computerUse.action({
|
||||
action: 'click_mouse',
|
||||
coordinates,
|
||||
button,
|
||||
holdKeys,
|
||||
numClicks,
|
||||
});
|
||||
return {
|
||||
content: [{ type: MessageContentType.Text, text: 'mouse clicked' }],
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error clicking mouse: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_press_mouse',
|
||||
description: 'Press or release a mouse button.',
|
||||
parameters: z.object({
|
||||
coordinates: z.object({ x: z.number(), y: z.number() }).optional(),
|
||||
button: z.enum(['left', 'right', 'middle']),
|
||||
press: z.enum(['down', 'up']),
|
||||
}),
|
||||
})
|
||||
async pressMouse({
|
||||
coordinates,
|
||||
button,
|
||||
press,
|
||||
}: {
|
||||
coordinates?: { x: number; y: number };
|
||||
button: 'left' | 'right' | 'middle';
|
||||
press: 'down' | 'up';
|
||||
}) {
|
||||
try {
|
||||
await this.computerUse.action({
|
||||
action: 'press_mouse',
|
||||
coordinates,
|
||||
button,
|
||||
press,
|
||||
});
|
||||
return {
|
||||
content: [{ type: MessageContentType.Text, text: 'mouse pressed' }],
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error pressing mouse: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_drag_mouse',
|
||||
description: 'Drag the mouse along a path.',
|
||||
parameters: z.object({
|
||||
path: z.array(z.object({ x: z.number(), y: z.number() })),
|
||||
button: z.enum(['left', 'right', 'middle']),
|
||||
holdKeys: z.array(z.string()).optional(),
|
||||
}),
|
||||
})
|
||||
async dragMouse({
|
||||
path,
|
||||
button,
|
||||
holdKeys,
|
||||
}: {
|
||||
path: { x: number; y: number }[];
|
||||
button: 'left' | 'right' | 'middle';
|
||||
holdKeys?: string[];
|
||||
}) {
|
||||
try {
|
||||
await this.computerUse.action({
|
||||
action: 'drag_mouse',
|
||||
path,
|
||||
button,
|
||||
holdKeys,
|
||||
});
|
||||
return {
|
||||
content: [{ type: MessageContentType.Text, text: 'mouse dragged' }],
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error dragging mouse: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_scroll',
|
||||
description: 'Scroll the mouse wheel.',
|
||||
parameters: z.object({
|
||||
coordinates: z.object({ x: z.number(), y: z.number() }).optional(),
|
||||
direction: z.enum(['up', 'down', 'left', 'right']),
|
||||
numScrolls: z.number(),
|
||||
holdKeys: z.array(z.string()).optional(),
|
||||
}),
|
||||
})
|
||||
async scroll({
|
||||
coordinates,
|
||||
direction,
|
||||
numScrolls,
|
||||
holdKeys,
|
||||
}: {
|
||||
coordinates?: { x: number; y: number };
|
||||
direction: 'up' | 'down' | 'left' | 'right';
|
||||
numScrolls: number;
|
||||
holdKeys?: string[];
|
||||
}) {
|
||||
try {
|
||||
await this.computerUse.action({
|
||||
action: 'scroll',
|
||||
coordinates,
|
||||
direction,
|
||||
numScrolls,
|
||||
holdKeys,
|
||||
});
|
||||
return { content: [{ type: MessageContentType.Text, text: 'scrolled' }] };
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error scrolling: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_type_keys',
|
||||
description: 'Press a sequence of keys.',
|
||||
parameters: z.object({
|
||||
keys: z.array(z.string()),
|
||||
delay: z.number().optional(),
|
||||
}),
|
||||
})
|
||||
async typeKeys({ keys, delay }: { keys: string[]; delay?: number }) {
|
||||
try {
|
||||
await this.computerUse.action({ action: 'type_keys', keys, delay });
|
||||
return { content: [{ type: MessageContentType.Text, text: 'keys typed' }] };
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error typing keys: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_press_keys',
|
||||
description: 'Hold or release keys.',
|
||||
parameters: z.object({
|
||||
keys: z.array(z.string()),
|
||||
press: z.enum(['down', 'up']),
|
||||
}),
|
||||
})
|
||||
async pressKeys({ keys, press }: { keys: string[]; press: 'down' | 'up' }) {
|
||||
try {
|
||||
await this.computerUse.action({ action: 'press_keys', keys, press });
|
||||
return { content: [{ type: MessageContentType.Text, text: 'keys pressed' }] };
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error pressing keys: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_type_text',
|
||||
description: 'Type a text string.',
|
||||
parameters: z.object({
|
||||
text: z.string(),
|
||||
delay: z.number().optional(),
|
||||
}),
|
||||
})
|
||||
async typeText({ text, delay }: { text: string; delay?: number }) {
|
||||
try {
|
||||
await this.computerUse.action({ action: 'type_text', text, delay });
|
||||
return { content: [{ type: MessageContentType.Text, text: 'text typed' }] };
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error typing text: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_wait',
|
||||
description: 'Wait for a period of time.',
|
||||
parameters: z.object({ duration: z.number() }),
|
||||
})
|
||||
async wait({ duration }: { duration: number }) {
|
||||
try {
|
||||
await this.computerUse.action({ action: 'wait', duration });
|
||||
return { content: [{ type: MessageContentType.Text, text: 'waiting done' }] };
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error waiting: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_screenshot',
|
||||
description: 'Capture a screenshot of the desktop.',
|
||||
})
|
||||
async screenshot() {
|
||||
try {
|
||||
const shot = (await this.computerUse.action({
|
||||
action: 'screenshot',
|
||||
})) as { image: string };
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Image,
|
||||
source: {
|
||||
type: 'base64',
|
||||
media_type: 'image/png',
|
||||
data: shot.image,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error taking screenshot: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Tool({
|
||||
name: 'computer_cursor_position',
|
||||
description: 'Get the current cursor position.',
|
||||
})
|
||||
async cursorPosition() {
|
||||
try {
|
||||
const pos = (await this.computerUse.action({
|
||||
action: 'cursor_position',
|
||||
})) as { x: number; y: number };
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: JSON.stringify(pos),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: MessageContentType.Text,
|
||||
text: `Error getting cursor position: ${(err as Error).message}`,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './bytebot-mcp.module';
|
||||
Reference in New Issue
Block a user