Skip to main content
Glama
elhombrejd

BFL MCP Server

by elhombrejd

generate_image

Create custom images from text descriptions using the FLUX.1 Kontext model. Specify aspect ratio, format, and safety settings for tailored visual content generation.

Instructions

Generate an image using FLUX.1 Kontext model based on a text prompt

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
promptYesText description of the image to generate
aspect_ratioNoAspect ratio of the image (e.g., "1:1", "16:9", "9:16")1:1
seedNoSeed for reproducible generation
safety_toleranceNoSafety tolerance level (0-6)
output_formatNoOutput image formatjpeg

Implementation Reference

  • MCP tool handler for 'generate_image': parses arguments, calls bflClient.generateImage, and returns the image URL in response.
    case 'generate_image': {
      const imageUrl = await bflClient.generateImage({
        prompt: args.prompt as string,
        aspect_ratio: args.aspect_ratio as string | undefined,
        seed: args.seed as number | undefined,
        safety_tolerance: args.safety_tolerance as number | undefined,
        output_format: args.output_format as 'jpeg' | 'png' | undefined,
      });
    
      return {
        content: [
          {
            type: 'text',
            text: `Image generated successfully! URL: ${imageUrl}\n\nNote: The URL is valid for 10 minutes.`,
          },
        ],
      };
    }
  • Core implementation of image generation: submits POST request to BFL API /v1/flux-kontext-pro and polls for completion.
    async generateImage(request: GenerateImageRequest): Promise<string> {
      try {
        // Step 1: Submit generation request
        const response = await this.makeRequest('/v1/flux-kontext-pro', {
          prompt: request.prompt,
          aspect_ratio: request.aspect_ratio || '1:1',
          seed: request.seed,
          safety_tolerance: request.safety_tolerance,
          output_format: request.output_format || 'jpeg',
        });
    
        if (!response.id) {
          throw new Error('No request ID received from BFL API');
        }
    
        // Log the polling URL if provided
        if (response.polling_url) {
          console.log(`[BFL] Using polling URL: ${response.polling_url}`);
        }
    
        // Step 2: Poll for result
        const result = await this.pollForResult(response.id, response.polling_url);
    
        if (!result.result?.sample) {
          throw new Error('No image URL in response');
        }
    
        return result.result.sample;
      } catch (error) {
        throw new Error(`Image generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
      }
    }
  • src/index.ts:38-72 (registration)
    Registration of the 'generate_image' tool in the tools list, including name, description, and detailed inputSchema.
    {
      name: 'generate_image',
      description: 'Generate an image using FLUX.1 Kontext model based on a text prompt',
      inputSchema: {
        type: 'object',
        properties: {
          prompt: {
            type: 'string',
            description: 'Text description of the image to generate',
          },
          aspect_ratio: {
            type: 'string',
            description: 'Aspect ratio of the image (e.g., "1:1", "16:9", "9:16")',
            default: '1:1',
          },
          seed: {
            type: 'number',
            description: 'Seed for reproducible generation',
          },
          safety_tolerance: {
            type: 'number',
            description: 'Safety tolerance level (0-6)',
            minimum: 0,
            maximum: 6,
          },
          output_format: {
            type: 'string',
            enum: ['jpeg', 'png'],
            description: 'Output image format',
            default: 'jpeg',
          },
        },
        required: ['prompt'],
      },
    },
  • TypeScript type definition for GenerateImageRequest, matching the tool's input schema.
    export interface GenerateImageRequest {
      prompt: string;
      aspect_ratio?: string;
      seed?: number;
      safety_tolerance?: number;
      output_format?: 'jpeg' | 'png';
    }
  • Helper function to poll the BFL API for generation completion with retry logic and timeout.
    private async pollForResult(requestId: string, pollingUrl?: string): Promise<BFLApiResponse> {
      const maxAttempts = 20; // 1 minute with 3-second intervals
      const pollInterval = 3000;
    
      console.log(`[BFL] Starting polling for request ${requestId} (max ${maxAttempts} attempts, ${pollInterval/1000}s intervals)`);
    
      for (let attempt = 0; attempt < maxAttempts; attempt++) {
        try {
          console.log(`[BFL] Polling attempt ${attempt + 1}/${maxAttempts} for ${requestId}`);
          
          // Use polling URL if provided, otherwise construct from base URL
          const url = pollingUrl || `${this.baseUrl}/v1/get_result?id=${requestId}`;
          const response = await fetch(url, {
            headers: {
              'x-key': this.config.apiKey,
            },
          });
    
          if (!response.ok) {
            const errorMsg = `Poll error: ${response.status} - ${response.statusText}`;
            console.log(`[BFL] ${errorMsg}`);
            
            // Don't retry on client errors (4xx)
            if (response.status >= 400 && response.status < 500) {
              throw new Error(errorMsg);
            }
            
            throw new Error(errorMsg);
          }
    
          const result: PollResponse = await response.json();
          console.log(`[BFL] Status: ${result.status}`);
    
          if (result.status === 'Ready') {
            console.log(`[BFL] ✅ Image generation completed for ${requestId}`);
            return result;
          } else if (result.status === 'Error') {
            const errorMsg = `Generation failed: ${result.error || 'Unknown error'}`;
            console.log(`[BFL] ❌ ${errorMsg}`);
            throw new Error(errorMsg);
          } else if (result.status === 'Processing' || result.status === 'Pending') {
            console.log(`[BFL] ⏳ Still ${result.status.toLowerCase()}, waiting ${pollInterval/1000}s...`);
          }
    
          // Wait before next poll (only if not last attempt)
          if (attempt < maxAttempts - 1) {
            await new Promise(resolve => setTimeout(resolve, pollInterval));
          }
        } catch (error) {
          console.log(`[BFL] Error on attempt ${attempt + 1}: ${error instanceof Error ? error.message : 'Unknown error'}`);
          
          if (attempt === maxAttempts - 1) {
            console.log(`[BFL] ❌ Final attempt failed, giving up`);
            throw error;
          }
          
          // For network errors, wait before retry
          if (error instanceof Error && !error.message.includes('Poll error: 4')) {
            console.log(`[BFL] Retrying in ${pollInterval/1000}s...`);
            await new Promise(resolve => setTimeout(resolve, pollInterval));
          } else {
            // Don't retry 4xx errors
            throw error;
          }
        }
      }
    
      const timeoutMsg = `Timeout: Image generation exceeded ${maxAttempts * pollInterval / 1000}s limit`;
      console.log(`[BFL] ❌ ${timeoutMsg}`);
      throw new Error(timeoutMsg);
    }
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions the model but doesn't cover critical aspects like rate limits, authentication requirements, potential costs, error conditions, or what the output looks like (e.g., image data format). For a generative tool with zero annotation coverage, this is a significant gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single, efficient sentence that directly states the tool's purpose without unnecessary words. It's appropriately sized and front-loaded, with every element contributing essential information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity of image generation, no annotations, and no output schema, the description is incomplete. It doesn't address behavioral traits, output format details, or usage context, making it inadequate for an agent to fully understand how to invoke this tool effectively.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema fully documents all 5 parameters. The description adds no additional parameter semantics beyond what's already in the schema (e.g., it doesn't explain prompt best practices or safety tolerance implications). Baseline 3 is appropriate when the schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('generate an image') and specifies the model ('FLUX.1 Kontext model'), which distinguishes it from generic image generation. However, it doesn't explicitly differentiate from the sibling 'edit_image' tool, which would require mentioning this is for creation from scratch rather than modification.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives like 'edit_image'. It lacks context about appropriate scenarios, prerequisites, or exclusions, leaving the agent to infer usage based solely on the tool name and parameters.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/elhombrejd/bfl_mcp'

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