agent: add MCP server mode with task tool

This commit is contained in:
Adrià Arrufat
2026-04-30 17:11:48 +02:00
parent 85f2a08128
commit 300fdfb34c
13 changed files with 406 additions and 111 deletions

View File

@@ -6,7 +6,10 @@ const App = @import("../App.zig");
const HttpClient = @import("../browser/HttpClient.zig");
const testing = @import("../testing.zig");
const protocol = @import("protocol.zig");
const resources = @import("resources.zig");
const router = @import("router.zig");
const tools = @import("tools.zig");
const Transport = @import("Transport.zig");
const CDPNode = @import("../cdp/Node.zig");
const Self = @This();
@@ -20,9 +23,7 @@ browser: lp.Browser,
session: *lp.Session,
node_registry: CDPNode.Registry,
writer: *std.io.Writer,
mutex: std.Thread.Mutex = .{},
aw: std.io.Writer.Allocating,
transport: Transport,
pub fn init(allocator: std.mem.Allocator, app: *App, writer: *std.io.Writer) !*Self {
const http_client = try HttpClient.init(allocator, &app.network);
@@ -40,9 +41,8 @@ pub fn init(allocator: std.mem.Allocator, app: *App, writer: *std.io.Writer) !*S
self.* = .{
.allocator = allocator,
.app = app,
.writer = writer,
.browser = browser,
.aw = .init(allocator),
.transport = .init(allocator, writer),
.http_client = http_client,
.notification = notification,
.session = undefined,
@@ -64,7 +64,7 @@ pub fn deinit(self: *Self) void {
}
self.node_registry.deinit();
self.aw.deinit();
self.transport.deinit();
self.browser.deinit();
self.notification.deinit();
self.http_client.deinit();
@@ -72,39 +72,34 @@ pub fn deinit(self: *Self) void {
self.allocator.destroy(self);
}
pub fn sendResponse(self: *Self, response: anytype) !void {
self.mutex.lock();
defer self.mutex.unlock();
self.aw.clearRetainingCapacity();
try std.json.Stringify.value(response, .{ .emit_null_optional_fields = false }, &self.aw.writer);
try self.aw.writer.writeByte('\n');
try self.writer.writeAll(self.aw.writer.buffered());
try self.writer.flush();
}
pub fn sendResult(self: *Self, id: std.json.Value, result: anytype) !void {
const GenericResponse = struct {
jsonrpc: []const u8 = "2.0",
id: std.json.Value,
result: @TypeOf(result),
};
try self.sendResponse(GenericResponse{
.id = id,
.result = result,
});
}
pub fn sendError(self: *Self, id: std.json.Value, code: protocol.ErrorCode, message: []const u8) !void {
try self.sendResponse(protocol.Response{
.id = id,
.@"error" = protocol.Error{
.code = @intFromEnum(code),
.message = message,
pub fn handleInitialize(self: *Self, req: protocol.Request) !void {
const id = req.id orelse return;
try self.transport.sendResult(id, protocol.InitializeResult{
.protocolVersion = @tagName(protocol.Version.default),
.capabilities = .{
.resources = .{},
.tools = .{},
},
.serverInfo = .{ .name = "lightpanda", .version = "0.1.0" },
});
}
pub fn handleToolList(self: *Self, arena: std.mem.Allocator, req: protocol.Request) !void {
return tools.handleList(self, arena, req);
}
pub fn handleToolCall(self: *Self, arena: std.mem.Allocator, req: protocol.Request) !void {
return tools.handleCall(self, arena, req);
}
pub fn handleResourceList(self: *Self, req: protocol.Request) !void {
return resources.handleList(self, req);
}
pub fn handleResourceRead(self: *Self, arena: std.mem.Allocator, req: protocol.Request) !void {
return resources.handleRead(self, arena, req);
}
test "MCP.Server - Integration: synchronous smoke test" {
defer testing.reset();
const allocator = testing.allocator;