@dexwox-labs/a2a-server
Version:
TypeScript server implementation for Google's Agent-to-Agent (A2A) protocol - includes Express/WebSocket handlers, request validation and queue management
314 lines • 12 kB
JavaScript
;
/**
* @module DefaultJsonRpcRequestHandler
* @description Default implementation of the JSON-RPC request handler for the A2A protocol
*
* This module provides the default implementation of the JSON-RPC request handler
* interface for the A2A protocol server. It includes middleware for request validation,
* error handling, and response formatting, as well as methods for handling various
* JSON-RPC requests.
*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DefaultJsonRpcRequestHandler = void 0;
const express_1 = require("express");
/**
* Default implementation of the JSON-RPC request handler
*
* This class provides a standard implementation of the JSON-RPC request handler
* interface for the A2A protocol server. It includes middleware for request
* validation, error handling, and response formatting, as well as methods for
* handling various JSON-RPC requests.
*
* @example
* ```typescript
* // Create a JSON-RPC request handler
* const jsonRpcHandler = new DefaultJsonRpcRequestHandler();
*
* // Add middleware
* jsonRpcHandler.use(jsonRpcHandler.handleErrors());
* jsonRpcHandler.use(jsonRpcHandler.formatResponse());
*
* // Use in an Express app
* app.use('/jsonrpc', jsonRpcHandler.router);
* ```
*/
class DefaultJsonRpcRequestHandler {
constructor() {
/** Express router for handling HTTP requests */
this.router = (0, express_1.Router)();
/** Array of middleware functions to apply to requests */
this.middlewares = [];
}
/**
* Adds middleware to the request handler
*
* This method adds a middleware function to the request handler's middleware
* stack. Middleware functions are executed in the order they are added.
*
* @param middleware - Middleware function to add
*
* @example
* ```typescript
* // Add error handling middleware
* jsonRpcHandler.use(jsonRpcHandler.handleErrors());
*
* // Add custom logging middleware
* jsonRpcHandler.use(async (req, res, next) => {
* console.log(`${req.method} ${req.path}`);
* await next();
* });
* ```
*/
use(middleware) {
this.middlewares.push(middleware);
}
/**
* Creates middleware for validating request body against a schema
*
* This method returns a middleware function that validates the request body
* against a Zod schema. If validation fails, it responds with a 400 Bad Request
* and a JSON-RPC error response.
*
* @param schema - Zod schema to validate against
* @returns Middleware function that validates requests
*
* @example
* ```typescript
* // Create a schema for validating sendMessage requests
* const sendMessageSchema = z.object({
* jsonrpc: z.literal('2.0'),
* method: z.literal('sendMessage'),
* params: z.object({
* parts: z.array(z.object({
* type: z.string(),
* content: z.string()
* })),
* agentId: z.string()
* }),
* id: z.string().optional()
* });
*
* // Add validation middleware
* jsonRpcHandler.use(jsonRpcHandler.validateRequest(sendMessageSchema));
* ```
*/
validateRequest(schema) {
return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
var _a;
try {
schema.parse(req.body);
next();
}
catch (err) {
res.status(400).json(this.buildErrorResponse((_a = req.body) === null || _a === void 0 ? void 0 : _a.id, err));
}
});
}
/**
* Creates middleware for handling errors
*
* This method returns a middleware function that catches any errors thrown
* during request processing and responds with a 500 Internal Server Error
* and a JSON-RPC error response.
*
* @returns Middleware function that catches and processes errors
*
* @example
* ```typescript
* // Add error handling middleware
* jsonRpcHandler.use(jsonRpcHandler.handleErrors());
* ```
*/
handleErrors() {
return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
var _a;
try {
yield next();
}
catch (err) {
res.status(500).json(this.buildErrorResponse((_a = req.body) === null || _a === void 0 ? void 0 : _a.id, err));
}
});
}
/**
* Creates middleware for formatting responses
*
* This method returns a middleware function that formats responses as JSON-RPC
* success responses. If the response already has a jsonrpc property, it is
* left unchanged.
*
* @returns Middleware function that formats responses
*
* @example
* ```typescript
* // Add response formatting middleware
* jsonRpcHandler.use(jsonRpcHandler.formatResponse());
* ```
*/
formatResponse() {
return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
const originalJson = res.json;
res.json = (body) => {
var _a;
if ((body === null || body === void 0 ? void 0 : body.jsonrpc) === '2.0') {
return originalJson.call(res, body);
}
return originalJson.call(res, this.buildSuccessResponse((_a = req.body) === null || _a === void 0 ? void 0 : _a.id, body));
};
next();
});
}
/**
* Builds a JSON-RPC success response
*
* This method creates a properly formatted JSON-RPC 2.0 success response
* with the provided result data.
*
* @param id - Request ID from the original JSON-RPC request
* @param result - Result data to include in the response
* @returns Properly formatted JSON-RPC success response
*
* @example
* ```typescript
* // Create a success response
* const response = jsonRpcHandler.buildSuccessResponse('request-123', {
* taskId: 'task-456'
* });
*
* // Send the response
* res.json(response);
* ```
*/
buildSuccessResponse(id, result) {
return {
jsonrpc: '2.0',
id,
result
};
}
buildErrorResponse(id, error) {
return {
jsonrpc: '2.0',
id,
error: this.normalizeError(error)
};
}
handleJsonRpcSendMessage(req, res) {
return __awaiter(this, void 0, void 0, function* () {
try {
const { parts, agentId } = req.body;
const messageId = yield this.handleSendMessage(parts, agentId);
res.json(this.buildSuccessResponse(req.body.id, messageId));
}
catch (err) {
res.status(400).json(this.buildErrorResponse(req.body.id, err));
}
});
}
handleJsonRpcStreamMessage(req, res) {
return __awaiter(this, void 0, void 0, function* () {
try {
const { parts, agentId } = req.body;
const stream = this.handleStreamMessage(parts, agentId);
// Start consuming the stream in background
(() => __awaiter(this, void 0, void 0, function* () {
var _a, e_1, _b, _c;
try {
for (var _d = true, stream_1 = __asyncValues(stream), stream_1_1; stream_1_1 = yield stream_1.next(), _a = stream_1_1.done, !_a; _d = true) {
_c = stream_1_1.value;
_d = false;
const _ = _c;
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (!_d && !_a && (_b = stream_1.return)) yield _b.call(stream_1);
}
finally { if (e_1) throw e_1.error; }
}
}))();
res.json(this.buildSuccessResponse(req.body.id, 'Stream started'));
}
catch (err) {
res.status(400).json(this.buildErrorResponse(req.body.id, err));
}
});
}
handleJsonRpcGetTaskStatus(req, res) {
return __awaiter(this, void 0, void 0, function* () {
try {
const task = yield this.handleGetTaskStatus(req.params.taskId);
res.json(this.buildSuccessResponse(undefined, { task }));
}
catch (err) {
res.status(400).json(this.buildErrorResponse(undefined, err));
}
});
}
handleJsonRpcCancelTask(req, res) {
return __awaiter(this, void 0, void 0, function* () {
try {
yield this.handleCancelTask(req.params.taskId);
res.json(this.buildSuccessResponse(undefined, 'Cancellation requested'));
}
catch (err) {
res.status(400).json(this.buildErrorResponse(undefined, err));
}
});
}
handleJsonRpcDiscoverAgents(req, res) {
return __awaiter(this, void 0, void 0, function* () {
try {
const { capability } = req.query;
const agents = yield this.handleDiscoverAgents(capability);
res.json(this.buildSuccessResponse(undefined, { agents }));
}
catch (err) {
res.status(400).json(this.buildErrorResponse(undefined, err));
}
});
}
// Implement BaseRequestHandler methods
handleSendMessage(parts, agentId) {
throw new Error('Method not implemented.');
}
handleStreamMessage(parts, agentId) {
throw new Error('Method not implemented.');
}
handleGetTaskStatus(taskId) {
throw new Error('Method not implemented.');
}
handleCancelTask(taskId) {
throw new Error('Method not implemented.');
}
handleTaskResubscription(taskId) {
throw new Error('Method not implemented.');
}
handleDiscoverAgents(capability) {
return __awaiter(this, void 0, void 0, function* () {
throw new Error('Method not implemented.');
});
}
normalizeError(err) {
throw new Error('Method not implemented.');
}
}
exports.DefaultJsonRpcRequestHandler = DefaultJsonRpcRequestHandler;
//# sourceMappingURL=default-jsonrpc-handler.js.map