This repository has been archived on 2026-06-28. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
a2a-mcp/src/credentials.ts
Robert 024f14000e
All checks were successful
build / test (push) Successful in 29s
publish / npm (push) Successful in 30s
release: 0.1.4 — forward cp_jwt + bucket via params._meta
On login the gateway now captures user.id from /v1/auth/login, derives
the user's MinIO bucket (user-<id>-files), and persists both in
~/.a2a/credentials.json. UpstreamAgent.callTool injects
``params._meta = { cp_jwt, cp_url, bucket }`` on every tools/call so
upstream agents see the same caller context the platform orchestrator
provides. Unauthenticated clients omit _meta — back-compat preserved.

Requires a2a-pack with the matching _meta handler in
``a2a_pack/mcp/server.py`` (companion change in apps/a2a).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 20:53:07 -03:00

69 lines
2.0 KiB
TypeScript

/**
* Reader for ~/.a2a/credentials.json — same file the Python `a2a` CLI writes.
*
* We deliberately *only read* this file (and overwrite it on `a2amcp 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;
userId?: number;
bucket?: 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 ?? "",
userId: typeof data.user_id === "number" ? data.user_id : undefined,
bucket: typeof data.bucket === "string" ? data.bucket : undefined,
};
} 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 });
const out: Record<string, unknown> = {
api_url: c.apiUrl,
token: c.token,
email: c.email,
};
if (c.userId !== undefined) out.user_id = c.userId;
if (c.bucket !== undefined) out.bucket = c.bucket;
await fs.writeFile(credsFile(), JSON.stringify(out), { 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;
}