Skip to content

Phase 21: Add flux_generate tool to MCP server - #40

Merged
MarcoFPO merged 1 commit into
mainfrom
feature/phase-21-mcp-generate
Mar 14, 2026
Merged

Phase 21: Add flux_generate tool to MCP server#40
MarcoFPO merged 1 commit into
mainfrom
feature/phase-21-mcp-generate

Conversation

@MarcoFPO

Copy link
Copy Markdown
Owner

Summary

    1. MCP Tool flux_generate für LLM-gesteuerte FTL-Generierung
  • Async via tokio::runtime::Runtime, API-Key-Fehler als tool error
  • MCP-Dokumentation aktualisiert (mcp-integration.md)

Test plan

  • 16 MCP-Tests bestehen (inkl. flux_generate_in_tools_list, missing_api_key)
  • Clippy fehlerfrei
  • CI grün

Closes #34

🤖 Generated with Claude Code

Add flux_generate as the 7th tool to the MCP server, enabling LLMs to
generate FTL programs from natural language requirements via the MCP
protocol. The tool wraps the existing GenerationLoop with proper error
handling for missing API keys and invalid parameters.

- Tool definition in build_tools() with full inputSchema
- Handler handle_flux_generate() using tokio::runtime::Runtime for async
- Input: requirement (required), requirement_type, provider, model, max_iterations
- Output: GenerationResult as JSON
- API key errors returned as tool errors (isError: true), no panics
- Tests: mcp_flux_generate_in_tools_list, mcp_flux_generate_missing_api_key
- Updated tools_list test to assert 7 tools
- Added mcp-integration.md with full tool documentation

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request integrates a new flux_generate tool into the flux-mcp server, significantly enhancing its capabilities by allowing users to generate FTL programs from natural language requirements using Large Language Models. The tool supports iterative generation, checking, and repair of FTL code, and is designed for asynchronous operation. It also includes robust error handling for missing LLM API keys and is thoroughly documented and tested.

Highlights

  • New LLM-powered FTL Generation Tool: Introduced flux_generate to the MCP server, enabling natural language to FTL code generation.
  • Asynchronous Execution with Tokio: The flux_generate tool leverages tokio::runtime::Runtime for non-blocking LLM interactions.
  • Robust API Key Handling: Implemented checks for missing LLM API keys, returning specific tool errors.
  • Updated Documentation: The mcp-integration.md file was updated to reflect the new flux_generate tool and its usage.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Changelog
  • flux-ftl/mcp-integration.md
    • Documented the new flux_generate tool, including its input schema, output, and environment requirements.
  • flux-ftl/src/bin/flux-mcp.rs
    • Updated comments to reflect the addition of flux_generate.
    • Imported necessary LLM-related modules (GenerateRequest, GenerationLoop, LlmConfig, LlmProvider, RequirementType).
    • Registered the flux_generate tool with its JSON schema in the build_tools function.
    • Added a new handle_flux_generate function to process tool calls, parse arguments, configure the LLM, execute the generation loop asynchronously using tokio, and manage potential errors.
  • flux-ftl/tests/mcp_tests.rs
    • Introduced send_jsonrpc_with_env to facilitate testing scenarios requiring specific environment variable configurations.
    • Adjusted mcp_tools_list to account for the new tool count and to dynamically check required input parameters based on the tool name.
    • Added mcp_flux_generate_in_tools_list to specifically verify the flux_generate tool's presence and schema details.
    • Included mcp_flux_generate_missing_api_key to test the tool's error handling when required LLM API keys are absent.
Activity
  • The author, MarcoFPO, has indicated that 16 MCP tests (including new tests for flux_generate_in_tools_list and missing_api_key) are passing.
  • Clippy checks are reported as error-free.
  • The CI pipeline is expected to be green.
  • The PR was generated with Claude Code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@MarcoFPO
MarcoFPO merged commit 43c9733 into main Mar 14, 2026
1 check passed
@MarcoFPO
MarcoFPO deleted the feature/phase-21-mcp-generate branch March 14, 2026 19:55

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Hallo, danke für den Pull Request. Die Ergänzung des flux_generate-Tools ist eine tolle Erweiterung für den MCP-Server. Der Code ist gut strukturiert und die asynchrone Ausführung über Tokio ist sinnvoll umgesetzt.

Ich habe einige Anmerkungen, die hauptsächlich die Konsistenz, Wartbarkeit und Leistung betreffen:

  • Es gibt eine Inkonsistenz bei den Werten für requirement_type zwischen der Implementierung, dem Tool-Schema und der Dokumentation.
  • Die Funktion handle_flux_generate könnte durch Auslagerung der Logik in eine Hilfsfunktion lesbarer gestaltet werden.
  • Das wiederholte Erstellen einer Tokio-Runtime in handle_flux_generate könnte zu Leistungseinbußen führen.
  • In den Tests gibt es etwas Redundanz und eine Prüfungsbedingung könnte spezifischer formuliert werden.

