Portkey MCP Server

import dotenv from 'dotenv'; dotenv.config(); interface PortkeyUser { object: string; id: string; first_name: string; last_name: string; role: string; email: string; created_at: string; last_updated_at: string; } interface PortkeyUsersResponse { total: number; object: string; data: PortkeyUser[]; } interface WorkspaceDetails { id: string; // Changed from slug to id role: 'admin' | 'member' | 'manager'; // Added 'manager' option } interface WorkspaceApiKeyDetails { name?: string; expiry?: string; metadata?: Record<string, string>; scopes: string[]; // Added required scopes array } interface InviteUserRequest { email: string; role: 'admin' | 'member'; first_name?: string; last_name?: string; workspaces: WorkspaceDetails[]; workspace_api_key_details?: WorkspaceApiKeyDetails; } interface InviteUserResponse { id: string; // Changed to match API response invite_link: string; // Changed to match API response } interface UserGroupedDataParams { time_of_generation_min: string; // ISO8601 format time_of_generation_max: string; // ISO8601 format total_units_min?: number; total_units_max?: number; cost_min?: number; cost_max?: number; prompt_token_min?: number; prompt_token_max?: number; completion_token_min?: number; completion_token_max?: number; status_code?: string; weighted_feedback_min?: number; weighted_feedback_max?: number; virtual_keys?: string; configs?: string; workspace_slug?: string; api_key_ids?: string; current_page?: number; page_size?: number; metadata?: string; ai_org_model?: string; trace_id?: string; span_id?: string; } interface AnalyticsGroup { user: string; requests: string; cost: string; object: "analytics-group"; } interface UserGroupedData { total: number; object: string; data: AnalyticsGroup[]; } interface WorkspaceDefaults { is_default?: number; metadata?: Record<string, string>; object: 'workspace'; } interface Workspace { id: string; name: string; slug: string; description: string | null; created_at: string; last_updated_at: string; defaults: WorkspaceDefaults | null; object: 'workspace'; } interface ListWorkspacesResponse { total: number; object: 'list'; data: Workspace[]; } interface ListWorkspacesParams { page_size?: number; current_page?: number; } interface WorkspaceUser { object: 'workspace-user'; id: string; first_name: string; last_name: string; org_role: 'admin' | 'member' | 'owner'; role: 'admin' | 'member' | 'manager'; status: 'active'; created_at: string; last_updated_at: string; } interface SingleWorkspaceResponse { id: string; name: string; slug: string; description: string | null; created_at: string; last_updated_at: string; defaults: { is_default: number; metadata: Record<string, string>; object: 'workspace'; } | null; users: WorkspaceUser[]; } interface Config { id: string; name: string; slug: string; organisation_id: string; workspace_id: string; is_default: number; status: string; owner_id: string; updated_by: string; created_at: string; last_updated_at: string; } interface ListConfigsResponse { success: boolean; data: Config[]; } interface VirtualKeyRateLimit { type: 'requests'; unit: 'rpm'; value: number; } interface VirtualKeyUsageLimits { alert_threshold: number; credit_limit: number; periodic_reset: 'monthly'; } interface VirtualKey { name: string; note: string | null; status: 'active' | 'exhausted'; usage_limits: VirtualKeyUsageLimits | null; reset_usage: number | null; created_at: string; slug: string; model_config: Record<string, any>; rate_limits: VirtualKeyRateLimit[] | null; object: 'virtual-key'; } interface ListVirtualKeysResponse { object: 'list'; total: number; data: VirtualKey[]; } interface CostDataPoint { timestamp: string; total: number; avg: number; } interface CostSummary { total: number; avg: number; } interface CostAnalyticsResponse { object: 'analytics-graph'; data_points: CostDataPoint[]; summary: CostSummary; } interface CostAnalyticsParams { time_of_generation_min: string; // ISO8601 format time_of_generation_max: string; // ISO8601 format total_units_min?: number; total_units_max?: number; cost_min?: number; cost_max?: number; prompt_token_min?: number; prompt_token_max?: number; completion_token_min?: number; completion_token_max?: number; status_code?: string; weighted_feedback_min?: number; weighted_feedback_max?: number; virtual_keys?: string; configs?: string; workspace_slug?: string; api_key_ids?: string; metadata?: string; ai_org_model?: string; trace_id?: string; span_id?: string; } interface ConfigTarget { provider?: string; virtual_key?: string; } interface ConfigDetails { retry?: { attempts?: number; on_status_codes?: number[]; }; cache?: { mode?: string; max_age?: number; }; strategy?: { mode?: string; }; targets?: ConfigTarget[]; } interface GetConfigResponse { success?: boolean; data?: { config?: ConfigDetails; }; } export class PortkeyService { private readonly apiKey: string; private readonly baseUrl = 'https://api.portkey.ai/v1'; constructor() { const apiKey = process.env.PORTKEY_API_KEY; if (!apiKey) { throw new Error('PORTKEY_API_KEY environment variable is not set'); } this.apiKey = apiKey; } async listUsers(): Promise<PortkeyUsersResponse> { try { const response = await fetch(`${this.baseUrl}/admin/users`, { method: 'GET', headers: { 'x-portkey-api-key': this.apiKey, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json() as PortkeyUsersResponse; return { total: data.total, object: data.object, data: data.data.map(user => ({ object: user.object, id: user.id, first_name: user.first_name, last_name: user.last_name, email: user.email, role: user.role, created_at: user.created_at, last_updated_at: user.last_updated_at })) }; } catch (error) { console.error('PortkeyService Error:', error); throw new Error('Failed to fetch users from Portkey API'); } } async inviteUser(data: InviteUserRequest): Promise<InviteUserResponse> { try { const response = await fetch(`${this.baseUrl}/admin/users/invites`, { // Fixed URL method: 'POST', headers: { 'x-portkey-api-key': this.apiKey, 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ email: data.email, role: data.role, first_name: data.first_name, last_name: data.last_name, workspaces: data.workspaces, workspace_api_key_details: data.workspace_api_key_details }) }); if (!response.ok) { const error = await response.json(); throw new Error(error.message || `Failed to invite user: ${response.status}`); } const result = await response.json(); return { id: result.id, invite_link: result.invite_link }; } catch (error) { console.error('PortkeyService Error:', error); throw new Error('Failed to invite user to Portkey'); } } async getUserGroupedData(params: UserGroupedDataParams): Promise<UserGroupedData> { try { const queryParams = new URLSearchParams({ time_of_generation_min: params.time_of_generation_min, time_of_generation_max: params.time_of_generation_max, ...(params.total_units_min && { total_units_min: params.total_units_min.toString() }), ...(params.total_units_max && { total_units_max: params.total_units_max.toString() }), ...(params.cost_min && { cost_min: params.cost_min.toString() }), ...(params.cost_max && { cost_max: params.cost_max.toString() }), // Add other optional parameters as needed }); const response = await fetch( `${this.baseUrl}/analytics/groups/users?${queryParams.toString()}`, { method: 'GET', headers: { 'x-portkey-api-key': this.apiKey, 'Accept': 'application/json' } } ); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json() as UserGroupedData; } catch (error) { console.error('PortkeyService Error:', error); throw new Error('Failed to fetch user grouped data from Portkey API'); } } async listWorkspaces(params?: ListWorkspacesParams): Promise<ListWorkspacesResponse> { try { const queryParams = new URLSearchParams(); if (params?.page_size) { queryParams.append('page_size', params.page_size.toString()); } if (params?.current_page) { queryParams.append('current_page', params.current_page.toString()); } const url = `${this.baseUrl}/admin/workspaces${queryParams.toString() ? '?' + queryParams.toString() : ''}`; const response = await fetch(url, { method: 'GET', headers: { 'x-portkey-api-key': this.apiKey, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json() as ListWorkspacesResponse; } catch (error) { console.error('PortkeyService Error:', error); throw new Error('Failed to fetch workspaces from Portkey API'); } } async getWorkspace(workspaceId: string): Promise<SingleWorkspaceResponse> { try { const response = await fetch(`${this.baseUrl}/admin/workspaces/${workspaceId}`, { method: 'GET', headers: { 'x-portkey-api-key': this.apiKey, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json() as SingleWorkspaceResponse; } catch (error) { console.error('PortkeyService Error:', error); throw new Error('Failed to fetch workspace details from Portkey API'); } } async listConfigs(): Promise<ListConfigsResponse> { try { const response = await fetch(`${this.baseUrl}/configs`, { method: 'GET', headers: { 'x-portkey-api-key': this.apiKey, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json() as ListConfigsResponse; } catch (error) { console.error('PortkeyService Error:', error); throw new Error('Failed to fetch configurations from Portkey API'); } } async listVirtualKeys(): Promise<ListVirtualKeysResponse> { try { const response = await fetch(`${this.baseUrl}/virtual-keys`, { method: 'GET', headers: { 'x-portkey-api-key': this.apiKey, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json() as ListVirtualKeysResponse; } catch (error) { console.error('PortkeyService Error:', error); throw new Error('Failed to fetch virtual keys from Portkey API'); } } async getCostAnalytics(params: CostAnalyticsParams): Promise<CostAnalyticsResponse> { try { const queryParams = new URLSearchParams({ time_of_generation_min: params.time_of_generation_min, time_of_generation_max: params.time_of_generation_max, ...(params.total_units_min && { total_units_min: params.total_units_min.toString() }), ...(params.total_units_max && { total_units_max: params.total_units_max.toString() }), ...(params.cost_min && { cost_min: params.cost_min.toString() }), ...(params.cost_max && { cost_max: params.cost_max.toString() }), ...(params.prompt_token_min && { prompt_token_min: params.prompt_token_min.toString() }), ...(params.prompt_token_max && { prompt_token_max: params.prompt_token_max.toString() }), ...(params.completion_token_min && { completion_token_min: params.completion_token_min.toString() }), ...(params.completion_token_max && { completion_token_max: params.completion_token_max.toString() }), ...(params.status_code && { status_code: params.status_code }), ...(params.weighted_feedback_min && { weighted_feedback_min: params.weighted_feedback_min.toString() }), ...(params.weighted_feedback_max && { weighted_feedback_max: params.weighted_feedback_max.toString() }), ...(params.virtual_keys && { virtual_keys: params.virtual_keys }), ...(params.configs && { configs: params.configs }), ...(params.workspace_slug && { workspace_slug: params.workspace_slug }), ...(params.api_key_ids && { api_key_ids: params.api_key_ids }), ...(params.metadata && { metadata: params.metadata }), ...(params.ai_org_model && { ai_org_model: params.ai_org_model }), ...(params.trace_id && { trace_id: params.trace_id }), ...(params.span_id && { span_id: params.span_id }) }); const response = await fetch( `${this.baseUrl}/analytics/graphs/cost?${queryParams.toString()}`, { method: 'GET', headers: { 'x-portkey-api-key': this.apiKey, 'Accept': 'application/json' } } ); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } return await response.json() as CostAnalyticsResponse; } catch (error) { console.error('PortkeyService Error:', error); throw new Error('Failed to fetch cost analytics from Portkey API'); } } async getConfig(slug: string): Promise<GetConfigResponse> { try { const response = await fetch(`${this.baseUrl}/configs/${slug}`, { method: 'GET', headers: { 'x-portkey-api-key': this.apiKey, 'Accept': 'application/json' } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); return data; } catch (error) { console.error('PortkeyService Error:', error); throw new Error('Failed to fetch configuration details from Portkey API'); } } }