2026-02-21 04:55:45 +00:00
|
|
|
import http from 'node:http';
|
2026-04-17 22:34:00 +01:00
|
|
|
import https from 'node:https';
|
2026-02-21 04:55:45 +00:00
|
|
|
|
|
|
|
|
export interface ApiClientOptions {
|
|
|
|
|
baseUrl: string;
|
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00
|
|
|
timeout?: number | undefined;
|
|
|
|
|
token?: string | undefined;
|
2026-02-21 04:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ApiResponse<T = unknown> {
|
|
|
|
|
status: number;
|
|
|
|
|
data: T;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class ApiError extends Error {
|
|
|
|
|
constructor(
|
|
|
|
|
public readonly status: number,
|
|
|
|
|
public readonly body: string,
|
|
|
|
|
) {
|
|
|
|
|
super(`API error ${status}: ${body}`);
|
|
|
|
|
this.name = 'ApiError';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00
|
|
|
function request<T>(method: string, url: string, timeout: number, body?: unknown, token?: string): Promise<ApiResponse<T>> {
|
2026-02-21 04:55:45 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const parsed = new URL(url);
|
2026-02-23 19:54:34 +00:00
|
|
|
const headers: Record<string, string> = {};
|
|
|
|
|
if (body !== undefined) {
|
|
|
|
|
headers['Content-Type'] = 'application/json';
|
|
|
|
|
}
|
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00
|
|
|
if (token) {
|
|
|
|
|
headers['Authorization'] = `Bearer ${token}`;
|
|
|
|
|
}
|
2026-04-17 22:34:00 +01:00
|
|
|
const isHttps = parsed.protocol === 'https:';
|
2026-02-21 04:55:45 +00:00
|
|
|
const opts: http.RequestOptions = {
|
|
|
|
|
hostname: parsed.hostname,
|
2026-04-17 22:34:00 +01:00
|
|
|
port: parsed.port || (isHttps ? 443 : 80),
|
2026-02-21 04:55:45 +00:00
|
|
|
path: parsed.pathname + parsed.search,
|
|
|
|
|
method,
|
|
|
|
|
timeout,
|
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00
|
|
|
headers,
|
2026-02-21 04:55:45 +00:00
|
|
|
};
|
|
|
|
|
|
2026-04-17 22:34:00 +01:00
|
|
|
const driver = isHttps ? https : http;
|
|
|
|
|
const req = driver.request(opts, (res) => {
|
2026-02-21 04:55:45 +00:00
|
|
|
const chunks: Buffer[] = [];
|
|
|
|
|
res.on('data', (chunk: Buffer) => chunks.push(chunk));
|
|
|
|
|
res.on('end', () => {
|
|
|
|
|
const raw = Buffer.concat(chunks).toString('utf-8');
|
|
|
|
|
const status = res.statusCode ?? 0;
|
|
|
|
|
if (status >= 400) {
|
|
|
|
|
reject(new ApiError(status, raw));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
resolve({ status, data: JSON.parse(raw) as T });
|
|
|
|
|
} catch {
|
|
|
|
|
resolve({ status, data: raw as unknown as T });
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
req.on('error', reject);
|
|
|
|
|
req.on('timeout', () => {
|
|
|
|
|
req.destroy();
|
|
|
|
|
reject(new Error(`Request to ${url} timed out`));
|
|
|
|
|
});
|
|
|
|
|
if (body !== undefined) {
|
|
|
|
|
req.write(JSON.stringify(body));
|
|
|
|
|
}
|
|
|
|
|
req.end();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class ApiClient {
|
|
|
|
|
private baseUrl: string;
|
|
|
|
|
private timeout: number;
|
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00
|
|
|
private token?: string | undefined;
|
2026-02-21 04:55:45 +00:00
|
|
|
|
|
|
|
|
constructor(opts: ApiClientOptions) {
|
|
|
|
|
this.baseUrl = opts.baseUrl.replace(/\/$/, '');
|
|
|
|
|
this.timeout = opts.timeout ?? 10000;
|
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00
|
|
|
this.token = opts.token;
|
2026-02-21 04:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async get<T = unknown>(path: string): Promise<T> {
|
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00
|
|
|
const res = await request<T>('GET', `${this.baseUrl}${path}`, this.timeout, undefined, this.token);
|
2026-02-21 04:55:45 +00:00
|
|
|
return res.data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async post<T = unknown>(path: string, body?: unknown): Promise<T> {
|
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00
|
|
|
const res = await request<T>('POST', `${this.baseUrl}${path}`, this.timeout, body, this.token);
|
2026-02-21 04:55:45 +00:00
|
|
|
return res.data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async put<T = unknown>(path: string, body?: unknown): Promise<T> {
|
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00
|
|
|
const res = await request<T>('PUT', `${this.baseUrl}${path}`, this.timeout, body, this.token);
|
2026-02-21 04:55:45 +00:00
|
|
|
return res.data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async delete(path: string): Promise<void> {
|
feat: implement v2 3-tier architecture (mcpctl → mcplocal → mcpd)
- Rename local-proxy to mcplocal with HTTP server, LLM pipeline, mcpd discovery
- Add LLM pre-processing: token estimation, filter cache, metrics, Gemini CLI + DeepSeek providers
- Add mcpd auth (login/logout) and MCP proxy endpoints
- Update CLI: dual URLs (mcplocalUrl/mcpdUrl), auth commands, --direct flag
- Add tiered health monitoring, shell completions, e2e integration tests
- 57 test files, 597 tests passing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 11:42:06 +00:00
|
|
|
await request('DELETE', `${this.baseUrl}${path}`, this.timeout, undefined, this.token);
|
2026-02-21 04:55:45 +00:00
|
|
|
}
|
|
|
|
|
}
|