Details dazu finden Sie in den Kommentaren zu den einzelnen Codezeilen. Insgesamt ist dies ein solider Beitrag!

"type": "object",
"properties": {
"requirement": { "type": "string", "description": "Natural language description of the desired FTL program" },
"requirement_type": { "type": "string", "description": "Type of requirement: translate, explain, optimize, refactor", "default": "translate" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Das Schema für requirement_type ist inkonsistent mit der Implementierung in flux-ftl/src/llm.rs. Das Schema beschreibt die Werte translate, explain, optimize, refactor, während die Implementierung translate, optimize, invent, discover erwartet. Dies sollte korrigiert werden, um die tatsächliche Funktionalität widerzuspiegeln. Bitte denken Sie daran, auch die Dokumentation in flux-ftl/mcp-integration.md entsprechend zu aktualisieren.

Suggested change
"requirement_type": { "type": "string", "description": "Type of requirement: translate, explain, optimize, refactor", "default": "translate" },
"requirement_type": { "type": "string", "description": "Type of requirement: translate, optimize, invent, discover", "default": "translate" },

Comment on lines +640 to +741
fn handle_flux_generate(stdout: &std::io::Stdout, id: Value, args: &Value) {
let requirement = match args.get("requirement").and_then(|v| v.as_str()) {
Some(s) => s,
None => {
send_tool_error(stdout, id, "Missing required argument: requirement");
return;
}
};

let requirement_type = args
.get("requirement_type")
.and_then(|v| v.as_str())
.unwrap_or("translate");
let provider = args
.get("provider")
.and_then(|v| v.as_str())
.unwrap_or("anthropic");
let model = args.get("model").and_then(|v| v.as_str());
let max_iterations = args
.get("max_iterations")
.and_then(|v| v.as_u64())
.unwrap_or(5) as u32;

// Parse requirement type
let req_type = match RequirementType::from_str_loose(requirement_type) {
Ok(t) => t,
Err(e) => {
send_tool_error(stdout, id, &format!("Invalid requirement_type: {}", e));
return;
}
};

// Parse provider
let llm_provider = match LlmProvider::from_str_loose(provider) {
Ok(p) => p,
Err(e) => {
send_tool_error(stdout, id, &format!("Invalid provider: {}", e));
return;
}
};

// Build config from environment
let mut config = match LlmConfig::from_env(llm_provider, model.map(String::from)) {
Ok(c) => c,
Err(e) => {
send_tool_error(stdout, id, &format!("Configuration error: {}", e));
return;
}
};
config.max_iterations = max_iterations;

// Create generation loop
let gen_loop = match GenerationLoop::new(config) {
Ok(l) => l,
Err(e) => {
send_tool_error(
stdout,
id,
&format!("Failed to create generation loop: {}", e),
);
return;
}
};

let request = GenerateRequest {
requirement: requirement.to_string(),
requirement_type: req_type,
context: None,
examples: Vec::new(),
};

// Run async generation loop
let rt = match tokio::runtime::Runtime::new() {
Ok(rt) => rt,
Err(e) => {
send_tool_error(
stdout,
id,
&format!("Failed to create async runtime: {}", e),
);
return;
}
};

let result = match rt.block_on(gen_loop.generate(&request)) {
Ok(r) => r,
Err(e) => {
send_tool_error(stdout, id, &format!("Generation failed: {}", e));
return;
}
};

let json = match serde_json::to_string(&result) {
Ok(j) => j,
Err(e) => {
send_tool_error(stdout, id, &format!("Serialization error: {}", e));
return;
}
};

send_tool_result(stdout, id, &json);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Die Funktion handle_flux_generate ist sehr lang und enthält viel repetitiven Code für die Fehlerbehandlung. Dies beeinträchtigt die Lesbarkeit und Wartbarkeit. Ich schlage vor, die Logik in eine separate Funktion handle_flux_generate_inner auszulagern, die ein Result zurückgibt. Dadurch kann der ?-Operator für eine prägnantere Fehlerbehandlung verwendet werden.

Die neue handle_flux_generate_inner Funktion könnte so aussehen:

fn handle_flux_generate_inner(args: &Value) -> Result<String, String> {
    let requirement = args
        .get("requirement")
        .and_then(|v| v.as_str())
        .ok_or_else(|| "Missing required argument: requirement".to_string())?;

    let requirement_type = args
        .get("requirement_type")
        .and_then(|v| v.as_str())
        .unwrap_or("translate");
    let provider = args
        .get("provider")
        .and_then(|v| v.as_str())
        .unwrap_or("anthropic");
    let model = args.get("model").and_then(|v| v.as_str());
    let max_iterations = args
        .get("max_iterations")
        .and_then(|v| v.as_u64())
        .unwrap_or(5) as u32;

    let req_type = RequirementType::from_str_loose(requirement_type)
        .map_err(|e| format!("Invalid requirement_type: {}", e))?;

    let llm_provider = LlmProvider::from_str_loose(provider)
        .map_err(|e| format!("Invalid provider: {}", e))?;

    let mut config = LlmConfig::from_env(llm_provider, model.map(String::from))
        .map_err(|e| format!("Configuration error: {}", e))?;
    config.max_iterations = max_iterations;

    let gen_loop = GenerationLoop::new(config)
        .map_err(|e| format!("Failed to create generation loop: {}", e))?;

    let request = GenerateRequest {
        requirement: requirement.to_string(),
        requirement_type: req_type,
        context: None,
        examples: Vec::new(),
    };

    let rt = tokio::runtime::Runtime::new()
        .map_err(|e| format!("Failed to create async runtime: {}", e))?;

    let result = rt
        .block_on(gen_loop.generate(&request))
        .map_err(|e| format!("Generation failed: {}", e))?;

    serde_json::to_string(&result).map_err(|e| format!("Serialization error: {}", e))
}

Sie müssten diese Funktion dann über der handle_flux_generate definieren.

fn handle_flux_generate(stdout: &std::io::Stdout, id: Value, args: &Value) {
    match handle_flux_generate_inner(args) {
        Ok(result_json) => send_tool_result(stdout, id, &result_json),
        Err(error_message) => send_tool_error(stdout, id, &error_message),
    }
}

Comment on lines +712 to +722
let rt = match tokio::runtime::Runtime::new() {
Ok(rt) => rt,
Err(e) => {
send_tool_error(
stdout,
id,
&format!("Failed to create async runtime: {}", e),
);
return;
}
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Das Erstellen einer neuen tokio::runtime::Runtime bei jedem Aufruf von handle_flux_generate kann zu Leistungseinbußen führen, da dies ein relativ aufwändiger Vorgang ist. Obwohl die Netzwerklatenz zum LLM wahrscheinlich dominiert, wäre es für eine bessere Leistung und Ressourcennutzung vorteilhaft, die Runtime einmal zu initialisieren und wiederzuverwenden. Eine Möglichkeit wäre, die Runtime mit once_cell oder lazy_static global zu erstellen oder die main-Funktion als async zu deklarieren und die Runtime von Tokio verwalten zu lassen.

Comment on lines +291 to +311
#[test]
fn mcp_flux_generate_in_tools_list() {
let resp = send_jsonrpc("tools/list", None);

let tools = resp["result"]["tools"].as_array().expect("tools should be array");
assert_eq!(tools.len(), 7);

let gen_tool = tools.iter().find(|t| t["name"] == "flux_generate");
assert!(gen_tool.is_some(), "flux_generate should be in tools list");

let gen_tool = gen_tool.unwrap();
assert!(gen_tool["description"].as_str().unwrap().contains("Generate"));
let schema = &gen_tool["inputSchema"];
assert!(schema["properties"]["requirement"].is_object());
assert!(schema["properties"]["requirement_type"].is_object());
assert!(schema["properties"]["provider"].is_object());
assert!(schema["properties"]["model"].is_object());
assert!(schema["properties"]["max_iterations"].is_object());
let required = schema["required"].as_array().unwrap();
assert!(required.iter().any(|r| r == "requirement"));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Dieser Test mcp_flux_generate_in_tools_list überschneidet sich stark mit dem Test mcp_tools_list. Beide Tests rufen tools/list auf und prüfen die Anwesenheit und das Schema von flux_generate. Um die Redundanz zu reduzieren und die Wartung der Tests zu vereinfachen, könnten die spezifischen Prüfungen für flux_generate aus diesem Test in den mcp_tools_list-Test integriert werden. Dadurch wären alle tools/list-bezogenen Prüfungen an einem Ort gebündelt.

Comment on lines +334 to +336
text.to_lowercase().contains("error")
|| text.to_lowercase().contains("api")
|| text.to_lowercase().contains("key"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Die Überprüfung der Fehlermeldung ist recht allgemein. Sie prüft nur auf das Vorhandensein von "error", "api" oder "key". Der Test wäre aussagekräftiger und robuster, wenn er auf eine spezifischere Zeichenkette prüfen würde, die in der erwarteten Fehlermeldung vorkommt, z. B. "missing api key". Dies stellt sicher, dass der korrekte Fehlergrund gemeldet wird.

Suggested change
text.to_lowercase().contains("error")
|| text.to_lowercase().contains("api")
|| text.to_lowercase().contains("key"),
text.to_lowercase().contains("missing api key"),

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Phase 21: MCP flux_generate Tool

1 participant