mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-28 15:40:04 +00:00
@@ -22,12 +22,14 @@ pub const resource_list = [_]protocol.Resource{
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn handleList(server: *Server, req: protocol.Request) !void {
|
pub fn handleList(server: *Server, req: protocol.Request) !void {
|
||||||
try server.sendResult(req.id.?, .{ .resources = &resource_list });
|
const id = req.id orelse return;
|
||||||
|
try server.sendResult(id, .{ .resources = &resource_list });
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReadParams = struct {
|
const ReadParams = struct {
|
||||||
uri: []const u8,
|
uri: []const u8,
|
||||||
};
|
};
|
||||||
|
const Format = enum { html, markdown };
|
||||||
|
|
||||||
const ResourceStreamingResult = struct {
|
const ResourceStreamingResult = struct {
|
||||||
contents: []const struct {
|
contents: []const struct {
|
||||||
@@ -38,7 +40,7 @@ const ResourceStreamingResult = struct {
|
|||||||
|
|
||||||
const StreamingText = struct {
|
const StreamingText = struct {
|
||||||
page: *lp.Page,
|
page: *lp.Page,
|
||||||
format: enum { html, markdown },
|
format: Format,
|
||||||
|
|
||||||
pub fn jsonStringify(self: @This(), jw: *std.json.Stringify) !void {
|
pub fn jsonStringify(self: @This(), jw: *std.json.Stringify) !void {
|
||||||
try jw.beginWriteRaw();
|
try jw.beginWriteRaw();
|
||||||
@@ -47,9 +49,11 @@ const ResourceStreamingResult = struct {
|
|||||||
switch (self.format) {
|
switch (self.format) {
|
||||||
.html => lp.dump.root(self.page.document, .{}, &escaped.writer, self.page) catch |err| {
|
.html => lp.dump.root(self.page.document, .{}, &escaped.writer, self.page) catch |err| {
|
||||||
log.err(.mcp, "html dump failed", .{ .err = err });
|
log.err(.mcp, "html dump failed", .{ .err = err });
|
||||||
|
return error.WriteFailed;
|
||||||
},
|
},
|
||||||
.markdown => lp.markdown.dump(self.page.document.asNode(), .{}, &escaped.writer, self.page) catch |err| {
|
.markdown => lp.markdown.dump(self.page.document.asNode(), .{}, &escaped.writer, self.page) catch |err| {
|
||||||
log.err(.mcp, "markdown dump failed", .{ .err = err });
|
log.err(.mcp, "markdown dump failed", .{ .err = err });
|
||||||
|
return error.WriteFailed;
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
try jw.writer.writeByte('"');
|
try jw.writer.writeByte('"');
|
||||||
@@ -86,28 +90,25 @@ pub fn handleRead(server: *Server, arena: std.mem.Allocator, req: protocol.Reque
|
|||||||
return server.sendError(req_id, .PageNotLoaded, "Page not loaded");
|
return server.sendError(req_id, .PageNotLoaded, "Page not loaded");
|
||||||
};
|
};
|
||||||
|
|
||||||
switch (uri) {
|
const format: Format = switch (uri) {
|
||||||
.@"mcp://page/html" => {
|
.@"mcp://page/html" => .html,
|
||||||
|
.@"mcp://page/markdown" => .markdown,
|
||||||
|
};
|
||||||
|
const mime_type: []const u8 = switch (uri) {
|
||||||
|
.@"mcp://page/html" => "text/html",
|
||||||
|
.@"mcp://page/markdown" => "text/markdown",
|
||||||
|
};
|
||||||
|
|
||||||
const result: ResourceStreamingResult = .{
|
const result: ResourceStreamingResult = .{
|
||||||
.contents = &.{.{
|
.contents = &.{.{
|
||||||
.uri = params.uri,
|
.uri = params.uri,
|
||||||
.mimeType = "text/html",
|
.mimeType = mime_type,
|
||||||
.text = .{ .page = page, .format = .html },
|
.text = .{ .page = page, .format = format },
|
||||||
}},
|
}},
|
||||||
};
|
};
|
||||||
try server.sendResult(req_id, result);
|
server.sendResult(req_id, result) catch {
|
||||||
},
|
return server.sendError(req_id, .InternalError, "Failed to serialize resource content");
|
||||||
.@"mcp://page/markdown" => {
|
|
||||||
const result: ResourceStreamingResult = .{
|
|
||||||
.contents = &.{.{
|
|
||||||
.uri = params.uri,
|
|
||||||
.mimeType = "text/markdown",
|
|
||||||
.text = .{ .page = page, .format = .markdown },
|
|
||||||
}},
|
|
||||||
};
|
};
|
||||||
try server.sendResult(req_id, result);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const testing = @import("../testing.zig");
|
const testing = @import("../testing.zig");
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub fn processRequests(server: *Server, reader: *std.io.Reader) !void {
|
|||||||
const buffered_line = reader.takeDelimiter('\n') catch |err| switch (err) {
|
const buffered_line = reader.takeDelimiter('\n') catch |err| switch (err) {
|
||||||
error.StreamTooLong => {
|
error.StreamTooLong => {
|
||||||
log.err(.mcp, "Message too long", .{});
|
log.err(.mcp, "Message too long", .{});
|
||||||
|
try server.sendError(.null, .InvalidRequest, "Message too long");
|
||||||
continue;
|
continue;
|
||||||
},
|
},
|
||||||
else => return err,
|
else => return err,
|
||||||
@@ -80,6 +81,7 @@ pub fn handleMessage(server: *Server, arena: std.mem.Allocator, msg: []const u8)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn handleInitialize(server: *Server, req: protocol.Request) !void {
|
fn handleInitialize(server: *Server, req: protocol.Request) !void {
|
||||||
|
const id = req.id orelse return;
|
||||||
const result = protocol.InitializeResult{
|
const result = protocol.InitializeResult{
|
||||||
.protocolVersion = "2025-11-25",
|
.protocolVersion = "2025-11-25",
|
||||||
.capabilities = .{
|
.capabilities = .{
|
||||||
@@ -92,7 +94,7 @@ fn handleInitialize(server: *Server, req: protocol.Request) !void {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
try server.sendResult(req.id.?, result);
|
try server.sendResult(id, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handlePing(server: *Server, req: protocol.Request) !void {
|
fn handlePing(server: *Server, req: protocol.Request) !void {
|
||||||
|
|||||||
@@ -172,7 +172,8 @@ pub const tool_list = [_]protocol.Tool{
|
|||||||
|
|
||||||
pub fn handleList(server: *Server, arena: std.mem.Allocator, req: protocol.Request) !void {
|
pub fn handleList(server: *Server, arena: std.mem.Allocator, req: protocol.Request) !void {
|
||||||
_ = arena;
|
_ = arena;
|
||||||
try server.sendResult(req.id.?, .{ .tools = &tool_list });
|
const id = req.id orelse return;
|
||||||
|
try server.sendResult(id, .{ .tools = &tool_list });
|
||||||
}
|
}
|
||||||
|
|
||||||
const GotoParams = struct {
|
const GotoParams = struct {
|
||||||
@@ -205,18 +206,19 @@ const ToolStreamingText = struct {
|
|||||||
switch (self.action) {
|
switch (self.action) {
|
||||||
.markdown => lp.markdown.dump(self.page.document.asNode(), .{}, w, self.page) catch |err| {
|
.markdown => lp.markdown.dump(self.page.document.asNode(), .{}, w, self.page) catch |err| {
|
||||||
log.err(.mcp, "markdown dump failed", .{ .err = err });
|
log.err(.mcp, "markdown dump failed", .{ .err = err });
|
||||||
|
return error.WriteFailed;
|
||||||
},
|
},
|
||||||
.links => {
|
.links => {
|
||||||
if (lp.links.collectLinks(self.page.call_arena, self.page.document.asNode(), self.page)) |links| {
|
const links = lp.links.collectLinks(self.page.call_arena, self.page.document.asNode(), self.page) catch |err| {
|
||||||
|
log.err(.mcp, "query links failed", .{ .err = err });
|
||||||
|
return error.WriteFailed;
|
||||||
|
};
|
||||||
var first = true;
|
var first = true;
|
||||||
for (links) |href| {
|
for (links) |href| {
|
||||||
if (!first) try w.writeByte('\n');
|
if (!first) try w.writeByte('\n');
|
||||||
try w.writeAll(href);
|
try w.writeAll(href);
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
} else |err| {
|
|
||||||
log.err(.mcp, "query links failed", .{ .err = err });
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
.semantic_tree => {
|
.semantic_tree => {
|
||||||
var root_node = self.page.document.asNode();
|
var root_node = self.page.document.asNode();
|
||||||
@@ -241,6 +243,7 @@ const ToolStreamingText = struct {
|
|||||||
|
|
||||||
st.textStringify(w) catch |err| {
|
st.textStringify(w) catch |err| {
|
||||||
log.err(.mcp, "semantic tree dump failed", .{ .err = err });
|
log.err(.mcp, "semantic tree dump failed", .{ .err = err });
|
||||||
|
return error.WriteFailed;
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -331,7 +334,9 @@ fn handleMarkdown(server: *Server, arena: std.mem.Allocator, id: std.json.Value,
|
|||||||
const content = [_]protocol.TextContent(ToolStreamingText){.{
|
const content = [_]protocol.TextContent(ToolStreamingText){.{
|
||||||
.text = .{ .page = page, .action = .markdown },
|
.text = .{ .page = page, .action = .markdown },
|
||||||
}};
|
}};
|
||||||
try server.sendResult(id, protocol.CallToolResult(ToolStreamingText){ .content = &content });
|
server.sendResult(id, protocol.CallToolResult(ToolStreamingText){ .content = &content }) catch {
|
||||||
|
return server.sendError(id, .InternalError, "Failed to serialize markdown content");
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleLinks(server: *Server, arena: std.mem.Allocator, id: std.json.Value, arguments: ?std.json.Value) !void {
|
fn handleLinks(server: *Server, arena: std.mem.Allocator, id: std.json.Value, arguments: ?std.json.Value) !void {
|
||||||
@@ -341,7 +346,9 @@ fn handleLinks(server: *Server, arena: std.mem.Allocator, id: std.json.Value, ar
|
|||||||
const content = [_]protocol.TextContent(ToolStreamingText){.{
|
const content = [_]protocol.TextContent(ToolStreamingText){.{
|
||||||
.text = .{ .page = page, .action = .links },
|
.text = .{ .page = page, .action = .links },
|
||||||
}};
|
}};
|
||||||
try server.sendResult(id, protocol.CallToolResult(ToolStreamingText){ .content = &content });
|
server.sendResult(id, protocol.CallToolResult(ToolStreamingText){ .content = &content }) catch {
|
||||||
|
return server.sendError(id, .InternalError, "Failed to serialize links content");
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleSemanticTree(server: *Server, arena: std.mem.Allocator, id: std.json.Value, arguments: ?std.json.Value) !void {
|
fn handleSemanticTree(server: *Server, arena: std.mem.Allocator, id: std.json.Value, arguments: ?std.json.Value) !void {
|
||||||
@@ -363,7 +370,9 @@ fn handleSemanticTree(server: *Server, arena: std.mem.Allocator, id: std.json.Va
|
|||||||
.maxDepth = args.maxDepth,
|
.maxDepth = args.maxDepth,
|
||||||
},
|
},
|
||||||
}};
|
}};
|
||||||
try server.sendResult(id, protocol.CallToolResult(ToolStreamingText){ .content = &content });
|
server.sendResult(id, protocol.CallToolResult(ToolStreamingText){ .content = &content }) catch {
|
||||||
|
return server.sendError(id, .InternalError, "Failed to serialize semantic tree content");
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handleInteractiveElements(server: *Server, arena: std.mem.Allocator, id: std.json.Value, arguments: ?std.json.Value) !void {
|
fn handleInteractiveElements(server: *Server, arena: std.mem.Allocator, id: std.json.Value, arguments: ?std.json.Value) !void {
|
||||||
|
|||||||
Reference in New Issue
Block a user