Skip to content

Commit 538f8ac

Browse files
committed
chore: experimental tool provider
1 parent 7bb701e commit 538f8ac

File tree

8 files changed

+127
-4
lines changed

8 files changed

+127
-4
lines changed

cloudflare/.npmignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77

88
# Include playwright core and test entry points
99
!index.d.ts
10+
!experimental.d.ts

cloudflare/experimental.d.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { Browser, BrowserEndpoint } from '@cloudflare/playwright';
2+
import { ToolSet } from 'ai';
3+
import { ToolCapability } from './index.js';
4+
5+
/**
6+
* ToolsProvider interface for providing Playwright MCP tools.
7+
*/
8+
export interface ToolsProvider {
9+
/**
10+
* Returns a ToolSet containing all available tools mapped from the context
11+
* Each tool includes parameters, description, and execute function
12+
*/
13+
tools(): ToolSet;
14+
15+
/**
16+
* Closes the underlying browser context and cleans up resources
17+
*/
18+
close(): Promise<void>;
19+
20+
[Symbol.asyncDispose](): Promise<void>;
21+
}
22+
23+
export declare function createToolsProvider(endpoint: BrowserEndpoint | Browser, options?: { vision?: boolean; capabilities?: ToolCapability[] }): Promise<ToolsProvider>;

cloudflare/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { env } from 'cloudflare:workers';
1919
import { McpAgent } from 'agents/mcp';
2020
import { BrowserEndpoint } from '@cloudflare/playwright';
2121

22-
type ToolCapability = 'core' | 'tabs' | 'pdf' | 'history' | 'wait' | 'files';
22+
export type ToolCapability = 'core' | 'tabs' | 'pdf' | 'history' | 'wait' | 'files';
2323

2424
type Options = {
2525
/**

cloudflare/package-lock.json

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cloudflare/package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@
2222
"import": "./lib/esm/index.js",
2323
"require": "./lib/cjs/index.js",
2424
"default": "./lib/esm/index.js"
25+
},
26+
"./experimental": {
27+
"types": "./experimental.d.ts",
28+
"import": "./lib/esm/experimental.js",
29+
"require": "./lib/cjs/experimental.js",
30+
"default": "./lib/esm/experimental.js"
2531
}
2632
},
2733
"dependencies": {
@@ -34,5 +40,8 @@
3440
"devDependencies": {
3541
"@cloudflare/workers-types": "^4.20250725.0",
3642
"vite": "^7.0.6"
43+
},
44+
"peerDependencies": {
45+
"ai": "^4.3.19"
3746
}
3847
}

cloudflare/src/experimental.ts

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { Browser, BrowserEndpoint, endpointURLString } from '@cloudflare/playwright';
2+
import type { ToolCapability } from '../../config';
3+
import { Context } from '../../src/context.js';
4+
import { snapshotTools, visionTools } from '../../src/tools.js';
5+
import { FullConfig, resolveConfig } from '../../src/config.js';
6+
import { BrowserContextFactory, contextFactory } from '../../src/browserContextFactory.js';
7+
import type { ToolSet } from 'ai';
8+
import { ToolsProvider } from '../experimental.js';
9+
10+
class ToolsProviderImpl implements ToolsProvider {
11+
private context: Context;
12+
private _tools: ToolSet;
13+
14+
constructor(context: Context) {
15+
this.context = context;
16+
}
17+
18+
tools(): ToolSet {
19+
if (!this._tools) {
20+
this._tools = Object.fromEntries(
21+
this.context.tools.map(tool => [
22+
tool.schema.name,
23+
{
24+
parameters: tool.schema.inputSchema,
25+
description: tool.schema.description,
26+
execute: async (args: any) => {
27+
return await this.context.run(tool, args);
28+
},
29+
},
30+
])
31+
);
32+
}
33+
return this._tools;
34+
}
35+
36+
async close() {
37+
await this.context.close();
38+
}
39+
40+
async [Symbol.asyncDispose]() {
41+
await this.close();
42+
}
43+
}
44+
45+
function isBrowser(browser: BrowserEndpoint | Browser): browser is Browser {
46+
return (
47+
typeof (browser as Browser).newPage === 'function' &&
48+
typeof (browser as Browser).newContext === 'function' &&
49+
// Fetcher may match the previous ones
50+
typeof (browser as Browser)[Symbol.asyncDispose] === 'function'
51+
);
52+
}
53+
54+
export async function createToolsProvider(endpoint: BrowserEndpoint | Browser, options?: { vision?: boolean, capabilities?: ToolCapability[] }): Promise<ToolsProvider> {
55+
const allTools = options?.vision ? visionTools : snapshotTools;
56+
const tools = options?.capabilities ? allTools.filter(tool => options.capabilities.includes(tool.capability)) : allTools;
57+
let config: FullConfig;
58+
let browserContextFactory: BrowserContextFactory;
59+
60+
if (isBrowser(endpoint)) {
61+
config = await resolveConfig({});
62+
browserContextFactory = {
63+
createContext: async () => {
64+
const browserContext = await endpoint.newContext();
65+
return { browserContext, close: () => browserContext.close() };
66+
},
67+
} as BrowserContextFactory;
68+
} else {
69+
const cdpEndpoint = typeof endpoint === 'string'
70+
? endpoint
71+
: endpoint instanceof URL
72+
? endpoint.toString()
73+
: endpointURLString(endpoint);
74+
config = await resolveConfig({
75+
browser: {
76+
cdpEndpoint,
77+
},
78+
});
79+
browserContextFactory = contextFactory(config.browser);
80+
}
81+
82+
const context = new Context(tools, config, browserContextFactory);
83+
return new ToolsProviderImpl(context);
84+
}

cloudflare/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { ToolCapability } from '../../config.js';
99

1010
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
1111

12+
export { createToolsProvider as experimental_createToolsProvider } from './experimental.js';
13+
1214
type Options = {
1315
vision?: boolean;
1416
capabilities?: ToolCapability[];

cloudflare/vite.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export default defineConfig({
4747
name: '@cloudflare/playwright',
4848
entry: [
4949
path.resolve(__dirname, './src/index.ts'),
50+
path.resolve(__dirname, './src/experimental.ts'),
5051
],
5152
},
5253
// prevents __defProp, __defNormalProp, __publicField in compiled code

0 commit comments

Comments
 (0)