@playwright/mcp 0.0.31 → 0.0.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/README.md +27 -6
  2. package/config.d.ts +5 -0
  3. package/index.d.ts +1 -6
  4. package/lib/browserContextFactory.js +64 -54
  5. package/lib/browserServerBackend.js +121 -0
  6. package/lib/config.js +10 -9
  7. package/lib/context.js +107 -182
  8. package/lib/extension/cdpRelay.js +346 -0
  9. package/lib/extension/extensionContextFactory.js +56 -0
  10. package/lib/extension/main.js +26 -0
  11. package/lib/httpServer.js +20 -182
  12. package/lib/index.js +6 -3
  13. package/lib/loop/loop.js +69 -0
  14. package/lib/loop/loopClaude.js +152 -0
  15. package/lib/loop/loopOpenAI.js +141 -0
  16. package/lib/loop/main.js +60 -0
  17. package/lib/loopTools/context.js +66 -0
  18. package/lib/loopTools/main.js +49 -0
  19. package/lib/loopTools/perform.js +32 -0
  20. package/lib/loopTools/snapshot.js +29 -0
  21. package/lib/loopTools/tool.js +18 -0
  22. package/lib/mcp/inProcessTransport.js +72 -0
  23. package/lib/mcp/server.js +93 -0
  24. package/lib/{transport.js → mcp/transport.js} +30 -42
  25. package/lib/package.js +3 -3
  26. package/lib/program.js +39 -9
  27. package/lib/response.js +165 -0
  28. package/lib/sessionLog.js +121 -0
  29. package/lib/tab.js +138 -24
  30. package/lib/tools/common.js +10 -23
  31. package/lib/tools/console.js +4 -15
  32. package/lib/tools/dialogs.js +12 -17
  33. package/lib/tools/evaluate.js +12 -21
  34. package/lib/tools/files.js +9 -16
  35. package/lib/tools/install.js +3 -7
  36. package/lib/tools/keyboard.js +28 -42
  37. package/lib/tools/mouse.js +27 -50
  38. package/lib/tools/navigate.js +12 -35
  39. package/lib/tools/network.js +5 -15
  40. package/lib/tools/pdf.js +7 -16
  41. package/lib/tools/screenshot.js +35 -33
  42. package/lib/tools/snapshot.js +44 -69
  43. package/lib/tools/tabs.js +10 -41
  44. package/lib/tools/tool.js +15 -0
  45. package/lib/tools/utils.js +2 -9
  46. package/lib/tools/wait.js +3 -6
  47. package/lib/tools.js +3 -0
  48. package/lib/utils.js +26 -0
  49. package/package.json +11 -6
  50. package/lib/connection.js +0 -81
  51. package/lib/pageSnapshot.js +0 -43
  52. package/lib/server.js +0 -48
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import debug from 'debug';
17
+ export async function runTask(delegate, client, task, oneShot = false) {
18
+ const { tools } = await client.listTools();
19
+ const taskContent = oneShot ? `Perform following task: ${task}.` : `Perform following task: ${task}. Once the task is complete, call the "done" tool.`;
20
+ const conversation = delegate.createConversation(taskContent, tools, oneShot);
21
+ for (let iteration = 0; iteration < 5; ++iteration) {
22
+ debug('history')('Making API call for iteration', iteration);
23
+ const toolCalls = await delegate.makeApiCall(conversation);
24
+ if (toolCalls.length === 0)
25
+ throw new Error('Call the "done" tool when the task is complete.');
26
+ const toolResults = [];
27
+ for (const toolCall of toolCalls) {
28
+ const doneResult = delegate.checkDoneToolCall(toolCall);
29
+ if (doneResult !== null)
30
+ return conversation.messages;
31
+ const { name, arguments: args, id } = toolCall;
32
+ try {
33
+ debug('tool')(name, args);
34
+ const response = await client.callTool({
35
+ name,
36
+ arguments: args,
37
+ });
38
+ const responseContent = (response.content || []);
39
+ debug('tool')(responseContent);
40
+ const text = responseContent.filter(part => part.type === 'text').map(part => part.text).join('\n');
41
+ toolResults.push({
42
+ toolCallId: id,
43
+ content: text,
44
+ });
45
+ }
46
+ catch (error) {
47
+ debug('tool')(error);
48
+ toolResults.push({
49
+ toolCallId: id,
50
+ content: `Error while executing tool "${name}": ${error instanceof Error ? error.message : String(error)}\n\nPlease try to recover and complete the task.`,
51
+ isError: true,
52
+ });
53
+ // Skip remaining tool calls for this iteration
54
+ for (const remainingToolCall of toolCalls.slice(toolCalls.indexOf(toolCall) + 1)) {
55
+ toolResults.push({
56
+ toolCallId: remainingToolCall.id,
57
+ content: `This tool call is skipped due to previous error.`,
58
+ isError: true,
59
+ });
60
+ }
61
+ break;
62
+ }
63
+ }
64
+ delegate.addToolResults(conversation, toolResults);
65
+ if (oneShot)
66
+ return conversation.messages;
67
+ }
68
+ throw new Error('Failed to perform step, max attempts reached');
69
+ }
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ const model = 'claude-sonnet-4-20250514';
17
+ export class ClaudeDelegate {
18
+ _anthropic;
19
+ async anthropic() {
20
+ if (!this._anthropic) {
21
+ const anthropic = await import('@anthropic-ai/sdk');
22
+ this._anthropic = new anthropic.Anthropic();
23
+ }
24
+ return this._anthropic;
25
+ }
26
+ createConversation(task, tools, oneShot) {
27
+ const llmTools = tools.map(tool => ({
28
+ name: tool.name,
29
+ description: tool.description || '',
30
+ inputSchema: tool.inputSchema,
31
+ }));
32
+ if (!oneShot) {
33
+ llmTools.push({
34
+ name: 'done',
35
+ description: 'Call this tool when the task is complete.',
36
+ inputSchema: {
37
+ type: 'object',
38
+ properties: {},
39
+ },
40
+ });
41
+ }
42
+ return {
43
+ messages: [{
44
+ role: 'user',
45
+ content: task
46
+ }],
47
+ tools: llmTools,
48
+ };
49
+ }
50
+ async makeApiCall(conversation) {
51
+ // Convert generic messages to Claude format
52
+ const claudeMessages = [];
53
+ for (const message of conversation.messages) {
54
+ if (message.role === 'user') {
55
+ claudeMessages.push({
56
+ role: 'user',
57
+ content: message.content
58
+ });
59
+ }
60
+ else if (message.role === 'assistant') {
61
+ const content = [];
62
+ // Add text content
63
+ if (message.content) {
64
+ content.push({
65
+ type: 'text',
66
+ text: message.content,
67
+ citations: []
68
+ });
69
+ }
70
+ // Add tool calls
71
+ if (message.toolCalls) {
72
+ for (const toolCall of message.toolCalls) {
73
+ content.push({
74
+ type: 'tool_use',
75
+ id: toolCall.id,
76
+ name: toolCall.name,
77
+ input: toolCall.arguments
78
+ });
79
+ }
80
+ }
81
+ claudeMessages.push({
82
+ role: 'assistant',
83
+ content
84
+ });
85
+ }
86
+ else if (message.role === 'tool') {
87
+ // Tool results are added differently - we need to find if there's already a user message with tool results
88
+ const lastMessage = claudeMessages[claudeMessages.length - 1];
89
+ const toolResult = {
90
+ type: 'tool_result',
91
+ tool_use_id: message.toolCallId,
92
+ content: message.content,
93
+ is_error: message.isError,
94
+ };
95
+ if (lastMessage && lastMessage.role === 'user' && Array.isArray(lastMessage.content)) {
96
+ // Add to existing tool results message
97
+ lastMessage.content.push(toolResult);
98
+ }
99
+ else {
100
+ // Create new tool results message
101
+ claudeMessages.push({
102
+ role: 'user',
103
+ content: [toolResult]
104
+ });
105
+ }
106
+ }
107
+ }
108
+ // Convert generic tools to Claude format
109
+ const claudeTools = conversation.tools.map(tool => ({
110
+ name: tool.name,
111
+ description: tool.description,
112
+ input_schema: tool.inputSchema,
113
+ }));
114
+ const anthropic = await this.anthropic();
115
+ const response = await anthropic.messages.create({
116
+ model,
117
+ max_tokens: 10000,
118
+ messages: claudeMessages,
119
+ tools: claudeTools,
120
+ });
121
+ // Extract tool calls and add assistant message to generic conversation
122
+ const toolCalls = response.content.filter(block => block.type === 'tool_use');
123
+ const textContent = response.content.filter(block => block.type === 'text').map(block => block.text).join('');
124
+ const llmToolCalls = toolCalls.map(toolCall => ({
125
+ name: toolCall.name,
126
+ arguments: toolCall.input,
127
+ id: toolCall.id,
128
+ }));
129
+ // Add assistant message to generic conversation
130
+ conversation.messages.push({
131
+ role: 'assistant',
132
+ content: textContent,
133
+ toolCalls: llmToolCalls.length > 0 ? llmToolCalls : undefined
134
+ });
135
+ return llmToolCalls;
136
+ }
137
+ addToolResults(conversation, results) {
138
+ for (const result of results) {
139
+ conversation.messages.push({
140
+ role: 'tool',
141
+ toolCallId: result.toolCallId,
142
+ content: result.content,
143
+ isError: result.isError,
144
+ });
145
+ }
146
+ }
147
+ checkDoneToolCall(toolCall) {
148
+ if (toolCall.name === 'done')
149
+ return toolCall.arguments.result;
150
+ return null;
151
+ }
152
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ const model = 'gpt-4.1';
17
+ export class OpenAIDelegate {
18
+ _openai;
19
+ async openai() {
20
+ if (!this._openai) {
21
+ const oai = await import('openai');
22
+ this._openai = new oai.OpenAI();
23
+ }
24
+ return this._openai;
25
+ }
26
+ createConversation(task, tools, oneShot) {
27
+ const genericTools = tools.map(tool => ({
28
+ name: tool.name,
29
+ description: tool.description || '',
30
+ inputSchema: tool.inputSchema,
31
+ }));
32
+ if (!oneShot) {
33
+ genericTools.push({
34
+ name: 'done',
35
+ description: 'Call this tool when the task is complete.',
36
+ inputSchema: {
37
+ type: 'object',
38
+ properties: {},
39
+ },
40
+ });
41
+ }
42
+ return {
43
+ messages: [{
44
+ role: 'user',
45
+ content: task
46
+ }],
47
+ tools: genericTools,
48
+ };
49
+ }
50
+ async makeApiCall(conversation) {
51
+ // Convert generic messages to OpenAI format
52
+ const openaiMessages = [];
53
+ for (const message of conversation.messages) {
54
+ if (message.role === 'user') {
55
+ openaiMessages.push({
56
+ role: 'user',
57
+ content: message.content
58
+ });
59
+ }
60
+ else if (message.role === 'assistant') {
61
+ const toolCalls = [];
62
+ if (message.toolCalls) {
63
+ for (const toolCall of message.toolCalls) {
64
+ toolCalls.push({
65
+ id: toolCall.id,
66
+ type: 'function',
67
+ function: {
68
+ name: toolCall.name,
69
+ arguments: JSON.stringify(toolCall.arguments)
70
+ }
71
+ });
72
+ }
73
+ }
74
+ const assistantMessage = {
75
+ role: 'assistant'
76
+ };
77
+ if (message.content)
78
+ assistantMessage.content = message.content;
79
+ if (toolCalls.length > 0)
80
+ assistantMessage.tool_calls = toolCalls;
81
+ openaiMessages.push(assistantMessage);
82
+ }
83
+ else if (message.role === 'tool') {
84
+ openaiMessages.push({
85
+ role: 'tool',
86
+ tool_call_id: message.toolCallId,
87
+ content: message.content,
88
+ });
89
+ }
90
+ }
91
+ // Convert generic tools to OpenAI format
92
+ const openaiTools = conversation.tools.map(tool => ({
93
+ type: 'function',
94
+ function: {
95
+ name: tool.name,
96
+ description: tool.description,
97
+ parameters: tool.inputSchema,
98
+ },
99
+ }));
100
+ const openai = await this.openai();
101
+ const response = await openai.chat.completions.create({
102
+ model,
103
+ messages: openaiMessages,
104
+ tools: openaiTools,
105
+ tool_choice: 'auto'
106
+ });
107
+ const message = response.choices[0].message;
108
+ // Extract tool calls and add assistant message to generic conversation
109
+ const toolCalls = message.tool_calls || [];
110
+ const genericToolCalls = toolCalls.map(toolCall => {
111
+ const functionCall = toolCall.function;
112
+ return {
113
+ name: functionCall.name,
114
+ arguments: JSON.parse(functionCall.arguments),
115
+ id: toolCall.id,
116
+ };
117
+ });
118
+ // Add assistant message to generic conversation
119
+ conversation.messages.push({
120
+ role: 'assistant',
121
+ content: message.content || '',
122
+ toolCalls: genericToolCalls.length > 0 ? genericToolCalls : undefined
123
+ });
124
+ return genericToolCalls;
125
+ }
126
+ addToolResults(conversation, results) {
127
+ for (const result of results) {
128
+ conversation.messages.push({
129
+ role: 'tool',
130
+ toolCallId: result.toolCallId,
131
+ content: result.content,
132
+ isError: result.isError,
133
+ });
134
+ }
135
+ }
136
+ checkDoneToolCall(toolCall) {
137
+ if (toolCall.name === 'done')
138
+ return toolCall.arguments.result;
139
+ return null;
140
+ }
141
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ /* eslint-disable no-console */
17
+ import path from 'path';
18
+ import url from 'url';
19
+ import dotenv from 'dotenv';
20
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
21
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
22
+ import { program } from 'commander';
23
+ import { OpenAIDelegate } from './loopOpenAI.js';
24
+ import { ClaudeDelegate } from './loopClaude.js';
25
+ import { runTask } from './loop.js';
26
+ dotenv.config();
27
+ const __filename = url.fileURLToPath(import.meta.url);
28
+ async function run(delegate) {
29
+ const transport = new StdioClientTransport({
30
+ command: 'node',
31
+ args: [
32
+ path.resolve(__filename, '../../../cli.js'),
33
+ '--save-session',
34
+ '--output-dir', path.resolve(__filename, '../../../sessions')
35
+ ],
36
+ stderr: 'inherit',
37
+ env: process.env,
38
+ });
39
+ const client = new Client({ name: 'test', version: '1.0.0' });
40
+ await client.connect(transport);
41
+ await client.ping();
42
+ for (const task of tasks) {
43
+ const messages = await runTask(delegate, client, task);
44
+ for (const message of messages)
45
+ console.log(`${message.role}: ${message.content}`);
46
+ }
47
+ await client.close();
48
+ }
49
+ const tasks = [
50
+ 'Open https://playwright.dev/',
51
+ ];
52
+ program
53
+ .option('--model <model>', 'model to use')
54
+ .action(async (options) => {
55
+ if (options.model === 'claude')
56
+ await run(new ClaudeDelegate());
57
+ else
58
+ await run(new OpenAIDelegate());
59
+ });
60
+ void program.parseAsync(process.argv);
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
17
+ import { contextFactory } from '../browserContextFactory.js';
18
+ import { BrowserServerBackend } from '../browserServerBackend.js';
19
+ import { Context as BrowserContext } from '../context.js';
20
+ import { runTask } from '../loop/loop.js';
21
+ import { OpenAIDelegate } from '../loop/loopOpenAI.js';
22
+ import { ClaudeDelegate } from '../loop/loopClaude.js';
23
+ import { InProcessTransport } from '../mcp/inProcessTransport.js';
24
+ import * as mcpServer from '../mcp/server.js';
25
+ export class Context {
26
+ config;
27
+ _client;
28
+ _delegate;
29
+ constructor(config, client) {
30
+ this.config = config;
31
+ this._client = client;
32
+ if (process.env.OPENAI_API_KEY)
33
+ this._delegate = new OpenAIDelegate();
34
+ else if (process.env.ANTHROPIC_API_KEY)
35
+ this._delegate = new ClaudeDelegate();
36
+ else
37
+ throw new Error('No LLM API key found. Please set OPENAI_API_KEY or ANTHROPIC_API_KEY environment variable.');
38
+ }
39
+ static async create(config) {
40
+ const client = new Client({ name: 'Playwright Proxy', version: '1.0.0' });
41
+ const browserContextFactory = contextFactory(config);
42
+ const server = mcpServer.createServer(new BrowserServerBackend(config, [browserContextFactory]), false);
43
+ await client.connect(new InProcessTransport(server));
44
+ await client.ping();
45
+ return new Context(config, client);
46
+ }
47
+ async runTask(task, oneShot = false) {
48
+ const messages = await runTask(this._delegate, this._client, task, oneShot);
49
+ const lines = [];
50
+ // Skip the first message, which is the user's task.
51
+ for (const message of messages.slice(1)) {
52
+ // Trim out all page snapshots.
53
+ if (!message.content.trim())
54
+ continue;
55
+ const index = oneShot ? -1 : message.content.indexOf('### Page state');
56
+ const trimmedContent = index === -1 ? message.content : message.content.substring(0, index);
57
+ lines.push(`[${message.role}]:`, trimmedContent);
58
+ }
59
+ return {
60
+ content: [{ type: 'text', text: lines.join('\n') }],
61
+ };
62
+ }
63
+ async close() {
64
+ await BrowserContext.disposeAll();
65
+ }
66
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import dotenv from 'dotenv';
17
+ import * as mcpTransport from '../mcp/transport.js';
18
+ import { packageJSON } from '../package.js';
19
+ import { Context } from './context.js';
20
+ import { perform } from './perform.js';
21
+ import { snapshot } from './snapshot.js';
22
+ export async function runLoopTools(config) {
23
+ dotenv.config();
24
+ const serverBackendFactory = () => new LoopToolsServerBackend(config);
25
+ await mcpTransport.start(serverBackendFactory, config.server);
26
+ }
27
+ class LoopToolsServerBackend {
28
+ name = 'Playwright';
29
+ version = packageJSON.version;
30
+ _config;
31
+ _context;
32
+ _tools = [perform, snapshot];
33
+ constructor(config) {
34
+ this._config = config;
35
+ }
36
+ async initialize() {
37
+ this._context = await Context.create(this._config);
38
+ }
39
+ tools() {
40
+ return this._tools.map(tool => tool.schema);
41
+ }
42
+ async callTool(schema, parsedArguments) {
43
+ const tool = this._tools.find(tool => tool.schema.name === schema.name);
44
+ return await tool.handle(this._context, parsedArguments);
45
+ }
46
+ serverClosed() {
47
+ void this._context.close();
48
+ }
49
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { z } from 'zod';
17
+ import { defineTool } from './tool.js';
18
+ const performSchema = z.object({
19
+ task: z.string().describe('The task to perform with the browser'),
20
+ });
21
+ export const perform = defineTool({
22
+ schema: {
23
+ name: 'browser_perform',
24
+ title: 'Perform a task with the browser',
25
+ description: 'Perform a task with the browser. It can click, type, export, capture screenshot, drag, hover, select options, etc.',
26
+ inputSchema: performSchema,
27
+ type: 'destructive',
28
+ },
29
+ handle: async (context, params) => {
30
+ return await context.runTask(params.task);
31
+ },
32
+ });
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { z } from 'zod';
17
+ import { defineTool } from './tool.js';
18
+ export const snapshot = defineTool({
19
+ schema: {
20
+ name: 'browser_snapshot',
21
+ title: 'Take a snapshot of the browser',
22
+ description: 'Take a snapshot of the browser to read what is on the page.',
23
+ inputSchema: z.object({}),
24
+ type: 'readOnly',
25
+ },
26
+ handle: async (context, params) => {
27
+ return await context.runTask('Capture browser snapshot', true);
28
+ },
29
+ });
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ export function defineTool(tool) {
17
+ return tool;
18
+ }