spreadsheet-mcp 0.10.1

Stateful MCP server for spreadsheet analysis and editing — token-efficient tools for LLM agents to read, profile, edit, and recalculate .xlsx workbooks
Documentation
use anyhow::{Result, anyhow};
use globset::{Glob, GlobMatcher};
use std::path::Path;

#[derive(Default)]
pub struct WorkbookFilter {
    slug_prefix: Option<String>,
    folder: Option<String>,
    path_glob: Option<GlobMatcher>,
}

impl WorkbookFilter {
    pub fn new(
        slug_prefix: Option<String>,
        folder: Option<String>,
        path_glob: Option<String>,
    ) -> Result<Self> {
        let matcher = if let Some(glob) = path_glob {
            Some(
                Glob::new(&glob)
                    .map_err(|err| anyhow!("invalid glob pattern {glob}: {err}"))?
                    .compile_matcher(),
            )
        } else {
            None
        };

        Ok(Self {
            slug_prefix: slug_prefix.map(|s| s.to_ascii_lowercase()),
            folder: folder.map(|s| s.to_ascii_lowercase()),
            path_glob: matcher,
        })
    }

    pub fn matches(&self, slug: &str, folder: Option<&str>, path: &Path) -> bool {
        if let Some(prefix) = &self.slug_prefix
            && !slug.to_ascii_lowercase().starts_with(prefix)
        {
            return false;
        }

        if let Some(expected_folder) = &self.folder {
            match folder.map(|f| f.to_ascii_lowercase()) {
                Some(actual) if &actual == expected_folder => {}
                _ => return false,
            }
        }

        if let Some(glob) = &self.path_glob
            && !glob.is_match(path)
        {
            return false;
        }

        true
    }
}