Add wave-mcp/src/services/wave-client.ts

This commit is contained in:
Zac Gaetano 2026-03-31 15:33:49 -04:00
parent bc56a8cda8
commit 0228ee1f09

View file

@ -0,0 +1,62 @@
import { WaveConfig, GraphQLResponse } from "../types.js";
import { WAVE_GRAPHQL_URL, CHARACTER_LIMIT } from "../constants.js";
export class WaveClient {
private accessToken: string;
constructor(config: WaveConfig) {
this.accessToken = config.accessToken;
}
// ── Core GraphQL executor ──────────────────────────────────────────────────
async query<T = unknown>(
query: string,
variables?: Record<string, unknown>
): Promise<T> {
const response = await fetch(WAVE_GRAPHQL_URL, {
method: "POST",
headers: {
Authorization: `Bearer ${this.accessToken}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({ query, variables: variables ?? {} }),
});
if (!response.ok) {
const text = await response.text().catch(() => "(no body)");
throw new Error(
`Wave API HTTP error ${response.status} ${response.statusText}: ${text.slice(0, 300)}`
);
}
const json = (await response.json()) as GraphQLResponse<T>;
if (json.errors?.length) {
const messages = json.errors.map((e) => e.message).join("; ");
throw new Error(`Wave GraphQL error: ${messages}`);
}
if (json.data === undefined) {
throw new Error("Wave API returned no data and no errors.");
}
return json.data;
}
// ── Formatting helpers ─────────────────────────────────────────────────────
/** Truncate a JSON response string if it exceeds CHARACTER_LIMIT */
static truncate(text: string): string {
if (text.length <= CHARACTER_LIMIT) return text;
return (
text.slice(0, CHARACTER_LIMIT) +
`\n\n...[TRUNCATED: response exceeded ${CHARACTER_LIMIT} characters. Use pagination (page, pageSize) to retrieve more data.]`
);
}
static formatCurrency(value: number, code: string): string {
return `${code} ${value.toFixed(2)}`;
}
}