initial: @a2a/mcp gateway for a2acloud agents
Local MCP server that exposes any number of deployed A2A agents to Claude Code, Cursor, Windsurf, and other MCP clients. Reuses the ~/.a2a/credentials.json token written by the Python a2a CLI. Adding or removing agents is a single ~/.a2a/mcp.json edit. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
81
src/api.ts
Normal file
81
src/api.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Thin control-plane client. Mirrors the relevant subset of
|
||||
* a2a_pack.cli.api_client.ControlPlaneClient.
|
||||
*/
|
||||
export class ApiError extends Error {
|
||||
constructor(public status: number, message: string) {
|
||||
super(`API ${status}: ${message}`);
|
||||
this.name = "ApiError";
|
||||
}
|
||||
}
|
||||
|
||||
export interface AgentRow {
|
||||
name: string;
|
||||
version: string;
|
||||
status: string;
|
||||
url?: string;
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export class ControlPlaneClient {
|
||||
constructor(
|
||||
private apiUrl: string,
|
||||
private token: string | null,
|
||||
) {
|
||||
this.apiUrl = apiUrl.replace(/\/+$/, "");
|
||||
}
|
||||
|
||||
private headers(): Record<string, string> {
|
||||
const h: Record<string, string> = { "content-type": "application/json" };
|
||||
if (this.token) h["authorization"] = `bearer ${this.token}`;
|
||||
return h;
|
||||
}
|
||||
|
||||
private async request<T>(method: string, path: string, body?: unknown): Promise<T> {
|
||||
const resp = await fetch(`${this.apiUrl}${path}`, {
|
||||
method,
|
||||
headers: this.headers(),
|
||||
body: body !== undefined ? JSON.stringify(body) : undefined,
|
||||
});
|
||||
if (!resp.ok) {
|
||||
let detail: string;
|
||||
try {
|
||||
const j: any = await resp.json();
|
||||
detail = j?.detail ?? JSON.stringify(j);
|
||||
} catch {
|
||||
detail = await resp.text();
|
||||
}
|
||||
throw new ApiError(resp.status, detail);
|
||||
}
|
||||
if (resp.status === 204) return undefined as T;
|
||||
return (await resp.json()) as T;
|
||||
}
|
||||
|
||||
login(email: string, password: string) {
|
||||
return this.request<{ access_token: string; user: { email: string } }>(
|
||||
"POST",
|
||||
"/v1/auth/login",
|
||||
{ email, password },
|
||||
);
|
||||
}
|
||||
|
||||
signup(email: string, password: string) {
|
||||
return this.request<{ access_token: string; user: { email: string } }>(
|
||||
"POST",
|
||||
"/v1/auth/signup",
|
||||
{ email, password },
|
||||
);
|
||||
}
|
||||
|
||||
me() {
|
||||
return this.request<{ email: string }>("GET", "/v1/me");
|
||||
}
|
||||
|
||||
listAgents() {
|
||||
return this.request<AgentRow[]>("GET", "/v1/agents");
|
||||
}
|
||||
|
||||
getAgent(name: string) {
|
||||
return this.request<AgentRow>("GET", `/v1/agents/${encodeURIComponent(name)}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user