qasphere-mcp
Version:
MCP server for QA Sphere integration
75 lines (74 loc) • 2.25 kB
JavaScript
import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from './config.js';
export class ApiError extends Error {
status;
body;
constructor(message, status, body) {
super(message);
this.name = 'ApiError';
this.status = status;
this.body = body;
}
}
export class ResponseValidationError extends Error {
body;
constructor(message, body) {
super(message);
this.name = 'ResponseValidationError';
this.body = body;
}
}
const buildQueryString = (query) => {
if (!query)
return '';
const params = new URLSearchParams();
for (const [key, value] of Object.entries(query)) {
if (value === undefined)
continue;
if (Array.isArray(value)) {
for (const item of value)
params.append(key, String(item));
}
else {
params.append(key, String(value));
}
}
const str = params.toString();
return str ? `?${str}` : '';
};
export async function apiQuery(path, opts) {
const url = `${QASPHERE_TENANT_URL}${path}${buildQueryString(opts.query)}`;
const method = opts.method ?? 'GET';
const headers = {
Authorization: `ApiKey ${QASPHERE_API_KEY}`,
};
if (opts.body !== undefined) {
headers['Content-Type'] = 'application/json';
}
const res = await fetch(url, {
method,
headers,
body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined,
});
if (!res.ok) {
let body;
let message = res.statusText;
try {
body = await res.json();
if (body && typeof body === 'object' && 'message' in body) {
const m = body.message;
if (typeof m === 'string' && m.length > 0)
message = m;
}
}
catch {
// ignore JSON parse failures; fall back to statusText
}
throw new ApiError(message, res.status, body);
}
const raw = await res.json();
const parsed = opts.schema.safeParse(raw);
if (!parsed.success) {
throw new ResponseValidationError(`Response validation failed: ${parsed.error.message}`, raw);
}
return parsed.data;
}