rustfs-mcp 0.0.5

RustFS MCP (Model Context Protocol) Server
// Copyright 2024 RustFS Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

pub mod config;
pub mod s3_client;
pub mod server;

pub use config::Config;
pub use s3_client::{BucketInfo, S3Client};
pub use server::RustfsMcpServer;

use anyhow::{Context, Result};
use rmcp::ServiceExt;
use tokio::io::{stdin, stdout};
use tracing::info;

/// Run the MCP server with the provided configuration
pub async fn run_server_with_config(config: Config) -> Result<()> {
    info!("Starting RustFS MCP Server with provided configuration");

    config
        .validate()
        .context("Configuration validation failed")?;

    let server = RustfsMcpServer::new(config).await?;

    info!("Running MCP server with stdio transport");

    // Run the server with stdio
    server
        .serve((stdin(), stdout()))
        .await
        .context("Failed to serve MCP server")?
        .waiting()
        .await
        .context("Error while waiting for server shutdown")?;

    Ok(())
}

/// Run the MCP server with default configuration (from environment variables)
pub async fn run_server() -> Result<()> {
    info!("Starting RustFS MCP Server with default configuration");

    let config = Config::default();
    run_server_with_config(config).await
}

/// Validate environment configuration (legacy function for backward compatibility)
pub fn validate_environment() -> Result<()> {
    use std::env;

    if env::var("AWS_ACCESS_KEY_ID").is_err() {
        anyhow::bail!("AWS_ACCESS_KEY_ID environment variable is required");
    }

    if env::var("AWS_SECRET_ACCESS_KEY").is_err() {
        anyhow::bail!("AWS_SECRET_ACCESS_KEY environment variable is required");
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_config_creation() {
        let config = Config {
            access_key_id: Some("test_key".to_string()),
            secret_access_key: Some("test_secret".to_string()),
            ..Config::default()
        };

        assert!(config.validate().is_ok());
        assert_eq!(config.access_key_id(), "test_key");
        assert_eq!(config.secret_access_key(), "test_secret");
    }

    #[tokio::test]
    async fn test_run_server_with_invalid_config() {
        let config = Config::default();

        let result = run_server_with_config(config).await;
        assert!(result.is_err());
    }
}