聚光灯MCP
A. 模型上下文协议 (MCP)服务器,将macOS Spotlight搜索暴露给大型语言模型。内置Swift,作为单个二进制文件运行,在macOS之外没有外部依赖关系。
它的作用
Spotlight是每台Mac内置的搜索引擎,它对整个文件系统中的文件内容、元数据和文件类型进行索引。此服务器将该功能封装到四个MCP工具中,任何LLM客户端都可以调用这些工具:
| 工具 | 说明 |
|---|---|
search | 在目录内的文件内容中进行全文搜索 |
get_metadata | 检索特定文件的Spotlight元数据(类型、大小、日期等) |
search_by_kind | 按类型查找文件: document, image, video, audio, pdf, code |
recent_files | 在目录中查找指定日期后修改的文件 |
所有工具都返回具有绝对文件路径、ISO 8601日期和完整Spotlight元数据的结构化JSON。搜索结果是分页的(默认值为100,最大值为1000)。
需求
- macOS 13.0或更晚
- 迅速 6.0或更高版本(Xcode 16+附带)——仅在从源代码构建时才需要
没有其他依赖关系。Swift包管理器处理一个外部包( MCP Swift SDK).
安装
自制(推荐)
最简单的安装方式是通过Homebrew:
brew tap adamrdrew/spotlight-mcp
brew install spotlight-mcp二进制文件将安装到 /opt/homebrew/bin/spotlight-mcp (苹果硅)或 /usr/local/bin/spotlight-mcp (英特尔)。
注: 由于二进制文件没有代码签名,macOS可能会在首次运行时提示您允许它。这对于Homebrew分布式二进制文件来说是正常的。
从源代码构建
# Clone the repository
git clone https://github.com/adamrdrew/spotlight-mcp.git
cd spotlight-mcp
# Build
swift build
# Run tests (90 tests)
swift test
# The binary is at:
.build/debug/spotlight-mcp服务器使用JSON-RPC通过stdin/stdout进行通信。您不直接运行它,MCP客户端将其作为子进程启动。
MCP客户端配置
将MCP客户端指向二进制文件。确切的格式取决于您的客户:
如果通过Homebrew安装:
{
"mcpServers": {
"spotlight": {
"command": "/opt/homebrew/bin/spotlight-mcp"
}
}
}(使用 /usr/local/bin/spotlight-mcp 在英特尔Mac上)
如果从源代码构建:
{
"mcpServers": {
"spotlight": {
"command": "/path/to/spotlight-mcp/.build/debug/spotlight-mcp"
}
}
}对于源代码构建的生产使用,请使用发布模式(swift build -c release)并指向 .build/release/spotlight-mcp.
工具参考
搜索
按作用域目录中的文本内容搜索文件。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
query | string | yes | 要在文件内容中搜索的文本 |
scope | string | yes | 要搜索的目录的绝对路径 |
limit | integer | no | 返回的最大结果数(默认值100,最大值1000) |
获取元数据
获取特定文件的Spotlight元数据属性。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
path | string | yes | 文件的绝对路径 |
search_by_kind
在作用域目录中按内容类型搜索文件。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
kind | string | yes | 以下之一: document, image, video, audio, pdf, code |
scope | string | yes | 要搜索的目录的绝对路径 |
limit | integer | no | 返回的最大结果数(默认值100,最大值1000) |
最近的文件
在作用域目录中查找最近修改的文件。
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
scope | string | yes | 要搜索的目录的绝对路径 |
since | string | no | ISO 8601日期--仅在此日期之后修改的文件(默认值:7天前) |
limit | integer | no | 返回的最大结果数(默认值100,最大值1000) |
安全模型
- 仅限范围搜索 --每个搜索工具都需要一个明确的目录范围。不允许进行全系统搜索。
- 路径净化 --所有文件路径都经过验证,以防止目录遍历攻击。符号链接在范围内得到解析和验证。
- 输入验证 --所有工具输入都经过验证。空查询、相对路径、无效的种类名称和超出范围的限制将被拒绝,并带有描述性错误。
- 只读 --所有工具都是只读的(注释为
readOnlyHint: true).没有创建、修改或删除任何内容。 - 绝对路径 --响应中的所有文件路径都是绝对的。不接受或返回相对路径。
- 没有提升的特权 --服务器以与启动它的用户相同的权限运行。
- TCC边界得到尊重 --Spotlight尊重macOS的隐私控制。如果用户没有授予对目录的访问权限,则不会显示该目录的结果。
- 最小日志 --为了可观察性,搜索结果和文件元数据不会记录在操作事件之外。
发展
建造和测试
swift build # Debug build
swift build -c release # Release build
swift test # Run all 90 tests
swift test --filter SearchTool # Run tests matching a name项目结构
Sources/SpotlightMCP/
├── main.swift # Server entry point, wires everything together
├── Search/ # Spotlight API abstraction layer
│ ├── SpotlightQuery.swift # MDQuery execution wrapper
│ ├── QueryBuilder.swift # Predicate construction from structured params
│ ├── MetadataItem.swift # MDItem attribute extraction
│ ├── KindMapping.swift # Friendly names → UTI type predicates
│ ├── MetadataValue+Codable.swift # Custom Codable for MetadataValue enum
│ └── Types.swift # SearchResult, MetadataValue types
└── Tools/ # MCP tool handlers
├── ToolRouter.swift # Dispatches CallTool to correct handler
├── SearchTool.swift # search tool implementation
├── GetMetadataTool.swift # get_metadata tool implementation
├── SearchByKindTool.swift # search_by_kind tool implementation
├── RecentFilesTool.swift # recent_files tool implementation
├── ToolSchemas.swift # Tool definitions for ListTools
├── ToolSchemas+Definitions.swift # Tool schema definitions (split for Sandi Metz)
├── ArgumentParser.swift # Extracts/validates MCP arguments
├── PathSanitizer.swift # Path validation and sanitization
├── PaginationConfig.swift # Result limit enforcement
├── ResultFormatter.swift # JSON serialization of results
└── ToolError.swift # Typed error enum
Tests/SpotlightMCPTests/
├── Search/ # Unit + integration tests for search layer
└── Tools/ # Unit tests for tool handlers代码的风格
该项目执行 Sandi Metz的规则:类型限制为100行,方法限制为5行,参数列表限制为4行。它始终使用Swift 6严格的并发性、类型化抛出和值语义。看 .ushabti/style.md 查看完整的风格指南。
故障排除
服务器未启动
- 验证MCP客户端配置中的二进制路径是否正确和绝对
- 检查二进制文件是否具有执行权限:
chmod +x .build/release/spotlight-mcp - 在MCP客户端的日志中查找错误
空搜索结果
- 确保Spotlight已将目标目录编入索引(检查“系统设置”>“Siri和Spotlight”>“搜索结果”)
- 验证作用域路径是否存在且可访问
- 检查TCC权限(系统设置>隐私和安全>文件和文件夹)
“作用域不是目录”错误
- 确保
scope参数指向目录,而不是文件 - 验证目录是否存在:
ls -ld /path/to/scope
“路径超出范围”错误
- 所有文件路径都必须在声明的作用域目录内
- 符号链接已解析;确保解析的路径在范围内
“未知类型”错误
- 有效类型包括:
document,image,video,audio,pdf,code - 种类名称不区分大小写
“ISO 8601日期无效”错误
- 日期必须采用ISO 8601格式:
YYYY-MM-DDTHH:MM:SSZ - 例子:
2024-01-15T10:30:00Z
“路径必须是绝对路径”错误
- 所有路径必须以开头
/ - 相对路径(例如。,
./file.txt,../dir)因安全原因被拒绝
技术细节
- MCP-SDK: modelcontextprotocol/swift-sdk v0.1.0+
- 日志记录: apple/swift日志 v1.0.0+
- 运输:Stdio(标准输入/标准输出上的JSON-RPC)
- Swift语言模式Swift 6(严格并发)
- Spotlight API:CoreServices MDQuery/MDItem(仅限公共API)
- 输出:单个静态链接的二进制文件,没有dylib或运行时依赖关系
