/* eslint-disable no-console */
import { config } from './config.js';
export class ApiClient {
private accessToken = '';
private readonly baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
/**
* Login to PeopleBox API and extract access_token from Set-Cookie header.
* The API sets tokens as HttpOnly cookies and strips them from the response body.
*/
async login(): Promise<void> {
const res = await fetch(`${this.baseUrl}/auth/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email: config.apiEmail,
password: config.apiPassword,
}),
redirect: 'manual',
});
if (!res.ok) {
const body = await res.text();
throw new Error(`Login failed (${res.status}): ${body}`);
}
// Extract access_token from Set-Cookie header
const cookies = res.headers.getSetCookie?.() ?? [];
for (const cookie of cookies) {
const match = cookie.match(/access_token=([^;]+)/);
if (match?.[1]) {
this.accessToken = match[1];
console.log('[MCP] Authenticated successfully');
return;
}
}
// Fallback: try raw set-cookie header
const rawCookie = res.headers.get('set-cookie') ?? '';
const match = rawCookie.match(/access_token=([^;]+)/);
if (match?.[1]) {
this.accessToken = match[1];
console.log('[MCP] Authenticated successfully (fallback)');
return;
}
throw new Error(
'Login succeeded but no access_token cookie found in response'
);
}
get<T = unknown>(path: string): Promise<T> {
return this.request<T>('GET', path);
}
post<T = unknown>(path: string, body?: unknown): Promise<T> {
return this.request<T>('POST', path, body);
}
patch<T = unknown>(path: string, body?: unknown): Promise<T> {
return this.request<T>('PATCH', path, body);
}
del<T = unknown>(path: string): Promise<T> {
return this.request<T>('DELETE', path);
}
private async request<T>(
method: string,
path: string,
body?: unknown
): Promise<T> {
const url = `${this.baseUrl}${path}`;
const headers: Record<string, string> = {
Cookie: `access_token=${this.accessToken}`,
};
if (body !== undefined) {
headers['Content-Type'] = 'application/json';
}
let res = await fetch(url, {
method,
headers,
body: body !== undefined ? JSON.stringify(body) : undefined,
redirect: 'manual',
});
// Auto re-login on 401
if (res.status === 401) {
console.log('[MCP] Token expired, re-authenticating...');
await this.login();
headers.Cookie = `access_token=${this.accessToken}`;
res = await fetch(url, {
method,
headers,
body: body !== undefined ? JSON.stringify(body) : undefined,
redirect: 'manual',
});
}
if (!res.ok) {
const text = await res.text();
throw new Error(`API ${method} ${path} failed (${res.status}): ${text}`);
}
if (res.status === 204) {
return undefined as T;
}
return res.json() as Promise<T>;
}
}