Skip to main content

systemprompt_cli/commands/core/plugins/generate/
mcp.rs

1//! `.mcp.json` fragment generation for plugin bundles.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use anyhow::Result;
7use std::path::Path;
8use systemprompt_models::PluginConfig;
9
10pub fn generate_mcp_json(
11    plugin: &PluginConfig,
12    services_path: &Path,
13    output_dir: &Path,
14    files_generated: &mut Vec<String>,
15) -> Result<()> {
16    if plugin.mcp_servers.include.is_empty() {
17        return Ok(());
18    }
19
20    let config_path = services_path.join("config").join("config.yaml");
21    let mut mcp_servers = serde_json::Map::new();
22
23    for mcp_name in &plugin.mcp_servers.include {
24        let port = resolve_mcp_port(mcp_name, &config_path).unwrap_or(5000);
25        let url = format!("http://localhost:{}/api/v1/mcp/{}/mcp", port, mcp_name);
26        let mut server = serde_json::Map::new();
27        server.insert("url".to_owned(), serde_json::Value::String(url));
28        mcp_servers.insert(mcp_name.clone(), serde_json::Value::Object(server));
29    }
30
31    let mcp_json = serde_json::json!({ "mcpServers": mcp_servers });
32    let mcp_path = output_dir.join(".mcp.json");
33    let content = serde_json::to_string_pretty(&mcp_json)?;
34    std::fs::write(&mcp_path, content)?;
35    files_generated.push(mcp_path.to_string_lossy().to_string());
36
37    Ok(())
38}
39
40fn resolve_mcp_port(mcp_name: &str, config_path: &Path) -> Option<u16> {
41    let content = std::fs::read_to_string(config_path)
42        .map_err(|e| {
43            tracing::debug!(error = %e, path = %config_path.display(), "Failed to read config for MCP port resolution");
44            e
45        })
46        .ok()?;
47    let config: serde_yaml::Value = serde_yaml::from_str(&content)
48        .map_err(|e| {
49            tracing::warn!(error = %e, path = %config_path.display(), "Failed to parse config for MCP port resolution");
50            e
51        })
52        .ok()?;
53    config
54        .get("mcp_servers")
55        .and_then(|m| m.get(mcp_name))
56        .and_then(|s| s.get("port"))
57        .and_then(serde_yaml::Value::as_u64)
58        .and_then(|p| u16::try_from(p).ok())
59}