Skip to main content
Glama
api-client.ts5.19 kB
/** * Councly API Client * * REST client for communicating with Councly's MCP API. * Handles authentication, requests, and error handling. */ export interface CounclyConfig { apiKey: string; baseUrl?: string; } export interface CreateHearingRequest { subject: string; preset?: 'balanced' | 'fast' | 'coding' | 'coding_plus'; workflow?: 'auto' | 'discussion' | 'review' | 'brainstorming'; idempotencyKey?: string; } export interface CreateHearingResponse { hearingId: string; status: string; preset: string; workflow: string; cost: { credits: number; usd: number; }; createdAt: string; } export interface HearingStatusResponse { hearingId: string; status: 'pending' | 'streaming' | 'completed' | 'failed' | 'early_stopped'; phase: string | null; createdAt: string; completedAt?: string | null; verdict?: string | null; trustScore?: number | null; totalCost?: number; totalTokens?: number; counselSummaries?: Array<{ seat: string; model: string; lastTurn: string; }>; progress?: number; error?: string; } export interface PresetInfo { id: string; name: string; description: string; counselCount: number; cost: { credits: number; usd: number; }; models: Array<{ seat: string; provider: string; model: string; }>; } export interface PresetsResponse { presets: PresetInfo[]; workflows: Array<{ id: string; name: string; description: string; }>; defaults: { preset: string; workflow: string; }; } export interface ApiError { code: string; message: string; details?: Record<string, unknown>; } export class CounclyApiError extends Error { constructor( public code: string, message: string, public status: number, public details?: Record<string, unknown> ) { super(message); this.name = 'CounclyApiError'; } } export class CounclyClient { private apiKey: string; private baseUrl: string; constructor(config: CounclyConfig) { this.apiKey = config.apiKey; this.baseUrl = config.baseUrl || 'https://councly.ai'; } private async request<T>( method: 'GET' | 'POST' | 'DELETE', path: string, body?: unknown, headers?: Record<string, string> ): Promise<T> { const url = `${this.baseUrl}/api/v1/mcp${path}`; const response = await fetch(url, { method, headers: { 'Authorization': `Bearer ${this.apiKey}`, 'Content-Type': 'application/json', 'User-Agent': '@councly/mcp', ...headers, }, body: body ? JSON.stringify(body) : undefined, }); const data = await response.json(); if (!response.ok) { const error = data as ApiError; throw new CounclyApiError( error.code || 'UNKNOWN_ERROR', error.message || 'An unknown error occurred', response.status, error.details ); } return data as T; } /** * Create a new council hearing. */ async createHearing(request: CreateHearingRequest): Promise<CreateHearingResponse> { const headers: Record<string, string> = {}; if (request.idempotencyKey) { headers['Idempotency-Key'] = request.idempotencyKey; } return this.request<CreateHearingResponse>('POST', '/council', request, headers); } /** * Get hearing status. */ async getHearingStatus(hearingId: string): Promise<HearingStatusResponse> { return this.request<HearingStatusResponse>('GET', `/council/${hearingId}`); } /** * Cancel a hearing. */ async cancelHearing(hearingId: string): Promise<{ success: boolean; message: string }> { return this.request<{ success: boolean; message: string }>('DELETE', `/council/${hearingId}`); } /** * List available presets. */ async listPresets(): Promise<PresetsResponse> { return this.request<PresetsResponse>('GET', '/presets'); } /** * Health check. */ async healthCheck(): Promise<{ status: string }> { return this.request<{ status: string }>('GET', '/health'); } /** * Poll for hearing completion with timeout. * Returns when hearing reaches terminal state or timeout. */ async waitForCompletion( hearingId: string, options: { pollIntervalMs?: number; timeoutMs?: number; onProgress?: (status: HearingStatusResponse) => void; } = {} ): Promise<HearingStatusResponse> { const { pollIntervalMs = 5000, timeoutMs = 300000, // 5 minutes default onProgress, } = options; const startTime = Date.now(); while (true) { const status = await this.getHearingStatus(hearingId); if (onProgress) { onProgress(status); } // Terminal states if (['completed', 'failed', 'early_stopped'].includes(status.status)) { return status; } // Timeout check if (Date.now() - startTime > timeoutMs) { throw new CounclyApiError( 'TIMEOUT', `Hearing did not complete within ${timeoutMs / 1000} seconds`, 408 ); } // Wait before next poll await new Promise((resolve) => setTimeout(resolve, pollIntervalMs)); } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/slmnsrf/councly-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server