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:
61
src/credentials.ts
Normal file
61
src/credentials.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Reader for ~/.a2a/credentials.json — same file the Python `a2a` CLI writes.
|
||||
*
|
||||
* We deliberately *only read* this file (and overwrite it on `a2a-mcp login`)
|
||||
* so a single login flow is shared between the Python and Node tooling.
|
||||
*/
|
||||
import { promises as fs } from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
export const DEFAULT_API_URL = "https://api.a2acloud.io";
|
||||
|
||||
export interface Credentials {
|
||||
apiUrl: string;
|
||||
token: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
const credsDir = () => path.join(os.homedir(), ".a2a");
|
||||
const credsFile = () => path.join(credsDir(), "credentials.json");
|
||||
|
||||
export async function loadCredentials(): Promise<Credentials | null> {
|
||||
try {
|
||||
const raw = await fs.readFile(credsFile(), "utf8");
|
||||
const data = JSON.parse(raw);
|
||||
if (!data?.token) return null;
|
||||
return {
|
||||
apiUrl: data.api_url ?? DEFAULT_API_URL,
|
||||
token: data.token,
|
||||
email: data.email ?? "",
|
||||
};
|
||||
} catch (err: any) {
|
||||
if (err?.code === "ENOENT") return null;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export async function saveCredentials(c: Credentials): Promise<void> {
|
||||
await fs.mkdir(credsDir(), { recursive: true });
|
||||
await fs.writeFile(
|
||||
credsFile(),
|
||||
JSON.stringify({ api_url: c.apiUrl, token: c.token, email: c.email }),
|
||||
{ mode: 0o600 },
|
||||
);
|
||||
}
|
||||
|
||||
export async function clearCredentials(): Promise<boolean> {
|
||||
try {
|
||||
await fs.unlink(credsFile());
|
||||
return true;
|
||||
} catch (err: any) {
|
||||
if (err?.code === "ENOENT") return false;
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveApiUrl(override?: string): string {
|
||||
if (override) return override;
|
||||
if (process.env.A2A_API_URL) return process.env.A2A_API_URL;
|
||||
return DEFAULT_API_URL;
|
||||
}
|
||||
Reference in New Issue
Block a user