Skip to main content
Glama
bruno-tools.ts6.25 kB
import { BrunoParser } from "./bruno-parser.js"; import debug from "debug"; import { z } from "zod"; const log = debug("bruno:tools"); // Define our standard schema interface export interface BrunoToolSchema { environment?: string; variables?: Record<string, string>; body?: Record<string, any>; query?: Record<string, string>; } // Tool interface for MCP protocol export interface BrunoTool { name: string; description: string; schema: any; handler: (params: BrunoToolSchema) => Promise<any>; } /** * Options for creating Bruno tools */ export interface BrunoToolsOptions { collectionPath: string; environment?: string; filterRequests?: (name: string) => boolean; includeTools?: string[]; excludeTools?: string[]; } /** * Create tools from Bruno API requests * @param options - Options for creating tools * @returns Array of tools for use with MCP */ export async function createBrunoTools( options: BrunoToolsOptions ): Promise<BrunoTool[]> { const { collectionPath, environment, filterRequests, includeTools, excludeTools, } = options; if (!collectionPath) { throw new Error("Collection path is required"); } log(`Creating tools from Bruno collection at ${collectionPath}`); log(`Using environment: ${environment || "default"}`); // Initialize the Bruno parser const parser = new BrunoParser(collectionPath, environment); await parser.init(); const tools: BrunoTool[] = []; // Get available requests let availableRequests = parser.getAvailableRequests(); log(`Found ${availableRequests.length} available requests`); // Apply filter if provided if (filterRequests) { log("Applying filter to requests"); availableRequests = availableRequests.filter(filterRequests); log(`${availableRequests.length} requests after filtering`); } // Create a tool for each request for (const requestName of availableRequests) { try { log(`Creating tool for request: ${requestName}`); // Parse the request const parsedRequest = await parser.parseRequest(requestName); // Generate a unique tool name const toolName = createToolName(parsedRequest.name); // Skip if not in includeTools list (if provided) if ( includeTools && includeTools.length > 0 && !includeTools.includes(toolName) ) { log(`Skipping tool ${toolName} - not in includeTools list`); continue; } // Skip if in excludeTools list (if provided) if ( excludeTools && excludeTools.length > 0 && excludeTools.includes(toolName) ) { log(`Skipping tool ${toolName} - in excludeTools list`); continue; } // Create our standardized schema const schema = { environment: { type: "string", description: "Optional environment to use for this request", }, variables: { type: "object", additionalProperties: { type: "string", }, description: "Optional variables to override for this request", }, query: { type: "object", additionalProperties: { type: "string", }, description: "Optional query parameters to add to the request URL", }, body: { type: "object", description: "Request body parameters", additionalProperties: true, }, }; // Build tool description let description = `Execute ${parsedRequest.method} request to ${ parsedRequest.rawRequest?.http?.url || parsedRequest.url }`; // Add documentation if available if (parsedRequest.rawRequest?.docs) { description += "\n\n" + parsedRequest.rawRequest.docs; } // Create the tool handler const handler = async (params: BrunoToolSchema) => { try { const { environment } = params; log( `Executing request "${requestName}" with params: ${JSON.stringify( params, null, 2 )}` ); // Set environment if provided if (environment && typeof environment === "string") { log(`Using environment from params: ${environment}`); parser.setEnvironment(environment); } const response = await parser.executeRequest(parsedRequest, params); if (response.error) { log(`Error executing request "${requestName}":`, response.data); return { success: false, message: `Error: ${response.data}`, }; } // Format the response return { success: true, message: "Request executed successfully.", status: response.status, headers: response.headers, data: response.data, }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); log(`Error in handler for request "${requestName}":`, errorMessage); return { success: false, message: `Error: ${errorMessage}`, }; } }; // Add the tool to the list tools.push({ name: toolName, description, schema, handler, }); log(`Created tool: ${toolName}`); } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error); log(`Error creating tool for request "${requestName}":`, errorMessage); } } log(`Created ${tools.length} tools from Bruno collection`); return tools; } /** * Create a valid tool name from a request name * @param requestName - The name of the request * @returns A valid tool name */ function createToolName(requestName: string): string { // Replace spaces and special characters with underscores let name = requestName .toLowerCase() .replace(/[^a-z0-9_]/g, "_") .replace(/_+/g, "_"); // Ensure the name starts with a valid character if (!/^[a-z]/.test(name)) { name = "mcp_api_" + name; } return name; }

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/djkz/bruno-api-mcp'

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