Skip to main content
Glama
superglue-client.ts39.1 kB
import axios from "axios"; import { ApiCallArgs, ApiConfig, ApiInputRequest, ApiResult, BuildToolArgs, CallEndpointArgs, CallEndpointResult, ExtractArgs, ExtractInputRequest, ExtractResult, GenerateStepConfigArgs, Integration, Log, Run, SuggestedTool, Tool, ToolArgs, ToolInputRequest, ToolResult, ToolSchedule, ToolScheduleInput, ToolStepResult, UpsertMode } from "./types.js"; import { LogSubscriptionOptions, WebSocketManager, WebSocketSubscription } from "./websocket-manager.js"; export class SuperglueClient { private endpoint: string; private apiKey: string; private wsManager: WebSocketManager; private apiEndpoint: string; private static workflowQL = ` id version createdAt updatedAt steps { id modify apiConfig { id urlHost urlPath instruction method queryParams headers body documentationUrl responseSchema responseMapping authentication pagination { type pageSize cursorPath stopCondition } dataPath } integrationId executionMode loopSelector loopMaxIters inputMapping responseMapping failureBehavior } integrationIds responseSchema originalResponseSchema finalTransform inputSchema instruction folder archived `; private static workflowScheduleQL = ` id workflowId cronExpression timezone enabled payload options lastRunAt nextRunAt createdAt updatedAt `; private static configQL = ` config { ... on ApiConfig { id version createdAt updatedAt urlHost urlPath instruction method queryParams headers body documentationUrl responseSchema responseMapping authentication pagination { type pageSize cursorPath stopCondition } dataPath } ... on Workflow { ${SuperglueClient.workflowQL} } } `; constructor({endpoint, apiKey, apiEndpoint}: {endpoint?: string, apiKey: string, apiEndpoint?: string}) { this.endpoint = endpoint ?? 'https://graphql.superglue.cloud'; this.apiKey = apiKey; this.apiEndpoint = apiEndpoint ?? 'https://api.superglue.cloud'; this.wsManager = new WebSocketManager(this.endpoint, this.apiKey); } protected async restRequest<T>( method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', path: string, body?: any ): Promise<T> { const url = `${this.apiEndpoint.replace(/\/$/, '')}${path}`; const headers: Record<string, string> = { 'Authorization': `Bearer ${this.apiKey}`, }; if (body && method !== 'GET') { headers['Content-Type'] = 'application/json'; } const response = await fetch(url, { method, headers, body: body ? JSON.stringify(body) : undefined, }); if (!response.ok) { const errorText = await response.text(); let errorMessage = `HTTP ${response.status}: ${errorText}`; try { const errorJson = JSON.parse(errorText); errorMessage = (errorJson as any).error || errorMessage; } catch { // ignore } throw new Error(errorMessage); } const data = await response.json(); return data as T; } private async request<T>(query: string, variables?: Record<string, any>): Promise<T> { try { const response = await axios.post(this.endpoint, { query, variables, }, { headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}`, } }); if(response.data.errors) { throw new Error(response.data.errors[0].message); } const json = response.data; return json.data as T; } catch (error) { console.error(error); throw error; } } private async graphQL<T = any>(query: string, variables?: any): Promise<T> { const res = await fetch(`${this.endpoint.replace(/\/$/, '')}/graphql`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${this.apiKey}`, }, body: JSON.stringify({ query, variables }) }); if (!res.ok) throw new Error(`GraphQL ${res.status}`); const json = await res.json(); if (json.errors && json.errors.length) throw new Error(json.errors[0]?.message || 'GraphQL error'); return json.data as T; } async subscribeToLogs(options: LogSubscriptionOptions = {}): Promise<WebSocketSubscription> { return this.wsManager.subscribeToLogs(options); } async disconnect(): Promise<void> { return this.wsManager.disconnect(); } async executeWorkflow<T = any>({ id, tool, payload, credentials, options, verbose = true, runId, traceId }: ToolArgs): Promise<ToolResult & { data?: T }> { const mutation = ` mutation ExecuteWorkflow($input: WorkflowInputRequest!, $payload: JSON, $credentials: JSON, $options: RequestOptions, $runId: ID, $traceId: ID) { executeWorkflow(input: $input, payload: $payload, credentials: $credentials, options: $options, runId: $runId, traceId: $traceId) { id success data config {${SuperglueClient.workflowQL}} stepResults { stepId success rawData transformedData error } error startedAt completedAt } } `; let gqlInput: Partial<ToolInputRequest> = {}; if (id) { gqlInput = { id }; } else if (tool) { const toolInput = { id: tool.id, steps: tool.steps.map(step => { const apiConfigInput = { id: step.apiConfig.id, urlHost: step.apiConfig.urlHost, instruction: step.apiConfig.instruction, urlPath: step.apiConfig.urlPath, method: step.apiConfig.method, queryParams: step.apiConfig.queryParams, headers: step.apiConfig.headers, body: step.apiConfig.body, documentationUrl: step.apiConfig.documentationUrl, responseSchema: step.apiConfig.responseSchema, responseMapping: step.apiConfig.responseMapping, authentication: step.apiConfig.authentication, pagination: step.apiConfig.pagination ? { type: step.apiConfig.pagination.type, ...(step.apiConfig.pagination.pageSize !== undefined && { pageSize: step.apiConfig.pagination.pageSize }), ...(step.apiConfig.pagination.cursorPath !== undefined && { cursorPath: step.apiConfig.pagination.cursorPath }), ...(step.apiConfig.pagination.stopCondition !== undefined && { stopCondition: step.apiConfig.pagination.stopCondition }), } : undefined, dataPath: step.apiConfig.dataPath, version: step.apiConfig.version, }; Object.keys(apiConfigInput).forEach(key => (apiConfigInput as any)[key] === undefined && delete (apiConfigInput as any)[key]); const executionStepInput = { id: step.id, modify: step.modify, apiConfig: apiConfigInput, integrationId: step.integrationId, executionMode: step.executionMode, loopSelector: step.loopSelector, loopMaxIters: step.loopMaxIters, inputMapping: step.inputMapping, responseMapping: step.responseMapping, failureBehavior: step.failureBehavior, }; Object.keys(executionStepInput).forEach(key => (executionStepInput as any)[key] === undefined && delete (executionStepInput as any)[key]); return executionStepInput; }), integrationIds: tool.integrationIds, finalTransform: tool.finalTransform, inputSchema: tool.inputSchema, responseSchema: tool.responseSchema, instruction: tool.instruction, }; Object.keys(toolInput).forEach(key => (toolInput as any)[key] === undefined && delete (toolInput as any)[key]); gqlInput = { workflow: toolInput }; } else { throw new Error("Either id or tool must be provided for executeWorkflow."); } let logSubscription: WebSocketSubscription | undefined; if (verbose) { try { logSubscription = await this.subscribeToLogs({ onLog: (log: Log) => { const timestamp = log.timestamp.toLocaleTimeString(); const levelColor = log.level === 'ERROR' ? '\x1b[31m' : log.level === 'WARN' ? '\x1b[33m' : log.level === 'DEBUG' ? '\x1b[36m' : '\x1b[0m'; console.log(`${levelColor}[${timestamp}] ${log.level}\x1b[0m: ${log.message}`); }, onError: (error: Error) => { console.error('Log subscription error:', error); }, includeDebug: true }); } catch (error) { console.error('Log subscription error:', error); } } try { type GraphQLWorkflowResult = Omit<ToolResult, 'stepResults'> & { data?: any, stepResults: (ToolStepResult & { rawData: any, transformedData: any })[] }; const result = await this.request<{ executeWorkflow: GraphQLWorkflowResult }>(mutation, { input: gqlInput, payload, credentials, options, runId, traceId }).then(data => data.executeWorkflow); if (result.error) { throw new Error(result.error); } result.stepResults.forEach(stepResult => { stepResult.data = stepResult.transformedData; }); return result as ToolResult & { data?: T }; } finally { if (logSubscription) { setTimeout(() => { logSubscription.unsubscribe(); }, 1000); } } } async abortToolExecution(runId: string): Promise<{ success: boolean; runId: string }> { const mutation = ` mutation AbortToolExecution($runId: ID!) { abortToolExecution(runId: $runId) { success runId } } `; const response = await this.request<{ abortToolExecution: { success: boolean; runId: string } }>(mutation, { runId }); return response.abortToolExecution; } async buildWorkflow({instruction, payload, integrationIds, responseSchema, save = true, verbose = true, traceId}: BuildToolArgs): Promise<Tool> { const mutation = ` mutation BuildWorkflow($instruction: String!, $payload: JSON, $integrationIds: [ID!], $responseSchema: JSONSchema, $traceId: ID) { buildWorkflow(instruction: $instruction, payload: $payload, integrationIds: $integrationIds, responseSchema: $responseSchema, traceId: $traceId) {${SuperglueClient.workflowQL}} } `; let logSubscription: WebSocketSubscription | undefined; if (verbose) { try { logSubscription = await this.subscribeToLogs({ onLog: (log: Log) => { const timestamp = log.timestamp.toLocaleTimeString(); const levelColor = log.level === 'ERROR' ? '\x1b[31m' : log.level === 'WARN' ? '\x1b[33m' : log.level === 'DEBUG' ? '\x1b[36m' : '\x1b[0m'; console.log(`${levelColor}[${timestamp}] ${log.level}\x1b[0m: ${log.message}`); }, onError: (error: Error) => { console.error('Log subscription error:', error); }, includeDebug: true }); } catch (error) { console.error('Log subscription error:', error); } } try { const workflow = await this.request<{ buildWorkflow: Tool }>(mutation, { instruction, payload, integrationIds, responseSchema: responseSchema ?? {}, traceId }).then(data => data.buildWorkflow); if (save) { await this.upsertWorkflow(workflow.id, workflow); } return workflow; } finally { if (logSubscription) { setTimeout(() => { logSubscription.unsubscribe(); }, 2000); } } } async generateStepConfig({ integrationId, currentStepConfig, currentDataSelector, stepInput, credentials, errorMessage }: GenerateStepConfigArgs): Promise<{config: ApiConfig, dataSelector: string}> { const mutation = ` mutation GenerateStepConfig( $integrationId: String, $currentStepConfig: JSON, $currentDataSelector: String, $stepInput: JSON, $credentials: JSON, $errorMessage: String ) { generateStepConfig( integrationId: $integrationId, currentStepConfig: $currentStepConfig, currentDataSelector: $currentDataSelector, stepInput: $stepInput, credentials: $credentials, errorMessage: $errorMessage ) { config { id version createdAt updatedAt urlHost urlPath instruction method queryParams headers body documentationUrl responseSchema responseMapping authentication pagination { type pageSize cursorPath stopCondition } dataPath } dataSelector } } `; const result = await this.request<{ generateStepConfig: { config: ApiConfig, dataSelector: string } }>(mutation, { integrationId, currentStepConfig, currentDataSelector, stepInput, credentials, errorMessage }); return { config: result.generateStepConfig.config, dataSelector: result.generateStepConfig.dataSelector }; } async callEndpoint(args: CallEndpointArgs): Promise<CallEndpointResult> { const mutation = ` mutation CallEndpoint($integrationId: ID, $method: HttpMethod!, $url: String!, $headers: JSON, $body: String, $timeout: Int) { callEndpoint(integrationId: $integrationId, method: $method, url: $url, headers: $headers, body: $body, timeout: $timeout) { success status statusText headers body error duration } } `; const result = await this.request<{ callEndpoint: CallEndpointResult }>(mutation, args); return result.callEndpoint; } async call<T = unknown>({ id, endpoint, payload, credentials, options }: ApiCallArgs): Promise<ApiResult & { data: T }> { const mutation = ` mutation Call($input: ApiInputRequest!, $payload: JSON, $credentials: JSON, $options: RequestOptions) { call(input: $input, payload: $payload, credentials: $credentials, options: $options) { id success data error headers statusCode startedAt completedAt ${SuperglueClient.configQL} } } `; let gqlInput: Partial<ApiInputRequest> = {}; if (id) { gqlInput = { id }; } else if (endpoint) { const apiInput = { id: endpoint.id, urlHost: endpoint.urlHost, instruction: endpoint.instruction, urlPath: endpoint.urlPath, method: endpoint.method, queryParams: endpoint.queryParams, headers: endpoint.headers, body: endpoint.body, documentationUrl: endpoint.documentationUrl, responseSchema: endpoint.responseSchema, responseMapping: endpoint.responseMapping, authentication: endpoint.authentication, pagination: endpoint.pagination ? { type: endpoint.pagination.type, ...(endpoint.pagination.pageSize !== undefined && { pageSize: endpoint.pagination.pageSize }), ...(endpoint.pagination.cursorPath !== undefined && { cursorPath: endpoint.pagination.cursorPath }), ...(endpoint.pagination.stopCondition !== undefined && { stopCondition: endpoint.pagination.stopCondition }), } : undefined, dataPath: endpoint.dataPath, version: endpoint.version, }; Object.keys(apiInput).forEach(key => (apiInput as any)[key] === undefined && delete (apiInput as any)[key]); gqlInput = { endpoint: apiInput }; } else { throw new Error("Either id or endpoint must be provided for call."); } const result = await this.request<{ call: ApiResult & { data: T } }>(mutation, { input: gqlInput, payload, credentials, options }).then(data => data?.call); if (result.error) { throw new Error(result.error); } return result; } async extract<T = any>({ id, endpoint, file, payload, credentials, options }: ExtractArgs): Promise<ExtractResult & { data: T }> { const mutation = ` mutation Extract($input: ExtractInputRequest!, $payload: JSON, $credentials: JSON, $options: RequestOptions) { extract(input: $input, payload: $payload, credentials: $credentials, options: $options) { id success data error startedAt completedAt ${SuperglueClient.configQL} } } `; if (file) { const operations = { query: mutation, variables: { input: { file: null }, payload, credentials, options } }; const formData = new FormData(); formData.append('operations', JSON.stringify(operations)); formData.append('map', JSON.stringify({ "0": ["variables.input.file"] })); formData.append('0', file); const response = await axios.post(this.endpoint, formData, { headers: { 'Authorization': `Bearer ${this.apiKey}`, } }); if (response.data.errors) { throw new Error(response.data.errors[0].message); } return response.data.data.extract; } let gqlInput: Partial<ExtractInputRequest> = {}; if (id) { gqlInput = { id }; } else if (endpoint) { const extractInput = { id: endpoint.id, urlHost: endpoint.urlHost, instruction: endpoint.instruction, urlPath: endpoint.urlPath, queryParams: endpoint.queryParams, method: endpoint.method, headers: endpoint.headers, body: endpoint.body, documentationUrl: endpoint.documentationUrl, decompressionMethod: endpoint.decompressionMethod, fileType: endpoint.fileType, authentication: endpoint.authentication, dataPath: endpoint.dataPath, version: endpoint.version, }; Object.keys(extractInput).forEach(key => (extractInput as any)[key] === undefined && delete (extractInput as any)[key]); gqlInput = { endpoint: extractInput }; } else { throw new Error("Either id, endpoint, or file must be provided for extract."); } return this.request<{ extract: ExtractResult & { data: T } }>(mutation, { input: gqlInput, payload, credentials, options }).then(data => data.extract); } async listRuns(limit: number = 100, offset: number = 0, configId?: string): Promise<{ items: Run[], total: number }> { const query = ` query ListRuns($limit: Int!, $offset: Int!, $configId: ID) { listRuns(limit: $limit, offset: $offset, configId: $configId) { items { id toolId status toolResult stepResults { stepId success error } error startedAt completedAt toolConfig { ${SuperglueClient.workflowQL} } } total } } `; const response = await this.request<{ listRuns: { items: Run[], total: number } }>(query, { limit, offset, configId }); return response.listRuns; } async getRun(id: string): Promise<Run> { const query = ` query GetRun($id: ID!) { getRun(id: $id) { id toolId status toolResult toolPayload stepResults { stepId success transformedData error } options error startedAt completedAt toolConfig { ${SuperglueClient.workflowQL} } } } `; const response = await this.request<{ getRun: Run }>(query, { id }); const run = response.getRun; if (run.stepResults) { run.stepResults.forEach((stepResult: any) => { stepResult.data = stepResult.transformedData; }); } return run; } async listApis(limit: number = 10, offset: number = 0): Promise<{ items: ApiConfig[], total: number }> { const query = ` query ListApis($limit: Int!, $offset: Int!) { listApis(limit: $limit, offset: $offset) { items { id version createdAt updatedAt urlHost urlPath instruction method queryParams headers body documentationUrl responseSchema responseMapping authentication pagination { type pageSize cursorPath stopCondition } dataPath } total } } `; const response = await this.request<{ listApis: { items: ApiConfig[], total: number } }>(query, { limit, offset }); return response.listApis; } async getApi(id: string): Promise<ApiConfig> { const query = ` query GetApi($id: ID!) { getApi(id: $id) { id version createdAt updatedAt urlHost urlPath instruction method queryParams headers body documentationUrl responseSchema responseMapping authentication pagination { type pageSize cursorPath stopCondition } dataPath } } `; const response = await this.request<{ getApi: ApiConfig }>(query, { id }); return response.getApi; } async getWorkflow(id: string): Promise<Tool> { const query = ` query GetWorkflow($id: ID!) { getWorkflow(id: $id) { id version createdAt updatedAt steps { id modify apiConfig { id version createdAt updatedAt urlHost urlPath instruction method queryParams headers body documentationUrl responseSchema responseMapping authentication pagination { type pageSize cursorPath stopCondition } dataPath } integrationId executionMode loopSelector loopMaxIters inputMapping responseMapping failureBehavior } integrationIds finalTransform inputSchema responseSchema instruction folder archived } } `; const response = await this.request<{ getWorkflow: Tool }>(query, { id }); return response.getWorkflow; } async archiveWorkflow(id: string, archived: boolean = true): Promise<Tool> { return this.upsertWorkflow(id, { archived }); } async listWorkflows(limit: number = 10, offset: number = 0): Promise<{ items: Tool[], total: number }> { const query = ` query ListWorkflows($limit: Int!, $offset: Int!) { listWorkflows(limit: $limit, offset: $offset) { items {${SuperglueClient.workflowQL}} total } } `; const response = await this.request<{ listWorkflows: { items: Tool[], total: number } }>(query, { limit, offset }); return response.listWorkflows; } async listWorkflowSchedules(workflowId?: string): Promise<ToolSchedule[]> { const query = ` query ListWorkflowSchedules ($workflowId: String) { listWorkflowSchedules(workflowId: $workflowId) { ${SuperglueClient.workflowScheduleQL} } } `; const response = await this.request<{ listWorkflowSchedules: ToolSchedule[] }>(query, { workflowId }); return response.listWorkflowSchedules; } async upsertWorkflowSchedule(schedule: ToolScheduleInput): Promise<ToolSchedule> { const mutation = ` mutation UpsertWorkflowSchedule($schedule: WorkflowScheduleInput!) { upsertWorkflowSchedule(schedule: $schedule) { ${SuperglueClient.workflowScheduleQL} } } `; const response = await this.request<{ upsertWorkflowSchedule: ToolSchedule }>(mutation, { schedule }); return response.upsertWorkflowSchedule; } async deleteWorkflowSchedule(id: string): Promise<boolean> { const mutation = ` mutation DeleteWorkflowSchedule($id: ID!) { deleteWorkflowSchedule(id: $id) } `; const response = await this.request<{ deleteWorkflowSchedule: boolean }>(mutation, { id }); return response.deleteWorkflowSchedule; } async upsertApi(id: string, input: Partial<ApiConfig>): Promise<ApiConfig> { const mutation = ` mutation UpsertApi($id: ID!, $input: JSON!) { upsertApi(id: $id, input: $input) { id version createdAt updatedAt urlHost urlPath instruction method queryParams headers body documentationUrl responseSchema responseMapping authentication pagination { type pageSize cursorPath stopCondition } dataPath } } `; const response = await this.request<{ upsertApi: ApiConfig }>(mutation, { id, input }); return response.upsertApi; } async deleteApi(id: string): Promise<boolean> { const mutation = ` mutation DeleteApi($id: ID!) { deleteApi(id: $id) } `; const response = await this.request<{ deleteApi: boolean }>(mutation, { id }); return response.deleteApi; } async updateApiConfigId(oldId: string, newId: string): Promise<ApiConfig> { const mutation = ` mutation UpdateApiConfigId($oldId: ID!, $newId: ID!) { updateApiConfigId(oldId: $oldId, newId: $newId) { id version createdAt updatedAt urlHost urlPath instruction method queryParams headers body documentationUrl responseSchema responseMapping authentication pagination { type pageSize cursorPath stopCondition } dataPath } } `; const response = await this.request<{ updateApiConfigId: ApiConfig }>(mutation, { oldId, newId }); return response.updateApiConfigId; } async generateSchema(instruction: string, responseData: string): Promise<any> { const query = ` query GenerateSchema($instruction: String!, $responseData: String) { generateSchema(instruction: $instruction, responseData: $responseData) } `; const response = await this.request<{ generateSchema: string }>(query, { instruction, responseData }); return response.generateSchema; } async upsertWorkflow(id: string, input: Partial<Tool>): Promise<Tool> { const mutation = ` mutation UpsertWorkflow($id: ID!, $input: JSON!) { upsertWorkflow(id: $id, input: $input) {${SuperglueClient.workflowQL}} } `; return this.request<{ upsertWorkflow: Tool }>(mutation, { id, input }) .then(data => data.upsertWorkflow); } async deleteWorkflow(id: string): Promise<boolean> { const mutation = ` mutation DeleteWorkflow($id: ID!) { deleteWorkflow(id: $id) } `; return this.request<{ deleteWorkflow: boolean }>(mutation, { id }) .then(data => data.deleteWorkflow); } async renameWorkflow(oldId: string, newId: string): Promise<Tool> { const mutation = ` mutation RenameWorkflow($oldId: ID!, $newId: ID!) { renameWorkflow(oldId: $oldId, newId: $newId) {${SuperglueClient.workflowQL}} } `; return this.request<{ renameWorkflow: Tool }>(mutation, { oldId, newId }) .then(data => data.renameWorkflow); } async listIntegrations(limit: number = 10, offset: number = 0): Promise<{ items: Integration[], total: number }> { const query = ` query ListIntegrations($limit: Int!, $offset: Int!) { listIntegrations(limit: $limit, offset: $offset) { items { id name type urlHost urlPath credentials documentationUrl documentationPending openApiSchema openApiUrl specificInstructions documentationKeywords icon version createdAt updatedAt } total } } `; const response = await this.request<{ listIntegrations: { items: Integration[], total: number } }>(query, { limit, offset }); return response.listIntegrations; } async findRelevantIntegrations(searchTerms: string): Promise<Integration[]> { const query = ` query FindRelevantIntegrations($searchTerms: String) { findRelevantIntegrations(searchTerms: $searchTerms) { reason integration { id name type urlHost urlPath credentials documentationUrl documentation documentationPending openApiUrl openApiSchema specificInstructions documentationKeywords icon version createdAt updatedAt } } } `; const response = await this.request<{ findRelevantIntegrations: Integration[] }>(query, { searchTerms }); return response.findRelevantIntegrations; } async findRelevantTools(searchTerms?: string): Promise<SuggestedTool[]> { const query = ` query FindRelevantTools($searchTerms: String) { findRelevantTools(searchTerms: $searchTerms) { id instruction inputSchema responseSchema steps { integrationId instruction } reason } } `; const response = await this.request<{ findRelevantTools: SuggestedTool[] }>(query, { searchTerms }); return response.findRelevantTools; } async getIntegration(id: string): Promise<Integration> { const query = ` query GetIntegration($id: ID!) { getIntegration(id: $id) { id name type urlHost urlPath credentials documentationUrl documentation documentationPending openApiSchema openApiUrl specificInstructions documentationKeywords icon version createdAt updatedAt } } `; const response = await this.request<{ getIntegration: Integration }>(query, { id }); return response.getIntegration; } async upsertIntegration(id: string, input: Partial<Integration>, mode: UpsertMode = UpsertMode.UPSERT): Promise<Integration> { const mutation = ` mutation UpsertIntegration($input: IntegrationInput!, $mode: UpsertMode) { upsertIntegration(input: $input, mode: $mode) { id name type urlHost urlPath credentials documentationUrl documentation documentationPending openApiSchema openApiUrl specificInstructions documentationKeywords icon version createdAt updatedAt } } `; const integrationInput = { id, ...input }; const response = await this.request<{ upsertIntegration: Integration }>(mutation, { input: integrationInput, mode }); return response.upsertIntegration; } async deleteIntegration(id: string): Promise<boolean> { const mutation = ` mutation DeleteIntegration($id: ID!) { deleteIntegration(id: $id) } `; const response = await this.request<{ deleteIntegration: boolean }>(mutation, { id }); return response.deleteIntegration; } async cacheOauthClientCredentials(args: { clientCredentialsUid: string; clientId: string; clientSecret: string }): Promise<boolean> { const data = await this.graphQL<{ cacheOauthClientCredentials: boolean }>(` mutation CacheOauthClientCredentials($clientCredentialsUid: String!, $clientId: String!, $clientSecret: String!) { cacheOauthClientCredentials(clientCredentialsUid: $clientCredentialsUid, clientId: $clientId, clientSecret: $clientSecret) } `, args); return Boolean(data?.cacheOauthClientCredentials); } async getOAuthClientCredentials(args: { templateId?: string; clientCredentialsUid?: string }): Promise<{ client_id: string; client_secret: string }> { const data = await this.graphQL<{ getOAuthClientCredentials: { client_id: string; client_secret: string } }>(` mutation GetOAuthClientCredentials($templateId: ID, $clientCredentialsUid: String) { getOAuthClientCredentials(templateId: $templateId, clientCredentialsUid: $clientCredentialsUid) { client_id client_secret } } `, args); return data.getOAuthClientCredentials; } async searchIntegrationDocumentation(integrationId: string, keywords: string): Promise<string> { const data = await this.graphQL<{ searchIntegrationDocumentation: string }>(` query SearchIntegrationDocumentation($integrationId: ID!, $keywords: String!) { searchIntegrationDocumentation(integrationId: $integrationId, keywords: $keywords) } `, { integrationId, keywords }); return data.searchIntegrationDocumentation; } async generateInstructions(integrations: any[]): Promise<string[]> { const data = await this.graphQL<{ generateInstructions: string[] }>(` query GenerateInstructions($integrations: [IntegrationInput!]!) { generateInstructions(integrations: $integrations) } `, { integrations }); const instructions = data.generateInstructions; if (instructions.length === 1 && instructions[0].startsWith('Error:')) { throw new Error(instructions[0].replace('Error: ', '')); } return instructions; } async generateTransform(args: { currentTransform: string; responseSchema?: any; stepData: Record<string, any>; errorMessage?: string; instruction?: string; }): Promise<{ transformCode: string; data?: any }> { const mutation = ` mutation GenerateTransform( $currentTransform: String!, $responseSchema: JSONSchema, $stepData: JSON!, $errorMessage: String, $instruction: String ) { generateTransform( currentTransform: $currentTransform, responseSchema: $responseSchema, stepData: $stepData, errorMessage: $errorMessage, instruction: $instruction ) { transformCode data } } `; const response = await this.request<{ generateTransform: { transformCode: string; data?: any } }>(mutation, args); return response.generateTransform; } }

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/superglue-ai/superglue'

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