Skip to main content
Glama
tool-step-builder.ts5.5 kB
import { ApiConfig, ExecutionStep, HttpMethod, Integration, Pagination } from "@superglue/client"; import { LLMMessage, LLMToolWithContext } from "../llm/llm-base-model.js"; import { LanguageModel } from "../llm/llm-base-model.js"; import { getWebSearchTool, searchDocumentationToolDefinition } from "../llm/llm-tools.js"; import { z } from "zod"; import { zodToJsonSchema } from "zod-to-json-schema"; type GenerateStepConfigInput = { retryCount: number; messages: LLMMessage[]; integration: Integration; } export interface GenerateStepConfigResult { success: boolean; error?: string; config?: Partial<ApiConfig>; dataSelector?: string; messages?: LLMMessage[]; } const stepConfigSchema = z.object({ dataSelector: z.string().describe("JavaScript function that returns OBJECT for direct execution or ARRAY for loop execution. If returns OBJECT (including {}), step executes once with object as currentItem. If returns ARRAY, step executes once per array item. Examples: (sourceData) => ({ userId: sourceData.userId }) OR (sourceData) => sourceData.getContacts.data.filter(c => c.active)"), apiConfig: z.object({ urlHost: z.string().describe("The base URL host (e.g., https://api.example.com). Must not be empty."), urlPath: z.string().describe("The API endpoint path (e.g., /v1/users)."), method: z.enum(["GET", "POST", "PUT", "DELETE", "PATCH"] as [string, ...string[]]).describe("HTTP method: GET, POST, PUT, DELETE, or PATCH"), queryParams: z.array(z.object({ key: z.string(), value: z.string() })).optional().describe("Query parameters as key-value pairs. If pagination is configured, ensure you have included the right pagination parameters here or in the body."), headers: z.array(z.object({ key: z.string(), value: z.string() })).optional().describe("HTTP headers as key-value pairs. Use <<variable>> syntax for dynamic values or JavaScript expressions"), body: z.string().optional().describe("Request body. Use <<variable>> syntax for dynamic values. If pagination is configured, ensure you have included the right pagination parameters here or in the queryParams."), pagination: z.object({ type: z.enum(["OFFSET_BASED", "PAGE_BASED", "CURSOR_BASED"]), pageSize: z.string().describe("Number of items per page (e.g., '50', '100'). Once set, this becomes available as <<limit>> (same as pageSize)."), cursorPath: z.string().describe("If cursor_based: The JSONPath to the cursor in the response. If not, set this to \"\""), stopCondition: z.string().describe("REQUIRED: JavaScript function that determines when to stop pagination. This is the primary control for pagination. Format: (response, pageInfo) => boolean. The pageInfo object contains: page (number), offset (number), cursor (any), totalFetched (number). response is the axios response object, access response data via response.data. Return true to STOP. E.g. (response, pageInfo) => !response.data.pagination.has_more") }).optional().describe("OPTIONAL: Only configure if you are using pagination variables in the URL, headers, or body. For OFFSET_BASED, ALWAYS use <<offset>>. If PAGE_BASED, ALWAYS use <<page>>. If CURSOR_BASED, ALWAYS use <<cursor>>.") }).describe("Complete API configuration to execute") }); export async function generateStepConfig({ retryCount, messages, integration }: GenerateStepConfigInput): Promise<GenerateStepConfigResult> { const temperature = Math.min(retryCount * 0.1, 1); const webSearchTool = getWebSearchTool(); const tools: LLMToolWithContext[] = [{toolDefinition: searchDocumentationToolDefinition, toolContext: { integration }}]; if (webSearchTool) { tools.push({ toolDefinition: { web_search: webSearchTool }, toolContext: {} }); } const generateStepConfigResult = await LanguageModel.generateObject<z.infer<typeof stepConfigSchema>>({ messages, schema: zodToJsonSchema(stepConfigSchema), temperature, tools }); if (!generateStepConfigResult.success) { return { success: false, error: generateStepConfigResult.response as string, messages: generateStepConfigResult.messages }; } const generatedConfig = generateStepConfigResult.response.apiConfig; const generatedDataSelector = generateStepConfigResult.response.dataSelector; if (!generatedConfig) { return { success: false, error: "LLM returned invalid response: stepConfig is missing", messages: generateStepConfigResult.messages }; } const config: Partial<ApiConfig> = { urlHost: generatedConfig.urlHost, urlPath: generatedConfig.urlPath, method: generatedConfig.method as HttpMethod, queryParams: generatedConfig.queryParams ? Object.fromEntries(generatedConfig.queryParams.map((p: any) => [p.key, p.value])) : undefined, headers: generatedConfig.headers ? Object.fromEntries(generatedConfig.headers.map((p: any) => [p.key, p.value])) : undefined, body: generatedConfig.body, pagination: generatedConfig.pagination as Pagination, }; return { success: true, config: config, dataSelector: generatedDataSelector, messages: generateStepConfigResult.messages }; };

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