Skip to content

Commit 9cd7a3a

Browse files
authored
Merge pull request #111 from kernel/browser-utilities-mcp-tool
Add browser utility tools to MCP
2 parents 56137f0 + a89ee11 commit 9cd7a3a

3 files changed

Lines changed: 350 additions & 180 deletions

File tree

src/lib/mcp/register.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { registerKernelPrompts } from "@/lib/mcp/prompts";
33
import { registerAPIKeyCapabilities } from "@/lib/mcp/tools/api-keys";
44
import { registerAppCapabilities } from "@/lib/mcp/tools/apps";
55
import { registerBrowserPoolCapabilities } from "@/lib/mcp/tools/browser-pools";
6+
import { registerBrowserCurlTool } from "@/lib/mcp/tools/browser-curl";
67
import { registerBrowserCapabilities } from "@/lib/mcp/tools/browsers";
78
import { registerComputerActionTool } from "@/lib/mcp/tools/computer-action";
89
import { registerDocsTools } from "@/lib/mcp/tools/docs";
@@ -22,6 +23,7 @@ const mcpToolRegistrations = [
2223
["projects", registerProjectCapabilities],
2324
["api_keys", registerAPIKeyCapabilities],
2425
["browser_pools", registerBrowserPoolCapabilities],
26+
["browser_curl", registerBrowserCurlTool],
2527
["proxies", registerProxyTools],
2628
["extensions", registerExtensionTools],
2729
["apps", registerAppCapabilities],

src/lib/mcp/tools/browser-curl.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2+
import { z } from "zod";
3+
import { createKernelClient, type KernelClient } from "@/lib/mcp/kernel-client";
4+
import { errorResponse, jsonResponse } from "@/lib/mcp/responses";
5+
6+
type BrowserCurlParams = Parameters<KernelClient["browsers"]["curl"]>[1];
7+
8+
function curlUrlValidationError(url: string) {
9+
const parsed = new URL(url);
10+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
11+
return "url must use http or https.";
12+
}
13+
}
14+
15+
export function registerBrowserCurlTool(server: McpServer) {
16+
server.tool(
17+
"browser_curl",
18+
"Send an HTTP request through an existing Kernel browser session's Chrome network stack.",
19+
{
20+
session_id: z.string().describe("Browser session ID."),
21+
url: z.string().url().describe("Target http or https URL."),
22+
method: z
23+
.enum(["GET", "HEAD", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"])
24+
.describe("HTTP method. Defaults to GET.")
25+
.optional(),
26+
headers: z
27+
.record(z.string(), z.string())
28+
.describe("Custom headers merged with browser defaults.")
29+
.optional(),
30+
body: z
31+
.string()
32+
.describe("Request body for POST, PUT, or PATCH requests.")
33+
.optional(),
34+
response_encoding: z
35+
.enum(["utf8", "base64"])
36+
.describe("Response body encoding. Use base64 for binary content.")
37+
.optional(),
38+
timeout_ms: z
39+
.number()
40+
.int()
41+
.describe("Request timeout in milliseconds.")
42+
.optional(),
43+
},
44+
async (params, extra) => {
45+
if (!extra.authInfo) throw new Error("Authentication required");
46+
const client = createKernelClient(extra.authInfo.token);
47+
48+
try {
49+
const curlRequest: { session_id: string } & BrowserCurlParams = params;
50+
const { session_id, ...curlParams } = curlRequest;
51+
const urlError = curlUrlValidationError(curlParams.url);
52+
if (urlError) return errorResponse(`Error: ${urlError}`);
53+
54+
const response = await client.browsers.curl(session_id, curlParams);
55+
return jsonResponse(response);
56+
} catch (error) {
57+
return errorResponse(
58+
`Error in browser_curl: ${
59+
error instanceof Error ? error.message : String(error)
60+
}`,
61+
);
62+
}
63+
},
64+
);
65+
}

0 commit comments

Comments
 (0)