Skip to main content
Glama
api.ts8.04 kB
import type { ToolGroup, ToolExtension, ToolExtensions, ConsumerConfig, Permissions, CreatePermissionConsumerRequest, } from "@mcpx/shared-model"; import { singleToolGroupSchema, toolExtensionSchema, toolExtensionsSchema, consumerConfigSchema, permissionsSchema, } from "@mcpx/shared-model"; import z from "zod/v4"; import { getMcpxServerURL } from "@/config/api-config"; import { targetServerAttributesSchema } from "@mcpx/shared-model"; class ApiClient { private getBaseUrl: () => string; constructor(getBaseUrl: () => string) { this.getBaseUrl = getBaseUrl; } private get baseUrl(): string { return this.getBaseUrl() || getMcpxServerURL("http"); } private async request<T>(endpoint: string, schema: z.ZodType<T>): Promise<T> { const response = await fetch(`${this.baseUrl}${endpoint}`, { credentials: "include", }); if (!response.ok) { throw new Error( `API request failed: ${response.status} ${response.statusText}`, ); } const data = await response.json(); const result = schema.safeParse(data); if (!result.success) { console.error(`Schema validation failed for ${endpoint}:`, { error: result.error, data, }); throw result.error; } return result.data; } private async requestWithBody<T>( endpoint: string, method: "POST" | "PUT", body: unknown, schema: z.ZodType<T>, ): Promise<T> { const response = await fetch(`${this.baseUrl}${endpoint}`, { method, headers: { "Content-Type": "application/json", }, credentials: "include", body: JSON.stringify(body), }); if (!response.ok) { throw new Error( `API request failed: ${response.status} ${response.statusText}`, ); } const responseData = await response.json(); const result = schema.safeParse(responseData); if (!result.success) { console.error(`Schema validation failed for ${method} ${endpoint}:`, { error: result.error, responseData, requestBody: body, }); throw result.error; } return result.data; } // ==================== TOOL GROUPS ==================== async getToolGroups(): Promise<ToolGroup[]> { return this.request("/config/tool-groups", z.array(singleToolGroupSchema)); } async getToolGroup(name: string): Promise<ToolGroup> { return this.request(`/config/tool-groups/${name}`, singleToolGroupSchema); } async createToolGroup(toolGroup: ToolGroup): Promise<ToolGroup> { return this.requestWithBody( "/config/tool-groups", "POST", toolGroup, singleToolGroupSchema, ); } async updateToolGroup( name: string, updates: Omit<ToolGroup, "name">, ): Promise<ToolGroup> { return this.requestWithBody( `/config/tool-groups/${name}`, "PUT", updates, singleToolGroupSchema, ); } async deleteToolGroup(name: string): Promise<void> { const response = await fetch(`${this.baseUrl}/config/tool-groups/${name}`, { method: "DELETE", credentials: "include", }); if (!response.ok) { // If group not found (404), that's okay - it's already deleted if (response.status === 404) { return; } throw new Error( `API request failed: ${response.status} ${response.statusText}`, ); } } // ==================== TOOL EXTENSIONS ==================== async getToolExtensions(): Promise<ToolExtensions> { return this.request("/config/tool-extensions", toolExtensionsSchema); } async getToolExtension( serverName: string, originalToolName: string, customToolName: string, ): Promise<ToolExtension> { return this.request( `/config/tool-extensions/${serverName}/${originalToolName}/${customToolName}`, toolExtensionSchema, ); } async createToolExtension( serverName: string, originalToolName: string, extension: ToolExtension, ): Promise<ToolExtension> { return this.requestWithBody( `/config/tool-extensions/${serverName}/${originalToolName}`, "POST", extension, toolExtensionSchema, ); } async updateToolExtension( serverName: string, originalToolName: string, customToolName: string, updates: Omit<ToolExtension, "name">, ): Promise<ToolExtension> { return this.requestWithBody( `/config/tool-extensions/${serverName}/${originalToolName}/${customToolName}`, "PUT", updates, toolExtensionSchema, ); } async deleteToolExtension( serverName: string, originalToolName: string, customToolName: string, ): Promise<void> { const response = await fetch( `${this.baseUrl}/config/tool-extensions/${serverName}/${originalToolName}/${customToolName}`, { method: "DELETE", credentials: "include", }, ); if (!response.ok) { // If tool extension not found (404), that's okay - it's already deleted if (response.status === 404) { return; } throw new Error( `API request failed: ${response.status} ${response.statusText}`, ); } } // ==================== PERMISSIONS ==================== async getPermissions(): Promise<Permissions> { return this.request("/config/permissions", permissionsSchema); } async getDefaultPermission(): Promise<ConsumerConfig> { return this.request("/config/permissions/default", consumerConfigSchema); } async updateDefaultPermission( config: ConsumerConfig, ): Promise<ConsumerConfig> { return this.requestWithBody( "/config/permissions/default", "PUT", config, consumerConfigSchema, ); } async getPermissionConsumers(): Promise<Record<string, ConsumerConfig>> { return this.request( "/config/permissions/consumers", z.record(z.string(), consumerConfigSchema), ); } async getPermissionConsumer(consumerName: string): Promise<ConsumerConfig> { return this.request( `/config/permissions/consumers/${consumerName}`, consumerConfigSchema, ); } async createPermissionConsumer( request: CreatePermissionConsumerRequest, ): Promise<ConsumerConfig> { return this.requestWithBody( "/config/permissions/consumers", "POST", request, consumerConfigSchema, ); } async updatePermissionConsumer( consumerName: string, config: ConsumerConfig, ): Promise<ConsumerConfig> { return this.requestWithBody( `/config/permissions/consumers/${consumerName}`, "PUT", config, consumerConfigSchema, ); } async deletePermissionConsumer(consumerName: string): Promise<void> { const response = await fetch( `${this.baseUrl}/config/permissions/consumers/${consumerName}`, { method: "DELETE", credentials: "include", }, ); if (!response.ok) { // If consumer not found (404), that's okay - it's already deleted if (response.status === 404) { return; } throw new Error( `API request failed: ${response.status} ${response.statusText}`, ); } } // ==================== TARGET SERVER ATTRIBUTES ==================== async getTargetServerAttributes(): Promise< Record<string, { inactive: boolean }> > { return this.request( "/config/target-servers/attributes", targetServerAttributesSchema, ); } async activateTargetServer(name: string): Promise<{ message: string }> { return this.request( `/config/target-server/${name}/activate`, z.object({ message: z.string() }), ); } async deactivateTargetServer(name: string): Promise<{ message: string }> { return this.request( `/config/target-server/${name}/deactivate`, z.object({ message: z.string() }), ); } } // Initialize with getMcpxServerURL as fallback export const apiClient = new ApiClient(() => getMcpxServerURL("http"));

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/TheLunarCompany/lunar'

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