feat(aiagent): allow mcp as tools (#6790)
* draft mcp client * testing * fix * cleaning * mcp resource in inputtransforms * cleaning * big cleaning * cleaning * no arc * add utils file * refactor tools * add mcp actions * draft frontend * send arguments from backend * better frontend * cleaning * use token for auth * add logo * rm * fix * fix * chore: refactor mcp for ai agents (#6829) * Add Tool enum for AIAgent with backward compatibility - Created Tool enum that can be either Windmill (FlowModule) or Mcp (resource reference) - Created McpToolRef struct to hold MCP resource path - Implemented custom Deserialize for Tool with backward compatibility: - New format: {type: 'windmill'|'mcp', ...} - Old format: FlowModule objects (automatically wrapped in Tool::Windmill) - Updated AIAgent to use Vec<Tool> instead of Vec<FlowModule> - Updated FlowValue::traverse_leafs to handle Tool enum - Backward compatible: old flows with Vec<FlowModule> will deserialize correctly * Refactor AI executor to process Tool enum instead of extracting MCP from input_transforms - Separate Windmill tools and MCP resource paths from tools list - Process Windmill FlowModules into Tool definitions - Load MCP tools from resource paths in Tool::Mcp variants - Remove old logic that extracted mcp_resources from input_transforms - Import FlowModule, remove unused InputTransform - Fix type issues: use .as_str() for path and handle Option<bool> properly * handle in args * mcp as flowmodule * frontend * config for mcp * simplify logic * fix ai executor logic * cleaning * clean frontend * fix * better resource picker * fix and styling * add endpoint to fetch tools * apply tool filtering * fix name validation * better ui * use cache * fix * fix merge * refactor: Separate MCP tools from FlowModule in AIAgent - Add new AgentTool, ToolValue, and McpToolValue types - Update AIAgent to use Vec<AgentTool> instead of Vec<FlowModule> - Implement From traits for clean conversion between AgentTool and FlowModule - Add backward compatibility via custom deserializer for AgentTool - Simplify resolve_module logic by reusing existing resolve_modules function - Update traverse_leafs to handle AgentTool structure This refactoring separates MCP tools from FlowModule tools, making the type system clearer and eliminating the need to treat MCP servers as a special case of FlowModule. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: Update ai_executor and worker_lockfiles for AgentTool - Update ai_executor.rs to handle new AgentTool structure - Separate MCP tools from FlowModule tools using ToolValue enum - Convert AgentTool to FlowModule for backward compatibility - Add imports for AgentTool and ToolValue types - Update worker_lockfiles.rs for lazy loading optimization - Convert AgentTool <-> FlowModule in insert_flow_modules - Preserve lazy loading for FlowModule tools via modules_node - Keep MCP tools inline (lightweight, no need for lazy loading) - Maintain backward compatibility with existing flows This enables the lazy loading optimization for FlowModule tools while keeping MCP tools inline, balancing performance and simplicity. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * cleaning * adapt frontend * cleaning * cleaning * type fix * cleaning * fix back comp * move mcp button position * nit * cleaning * fix nested removal * cleaning * opti * fix chat markdown display * fix chat messages layout * fix back comp frontend * fix deserializer * nit * simpler serializer * use if else --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -73,6 +73,7 @@ pub fn workspaced_service() -> Router {
|
||||
get(file_resource_ext_to_resource_type),
|
||||
)
|
||||
.route("/type/create", post(create_resource_type))
|
||||
.route("/mcp_tools/*path", get(get_mcp_tools))
|
||||
}
|
||||
|
||||
pub fn public_service() -> Router {
|
||||
@@ -1392,6 +1393,64 @@ where
|
||||
Ok(resource)
|
||||
}
|
||||
|
||||
/// Get list of tools from an MCP resource
|
||||
async fn get_mcp_tools(
|
||||
authed: ApiAuthed,
|
||||
Extension(db): Extension<DB>,
|
||||
Extension(user_db): Extension<UserDB>,
|
||||
Path((w_id, path)): Path<(String, StripPath)>,
|
||||
) -> JsonResult<Vec<serde_json::Value>> {
|
||||
let path = path.to_path();
|
||||
check_scopes(&authed, || format!("resources:read:{}", path))?;
|
||||
|
||||
let mut tx = user_db.begin(&authed).await?;
|
||||
|
||||
// Fetch the MCP resource from database
|
||||
let resource_value_o = sqlx::query_scalar!(
|
||||
"SELECT value as \"value: sqlx::types::Json<Box<RawValue>>\" FROM resource WHERE path = $1 AND workspace_id = $2",
|
||||
&path,
|
||||
&w_id
|
||||
)
|
||||
.fetch_optional(&mut *tx)
|
||||
.await?;
|
||||
|
||||
tx.commit().await?;
|
||||
|
||||
if resource_value_o.is_none() {
|
||||
explain_resource_perm_error(&path, &w_id, &db, &authed).await?;
|
||||
}
|
||||
|
||||
let resource_value = not_found_if_none(resource_value_o, "Resource", path)?
|
||||
.ok_or_else(|| Error::BadRequest(format!("Empty resource value for {}", path)))?;
|
||||
|
||||
// Parse MCP resource
|
||||
let mcp_resource =
|
||||
serde_json::from_str::<windmill_common::mcp_client::McpResource>(resource_value.0.get())
|
||||
.map_err(|e| Error::BadRequest(format!("Failed to parse MCP resource: {}", e)))?;
|
||||
|
||||
// Create MCP client connection
|
||||
let client = windmill_common::mcp_client::McpClient::from_resource(mcp_resource, &db, &w_id)
|
||||
.await
|
||||
.map_err(|e| Error::ExecutionErr(format!("Failed to connect to MCP server: {}", e)))?;
|
||||
|
||||
// Get raw MCP tools and convert to JSON
|
||||
let tools: Vec<serde_json::Value> = client
|
||||
.available_tools()
|
||||
.iter()
|
||||
.map(|tool| {
|
||||
serde_json::to_value(tool)
|
||||
.map_err(|e| Error::ExecutionErr(format!("Failed to serialize MCP tool: {}", e)))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
// Gracefully shutdown the client
|
||||
if let Err(e) = client.shutdown().await {
|
||||
tracing::warn!("Failed to shutdown MCP client: {}", e);
|
||||
}
|
||||
|
||||
Ok(Json(tools))
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct GitRepositoryResource {
|
||||
url: String,
|
||||
|
||||
Reference in New Issue
Block a user