-
Notifications
You must be signed in to change notification settings - Fork 936
Expand file tree
/
Copy pathmcp-tools.ts
More file actions
105 lines (93 loc) · 3.15 KB
/
mcp-tools.ts
File metadata and controls
105 lines (93 loc) · 3.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import { ScreenshotItem, z } from '@midscene/core';
import { BaseMidsceneTools, type ToolDefinition } from '@midscene/shared/mcp';
import { AgentOverChromeBridge } from './bridge-mode';
import { StaticPage } from './static';
/**
* Tools manager for Web bridge-mode MCP
*/
export class WebMidsceneTools extends BaseMidsceneTools<AgentOverChromeBridge> {
protected createTemporaryDevice() {
// Use require to avoid type incompatibility with DeviceAction vs ActionSpaceItem
// StaticPage.actionSpace() returns DeviceAction[] which is compatible at runtime
// Use screenshotBase64 field to avoid async ScreenshotItem.create()
return new StaticPage({
screenshot: ScreenshotItem.create('', Date.now()),
shotSize: { width: 1920, height: 1080 },
shrunkShotToLogicalRatio: 1,
});
}
protected async ensureAgent(
openNewTabWithUrl?: string,
): Promise<AgentOverChromeBridge> {
// Re-init if URL provided
if (this.agent && openNewTabWithUrl) {
try {
await this.agent?.destroy?.();
} catch (error) {
console.debug('Failed to destroy agent during re-init:', error);
}
this.agent = undefined;
}
if (this.agent) return this.agent;
// Connect to current tab when no URL provided (handles CLI stateless calls)
this.agent = await this.initBridgeModeAgent(openNewTabWithUrl);
return this.agent;
}
private async initBridgeModeAgent(
url?: string,
): Promise<AgentOverChromeBridge> {
const agent = new AgentOverChromeBridge({
closeConflictServer: true,
reportFileName: this.getPersistedReportFileName(),
});
if (!url) {
await agent.connectCurrentTab();
} else {
await agent.connectNewTabWithUrl(url);
}
this.persistAgentReportFileName(agent);
return agent;
}
protected preparePlatformTools(): ToolDefinition[] {
return [
{
name: 'web_connect',
description:
'Connect to web page. If URL provided, opens new tab; otherwise connects to current tab.',
schema: {
url: z
.string()
.url()
.optional()
.describe('URL to open in new tab (omit to connect current tab)'),
},
handler: async (args) => {
const { url } = args as { url?: string };
// Bypass ensureAgent's URL check — directly init bridge agent
if (this.agent) {
try {
await this.agent.destroy?.();
} catch {}
this.agent = undefined;
}
this.agent = await this.initBridgeModeAgent(url);
const screenshot = await this.agent.page?.screenshotBase64();
const label = url ?? 'current tab';
return {
content: [
{ type: 'text', text: `Connected to: ${label}` },
...(screenshot ? this.buildScreenshotContent(screenshot) : []),
],
};
},
},
{
name: 'web_disconnect',
description:
'Disconnect from current web page and release browser resources',
schema: {},
handler: this.createDisconnectHandler('web page'),
},
];
}
}