tools-kit 0.0.2

Portable Local and MCP tool execution with narrow extension traits.
Documentation
use std::{fs, path::Path};

fn source(relative: &str) -> String {
    fs::read_to_string(Path::new(env!("CARGO_MANIFEST_DIR")).join(relative))
        .unwrap_or_else(|error| panic!("failed to read `{relative}`: {error}"))
}

fn rust_sources(root: &Path, files: &mut Vec<std::path::PathBuf>) {
    for entry in fs::read_dir(root).expect("source directory should read") {
        let path = entry.expect("source entry should read").path();
        if path.is_dir() {
            rust_sources(&path, files);
        } else if path.extension().is_some_and(|extension| extension == "rs") {
            files.push(path);
        }
    }
}

#[test]
fn crate_root_is_tight_and_supporting_types_live_in_functional_modules() {
    let root = source("src/lib.rs");
    for module in [
        "pub mod declaration;",
        "pub mod execution;",
        "pub mod interaction;",
        "pub mod local;",
        "pub mod mcp;",
        "pub mod schema;",
    ] {
        assert!(
            root.contains(module),
            "missing functional module `{module}`"
        );
    }
    assert!(root.contains("handler::{ToolHandler, ToolHandlerBackend},"));
    assert!(root.contains("toolset::{Toolset, ToolsetBackend},"));
    assert!(!root.contains("name::ToolName"));
    assert!(source("src/declaration.rs").contains("pub use crate::name::ToolName;"));

    for forbidden in [
        "ToolsetBuilder",
        "ToolsetConfig",
        "ToolsetSourceConfig",
        "LocalSourceConfig",
        "McpSourceConfig",
        "ToolsetFactory",
        "ToolsetConformance",
        "ToolConformanceFailure",
        "ToolDeclaration",
        "ToolList",
    ] {
        assert!(
            !root.contains(forbidden),
            "crate root must not flatten or retain `{forbidden}`"
        );
    }
}

#[test]
fn hierarchy_uses_associated_handlers_and_only_root_custom_ingress() {
    let toolset = source("src/toolset.rs");
    let handler = source("src/handler.rs");
    assert!(toolset.contains("type Handler: crate::ToolHandlerBackend;"));
    assert!(toolset.contains("pub fn from_backend<B: ToolsetBackend>(backend: B) -> Self"));
    assert!(!handler.contains("pub fn from_backend"));

    for arm in [
        "Local(LocalToolset)",
        "Mcp(McpToolset)",
        "Extension(Arc<dyn ErasedToolsetBackend>)",
    ] {
        assert!(
            toolset.contains(arm),
            "Toolset dispatcher must retain `{arm}`"
        );
    }
    for arm in [
        "Local(local::handler::LocalToolHandler)",
        "Mcp(mcp::handler::McpToolHandler)",
        "Extension(Box<dyn ErasedToolHandlerBackend>)",
    ] {
        assert!(
            handler.contains(arm),
            "handler dispatcher must retain `{arm}`"
        );
    }
}

#[test]
fn deleted_product_and_compatibility_symbols_are_absent_from_production() {
    let mut files = Vec::new();
    rust_sources(
        &Path::new(env!("CARGO_MANIFEST_DIR")).join("src"),
        &mut files,
    );
    let deleted = [
        "ToolsetBuilder",
        "ToolsetConfig",
        "ToolsetSourceConfig",
        "LocalSourceConfig",
        "McpSourceConfig",
        "McpServeConfig",
        "ToolsetFactory",
        "ToolsetFactoryRequest",
        "ToolsetConformance",
        "ToolsetConformanceInputs",
        "ToolConformanceFailure",
        "ToolDeclaration",
        "ToolList",
        "FunctionToolRegistration",
        "InteractiveToolRegistration",
        "ToolFuture",
        "FormInteractionCapability",
        "TextInteractionCapability",
        "ToolInteractionIntent",
        "ToolInteractionDecision",
        "ToolHandlerOutput::Error",
        "ToolError::Execution",
        "source_id",
    ];
    for path in files {
        let contents = fs::read_to_string(&path).expect("Rust source should read");
        for symbol in deleted {
            assert!(
                !contents.contains(symbol),
                "deleted symbol `{symbol}` remains in {}",
                path.display()
            );
        }
        assert!(
            !contents.contains("#[cfg(test)]"),
            "production test code remains in {}",
            path.display()
        );
    }
}

#[test]
fn docs_teach_direct_construction_and_independent_server_ownership() {
    let docs = [
        source("README.md"),
        source("ARCHITECTURE.md"),
        source("docs/PORTABLE_API.md"),
        source("docs/STRICT_TESTING.md"),
    ]
    .join("\n");
    for required in [
        "Toolset::local",
        "Toolset::mcp",
        "Toolset::from_backend",
        "ToolsetBackend",
        "ToolHandlerBackend",
        "McpServer",
    ] {
        assert!(docs.contains(required), "docs omit `{required}`");
    }
    for stale in [
        "ToolsetFactoryRequest",
        "ToolsetConformanceInputs",
        "ToolHandlerOutput::Error",
        "McpSourceConfig::serve",
        "Toolset::serve",
        "Toolset::stop",
    ] {
        assert!(!docs.contains(stale), "docs still teach `{stale}`");
    }
}