mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 12:44:43 +00:00
mcp: add optional url parameter to tools
This commit is contained in:
@@ -96,7 +96,7 @@ pub const McpServer = struct {
|
|||||||
|
|
||||||
self.queue_mutex.lock();
|
self.queue_mutex.lock();
|
||||||
self.message_queue.append(self.allocator, msg) catch |err| {
|
self.message_queue.append(self.allocator, msg) catch |err| {
|
||||||
lp.log.err(.app, "MCP Error: Failed to queue message", .{ .err = err });
|
lp.log.err(.app, "MCP Queue failed", .{ .err = err });
|
||||||
self.allocator.free(msg);
|
self.allocator.free(msg);
|
||||||
};
|
};
|
||||||
self.queue_mutex.unlock();
|
self.queue_mutex.unlock();
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ pub fn handleRead(server: *McpServer, arena: std.mem.Allocator, req: protocol.Re
|
|||||||
|
|
||||||
if (std.mem.eql(u8, params.uri, "mcp://page/html")) {
|
if (std.mem.eql(u8, params.uri, "mcp://page/html")) {
|
||||||
var aw = std.Io.Writer.Allocating.init(arena);
|
var aw = std.Io.Writer.Allocating.init(arena);
|
||||||
lp.dump.root(server.page.document.asNode(), .{}, &aw.writer, server.page) catch {
|
lp.dump.root(server.page.document, .{}, &aw.writer, server.page) catch {
|
||||||
return sendError(server, req.id.?, -32603, "Internal error reading HTML");
|
return sendError(server, req.id.?, -32603, "Internal error reading HTML");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -40,22 +40,37 @@ pub fn handleList(server: *McpServer, arena: std.mem.Allocator, req: protocol.Re
|
|||||||
},
|
},
|
||||||
.{
|
.{
|
||||||
.name = "markdown",
|
.name = "markdown",
|
||||||
.description = "Get the page content in markdown format.",
|
.description = "Get the page content in markdown format. If a url is provided, it navigates to that url first.",
|
||||||
.inputSchema = std.json.parseFromSliceLeaky(std.json.Value, arena, "{\"type\":\"object\",\"properties\":{}}", .{}) catch unreachable,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.name = "links",
|
|
||||||
.description = "Extract all links in the opened page",
|
|
||||||
.inputSchema = std.json.parseFromSliceLeaky(std.json.Value, arena, "{\"type\":\"object\",\"properties\":{}}", .{}) catch unreachable,
|
|
||||||
},
|
|
||||||
.{
|
|
||||||
.name = "evaluate",
|
|
||||||
.description = "Evaluate JavaScript in the current page context",
|
|
||||||
.inputSchema = std.json.parseFromSliceLeaky(std.json.Value, arena,
|
.inputSchema = std.json.parseFromSliceLeaky(std.json.Value, arena,
|
||||||
\\{
|
\\{
|
||||||
\\ "type": "object",
|
\\ "type": "object",
|
||||||
\\ "properties": {
|
\\ "properties": {
|
||||||
\\ "script": { "type": "string" }
|
\\ "url": { "type": "string", "description": "Optional URL to navigate to before fetching markdown." }
|
||||||
|
\\ }
|
||||||
|
\\}
|
||||||
|
, .{}) catch unreachable,
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.name = "links",
|
||||||
|
.description = "Extract all links in the opened page. If a url is provided, it navigates to that url first.",
|
||||||
|
.inputSchema = std.json.parseFromSliceLeaky(std.json.Value, arena,
|
||||||
|
\\{
|
||||||
|
\\ "type": "object",
|
||||||
|
\\ "properties": {
|
||||||
|
\\ "url": { "type": "string", "description": "Optional URL to navigate to before extracting links." }
|
||||||
|
\\ }
|
||||||
|
\\}
|
||||||
|
, .{}) catch unreachable,
|
||||||
|
},
|
||||||
|
.{
|
||||||
|
.name = "evaluate",
|
||||||
|
.description = "Evaluate JavaScript in the current page context. If a url is provided, it navigates to that url first.",
|
||||||
|
.inputSchema = std.json.parseFromSliceLeaky(std.json.Value, arena,
|
||||||
|
\\{
|
||||||
|
\\ "type": "object",
|
||||||
|
\\ "properties": {
|
||||||
|
\\ "script": { "type": "string" },
|
||||||
|
\\ "url": { "type": "string", "description": "Optional URL to navigate to before evaluating." }
|
||||||
\\ },
|
\\ },
|
||||||
\\ "required": ["script"]
|
\\ "required": ["script"]
|
||||||
\\}
|
\\}
|
||||||
@@ -153,6 +168,18 @@ pub fn handleCall(server: *McpServer, arena: std.mem.Allocator, req: protocol.Re
|
|||||||
const content = [_]struct { type: []const u8, text: []const u8 }{.{ .type = "text", .text = "Search performed successfully." }};
|
const content = [_]struct { type: []const u8, text: []const u8 }{.{ .type = "text", .text = "Search performed successfully." }};
|
||||||
try sendResult(server, req.id.?, .{ .content = &content });
|
try sendResult(server, req.id.?, .{ .content = &content });
|
||||||
} else if (std.mem.eql(u8, call_params.name, "markdown")) {
|
} else if (std.mem.eql(u8, call_params.name, "markdown")) {
|
||||||
|
const MarkdownParams = struct {
|
||||||
|
url: ?[]const u8 = null,
|
||||||
|
};
|
||||||
|
if (call_params.arguments) |args_raw| {
|
||||||
|
if (std.json.parseFromValueLeaky(MarkdownParams, arena, args_raw, .{})) |args| {
|
||||||
|
if (args.url) |u| {
|
||||||
|
performGoto(server, arena, u) catch {
|
||||||
|
return sendError(server, req.id.?, -32603, "Internal error during navigation");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else |_| {}
|
||||||
|
}
|
||||||
var aw = std.Io.Writer.Allocating.init(arena);
|
var aw = std.Io.Writer.Allocating.init(arena);
|
||||||
lp.markdown.dump(server.page.document.asNode(), .{}, &aw.writer, server.page) catch {
|
lp.markdown.dump(server.page.document.asNode(), .{}, &aw.writer, server.page) catch {
|
||||||
return sendError(server, req.id.?, -32603, "Internal error parsing markdown");
|
return sendError(server, req.id.?, -32603, "Internal error parsing markdown");
|
||||||
@@ -161,6 +188,18 @@ pub fn handleCall(server: *McpServer, arena: std.mem.Allocator, req: protocol.Re
|
|||||||
const content = [_]struct { type: []const u8, text: []const u8 }{.{ .type = "text", .text = aw.written() }};
|
const content = [_]struct { type: []const u8, text: []const u8 }{.{ .type = "text", .text = aw.written() }};
|
||||||
try sendResult(server, req.id.?, .{ .content = &content });
|
try sendResult(server, req.id.?, .{ .content = &content });
|
||||||
} else if (std.mem.eql(u8, call_params.name, "links")) {
|
} else if (std.mem.eql(u8, call_params.name, "links")) {
|
||||||
|
const LinksParams = struct {
|
||||||
|
url: ?[]const u8 = null,
|
||||||
|
};
|
||||||
|
if (call_params.arguments) |args_raw| {
|
||||||
|
if (std.json.parseFromValueLeaky(LinksParams, arena, args_raw, .{})) |args| {
|
||||||
|
if (args.url) |u| {
|
||||||
|
performGoto(server, arena, u) catch {
|
||||||
|
return sendError(server, req.id.?, -32603, "Internal error during navigation");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else |_| {}
|
||||||
|
}
|
||||||
const list = Selector.querySelectorAll(server.page.document.asNode(), "a[href]", server.page) catch {
|
const list = Selector.querySelectorAll(server.page.document.asNode(), "a[href]", server.page) catch {
|
||||||
return sendError(server, req.id.?, -32603, "Internal error querying selector");
|
return sendError(server, req.id.?, -32603, "Internal error querying selector");
|
||||||
};
|
};
|
||||||
@@ -183,10 +222,22 @@ pub fn handleCall(server: *McpServer, arena: std.mem.Allocator, req: protocol.Re
|
|||||||
if (call_params.arguments == null) {
|
if (call_params.arguments == null) {
|
||||||
return sendError(server, req.id.?, -32602, "Missing arguments for evaluate");
|
return sendError(server, req.id.?, -32602, "Missing arguments for evaluate");
|
||||||
}
|
}
|
||||||
const args = std.json.parseFromValueLeaky(EvaluateParams, arena, call_params.arguments.?, .{}) catch {
|
|
||||||
|
const EvaluateParamsEx = struct {
|
||||||
|
script: []const u8,
|
||||||
|
url: ?[]const u8 = null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const args = std.json.parseFromValueLeaky(EvaluateParamsEx, arena, call_params.arguments.?, .{}) catch {
|
||||||
return sendError(server, req.id.?, -32602, "Invalid arguments for evaluate");
|
return sendError(server, req.id.?, -32602, "Invalid arguments for evaluate");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (args.url) |url| {
|
||||||
|
performGoto(server, arena, url) catch {
|
||||||
|
return sendError(server, req.id.?, -32603, "Internal error during navigation");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
var ls: js.Local.Scope = undefined;
|
var ls: js.Local.Scope = undefined;
|
||||||
server.page.js.localScope(&ls);
|
server.page.js.localScope(&ls);
|
||||||
defer ls.deinit();
|
defer ls.deinit();
|
||||||
|
|||||||
Reference in New Issue
Block a user