# Crates Docs MCP 服务器

[![Crates.io](https://img.shields.io/crates/v/crates-docs.svg)](https://crates.io/crates/crates-docs)
[![Documentation](https://docs.rs/crates-docs/badge.svg)](https://docs.rs/crates-docs)
[![Docker](https://img.shields.io/docker/v/kingingwang/crates-docs/latest?label=docker)](https://hub.docker.com/r/kingingwang/crates-docs)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Build Status](https://github.com/KingingWang/crates-docs/workflows/CI/badge.svg)](https://github.com/KingingWang/crates-docs/actions)
[![codecov](https://codecov.io/gh/kingingwang/crates-docs/branch/main/graph/badge.svg)](https://codecov.io/gh/kingingwang/crates-docs)
[![GitHub stars](https://img.shields.io/github/stars/KingingWang/crates-docs?style=social)](https://github.com/KingingWang/crates-docs/stargazers)

一个高性能的 Rust crate 文档查询 MCP 服务器，支持多种传输协议。

## 特性

- 🚀 **高性能**: 异步 Rust + TinyLFU/TTL 内存缓存，支持按条目 TTL，可选 Redis 扩展
- 📦 **多架构 Docker 镜像**: 支持 `linux/amd64` 和 `linux/arm64`
- 🔧 **多种传输协议**: Stdio、HTTP (Streamable HTTP)、SSE、Hybrid
- 📚 **完整文档查询**: crate 搜索、文档查找、特定项目查询
- 🛡️ **安全可靠**: 速率限制、连接池、请求验证
- 📊 **健康监控**: 内置健康检查和性能监控
- 🏗️ **模块化架构**: 清晰的模块划分，易于扩展和维护
- 🔄 **HTTP 重试机制**: 可配置的 HTTP 客户端重试逻辑，提高外部服务调用可靠性
- 📊 **Prometheus 指标**: 内置指标监控支持，便于运维观测
- ⏱️ **细粒度缓存 TTL**: 为 crate 文档、项目文档、搜索结果提供独立的 TTL 配置

## 架构图

### 系统架构

```mermaid
graph TB
    subgraph "MCP 客户端"
        Client[Claude/Cursor/Windsurf]
    end

    subgraph "传输层"
        Stdio[Stdio 传输]
        HTTP[HTTP 传输]
        SSE[SSE 传输]
    end

    subgraph "Crates Docs MCP 服务器"
        Server[CratesDocsServer]
        Handler[CratesDocsHandler]
        Registry[ToolRegistry]

        subgraph "工具层"
            LookupCrate[lookup_crate]
            SearchCrates[search_crates]
            LookupItem[lookup_item]
            HealthCheck[health_check]
        end

        subgraph "服务层"
            DocService[DocService]
            HttpClient[HTTP Client]
        end

        subgraph "缓存层"
            MemoryCache[MemoryCache]
            RedisCache[RedisCache]
            DocCache[DocCache]
        end
    end

    subgraph "外部服务"
        DocsRs[docs.rs]
        CratesIo[crates.io]
    end

    Client --> Stdio
    Client --> HTTP
    Client --> SSE

    Stdio --> Server
    HTTP --> Server
    SSE --> Server

    Server --> Handler
    Handler --> Registry

    Registry --> LookupCrate
    Registry --> SearchCrates
    Registry --> LookupItem
    Registry --> HealthCheck

    LookupCrate --> DocService
    SearchCrates --> DocService
    LookupItem --> DocService
    HealthCheck --> HttpClient

    DocService --> HttpClient
    DocService --> DocCache

    DocCache --> MemoryCache
    DocCache --> RedisCache

    HttpClient --> DocsRs
    HttpClient --> CratesIo
```

### 数据流

```mermaid
sequenceDiagram
    participant Client as MCP 客户端
    participant Server as CratesDocsServer
    participant Handler as CratesDocsHandler
    participant Tool as 工具实现
    participant Cache as DocCache
    participant External as 外部服务

    Client->>Server: MCP 请求
    Server->>Handler: 路由请求
    Handler->>Tool: 执行工具

    alt 缓存命中
        Tool->>Cache: 查询缓存
        Cache-->>Tool: 返回缓存数据
    else 缓存未命中
        Tool->>Cache: 查询缓存
        Cache-->>Tool: 未命中
        Tool->>External: HTTP 请求
        External-->>Tool: 返回数据
        Tool->>Cache: 写入缓存
    end

    Tool-->>Handler: 返回结果
    Handler-->>Server: 格式化响应
    Server-->>Client: MCP 响应
```

## 快速开始

### 使用 Docker（推荐）

```bash
# 从 Docker Hub 拉取镜像
docker pull kingingwang/crates-docs:latest

# 运行容器（官方镜像内置配置默认监听 0.0.0.0:8080）
docker run -d --name crates-docs -p 8080:8080 kingingwang/crates-docs:latest

# 使用自定义配置
docker run -d --name crates-docs -p 8080:8080 \
  -v $(pwd)/config.toml:/app/config.toml:ro \
  kingingwang/crates-docs:latest
```

### Docker Compose

```yaml
version: '3.8'
services:
  crates-docs:
    image: kingingwang/crates-docs:latest
    ports:
      - "8080:8080"
    environment:
      CRATES_DOCS_HOST: 0.0.0.0
      CRATES_DOCS_PORT: 8080
      CRATES_DOCS_TRANSPORT_MODE: hybrid
    volumes:
      - ./config.toml:/app/config.toml:ro
      - ./logs:/app/logs
    restart: unless-stopped
```

```bash
docker compose up -d
```

### 从源码构建

```bash
git clone https://github.com/KingingWang/crates-docs.git
cd crates-docs
cargo build --release
./target/release/crates-docs serve
```

### 从 crates.io 安装

```bash
cargo install crates-docs
crates-docs serve
```

## MCP 客户端集成

### Claude Desktop

编辑配置文件：
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`

```json
{
  "mcpServers": {
    "crates-docs": {
      "command": "/path/to/crates-docs",
      "args": ["serve", "--mode", "stdio"]
    }
  }
}
```

### Cursor

编辑 `~/.cursor/mcp.json`：

```json
{
  "mcpServers": {
    "crates-docs": {
      "command": "/path/to/crates-docs",
      "args": ["serve", "--mode", "stdio"]
    }
  }
}
```

### Windsurf

编辑 `~/.codeium/windsurf/mcp_config.json`：

```json
{
  "mcpServers": {
    "crates-docs": {
      "command": "/path/to/crates-docs",
      "args": ["serve", "--mode", "stdio"]
    }
  }
}
```

### Cherry Studio

1. 打开 Cherry Studio 设置
2. 找到 `MCP 服务器` 选项
3. 点击 `添加服务器`
4. 填写参数：

| 字段 | 值 |
|------|------|
| 名称 | `crates-docs` |
| 类型 | `STDIO` |
| 命令 | `/path/to/crates-docs` |
| 参数1 | `serve` |
| 参数2 | `--mode` |
| 参数3 | `stdio` |

5. 点击保存

> **注意**：将 `/path/to/crates-docs` 替换为实际的可执行文件路径。

### HTTP 模式

适合远程访问或网络服务：

```bash
crates-docs serve --mode hybrid --host 0.0.0.0 --port 8080
```

客户端配置：

```json
{
  "mcpServers": {
    "crates-docs": {
      "url": "http://your-server:8080/mcp"
    }
  }
}
```

## MCP 工具

### 1. lookup_crate - 查找 Crate 文档

从 docs.rs 获取完整文档。

| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `crate_name` | string | ✅ | Crate 名称，如 `serde`、`tokio` |
| `version` | string | ❌ | 版本号，默认最新 |
| `format` | string | ❌ | 输出格式：`markdown`（默认）、`text`、`html` |

```json
{ "crate_name": "serde" }
{ "crate_name": "tokio", "version": "1.35.0" }
```

### 2. search_crates - 搜索 Crate

从 crates.io 搜索 Rust crate，支持按相关性、总下载量、近期下载热度、最近更新时间和最新发布进行排序，适合做 crate 发现、选型和横向比较。

| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `query` | string | ✅ | 搜索关键词 |
| `limit` | number | ❌ | 结果数量（1-100），默认 10 |
| `sort` | string | ❌ | 排序方式，支持 `relevance`（默认）、`downloads`、`recent-downloads`、`recent-updates`、`new` |
| `format` | string | ❌ | 输出格式：`markdown`、`text`、`json` |

**排序建议**

- `relevance`：优先返回与关键词最相关的结果，适合通用搜索。
- `downloads`：按累计下载量排序，适合优先看生态里最常用、最成熟的 crate。
- `recent-downloads`：按近期下载热度排序，适合观察最近更活跃或更受关注的项目。
- `recent-updates`：按最近更新时间排序，适合关注仍在持续维护的 crate。
- `new`：按发布时间排序，适合探索新发布项目。

```json
{ "query": "web framework", "limit": 5, "sort": "downloads" }
{ "query": "mcp", "sort": "recent-downloads", "format": "json" }
```

### 3. lookup_item - 查找特定项目

查找 crate 中的特定类型、函数或模块。

| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `crate_name` | string | ✅ | Crate 名称 |
| `item_path` | string | ✅ | 项目路径，如 `serde::Serialize` |
| `version` | string | ❌ | 版本号 |
| `format` | string | ❌ | 输出格式 |

```json
{ "crate_name": "serde", "item_path": "serde::Serialize" }
{ "crate_name": "tokio", "item_path": "tokio::runtime::Runtime" }
```

### 4. health_check - 健康检查

检查服务器和外部服务状态。

| 参数 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `check_type` | string | ❌ | `all`、`external`、`internal`、`docs_rs`、`crates_io` |
| `verbose` | boolean | ❌ | 详细输出 |

```json
{ "check_type": "all", "verbose": true }
```

## 详细使用示例

### Stdio 模式

Stdio 模式适合与本地 MCP 客户端集成：

```bash
# 启动 Stdio 服务器
crates-docs serve --mode stdio

# 或使用默认配置（stdio 是某些客户端的默认模式）
crates-docs serve
```

**MCP 客户端配置示例：**

```json
{
  "mcpServers": {
    "crates-docs": {
      "command": "/usr/local/bin/crates-docs",
      "args": ["serve", "--mode", "stdio"]
    }
  }
}
```

### HTTP 模式

HTTP 模式适合远程访问或网络服务：

```bash
# 启动 HTTP 服务器
crates-docs serve --mode http --host 0.0.0.0 --port 8080

# 使用自定义配置
crates-docs serve --config config.toml
```

**使用 curl 测试 HTTP 端点：**

```bash
# 获取服务器信息（健康检查）
curl http://localhost:8080/health

# MCP 工具调用示例（需要 MCP 协议格式）
curl -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list"
  }'
```

### SSE 模式

SSE 模式支持服务器推送：

```bash
# 启动 SSE 服务器
crates-docs serve --mode sse --host 0.0.0.0 --port 8080
```

**SSE 客户端连接：**

```bash
# 连接到 SSE 端点
curl http://localhost:8080/sse
```

### 工具调用示例

#### search_crates - 搜索 Crate

```bash
# 使用 CLI 测试工具
crates-docs test --tool search_crates --query "serde" --limit 5

# 预期输出：
# 搜索 "serde" 的结果：
# 1. serde (v1.0.xxx) - 序列化框架
#    下载量: xxx
#    描述: ...
```

**MCP 调用参数：**

```json
{
  "name": "search_crates",
  "arguments": {
    "query": "web framework",
    "limit": 10,
    "sort": "downloads",
    "format": "markdown"
  }
}
```

#### lookup_crate - 查找 Crate 文档

```bash
# 使用 CLI 测试工具
crates-docs test --tool lookup_crate --crate-name serde

# 指定版本
crates-docs test --tool lookup_crate --crate-name tokio --version "1.35.0"
```

**MCP 调用参数：**

```json
{
  "name": "lookup_crate",
  "arguments": {
    "crate_name": "serde",
    "version": "1.0.200",
    "format": "markdown"
  }
}
```

#### lookup_item - 查找特定项目

```bash
# 使用 CLI 测试工具
crates-docs test --tool lookup_item --crate-name serde --item-path "serde::Serialize"
```

**MCP 调用参数：**

```json
{
  "name": "lookup_item",
  "arguments": {
    "crate_name": "tokio",
    "item_path": "tokio::runtime::Runtime",
    "version": "1.35.0",
    "format": "markdown"
  }
}
```

#### health_check - 健康检查

```bash
# 使用 CLI 执行健康检查
crates-docs health --check-type all --verbose

# 仅检查外部服务
crates-docs health --check-type external
```

**MCP 调用参数：**

```json
{
  "name": "health_check",
  "arguments": {
    "check_type": "all",
    "verbose": true
  }
}
```

## 使用示例

### 了解新 crate

**用户**: "帮我了解一下 serde"

**AI 调用**: `{ "crate_name": "serde" }`

### 查找特定功能

**用户**: "tokio 怎么创建异步任务？"

**AI 调用**: `{ "crate_name": "tokio", "item_path": "tokio::spawn" }`

### 搜索相关 crate

**用户**: "有什么稳定、大家都常用的 HTTP 客户端？"

**AI 调用**: `{ "query": "http client", "limit": 10, "sort": "downloads" }`

## 命令行

```bash
# 启动服务器
crates-docs serve                          # 混合模式
crates-docs serve --mode stdio             # Stdio 模式
crates-docs serve --mode http --port 8080  # HTTP 模式

# 生成配置
crates-docs config --output config.toml
crates-docs config --output config.toml --force

# 测试工具
crates-docs test --tool lookup_crate --crate-name serde
crates-docs test --tool search_crates --query "async"
crates-docs test --tool search_crates --query "mcp" --sort downloads
crates-docs test --tool search_crates --query "agent" --sort recent-updates --format json

# CLI 健康检查入口
crates-docs health
crates-docs health --check-type external --verbose

# 版本信息
crates-docs version
```

> 全局参数见 [`Cli`](src/cli/mod.rs:27)，常用项包括 `--config`、`--debug`、`--verbose`。
>
> [`run_health_command()`](src/cli/health_cmd.rs) 会执行真实的健康检查（内部状态 + docs.rs / crates.io 探测），与 MCP 工具 [`health_check`](src/tools/health.rs) 共用同一套检测逻辑。当整体状态不是 `healthy` 时，命令以非零退出码结束，可直接用作容器/编排器的健康探针（例如 Docker Compose 的 `healthcheck`）。

## 配置

### 完整配置文件示例

下面是一个完整的配置文件示例，包含所有可用的配置项：

```toml
# 服务器配置
[server]
name = "crates-docs"                    # 服务器名称
version = "0.1.0"                       # 服务器版本
description = "Rust crate docs MCP server"  # 服务器描述
host = "0.0.0.0"                        # 监听地址（0.0.0.0 允许外部访问）
port = 8080                             # 监听端口
transport_mode = "hybrid"               # 传输模式：stdio/http/sse/hybrid
enable_sse = true                       # 兼容字段；实际由 transport_mode 决定
enable_oauth = false                    # OAuth 尚未实现，true 会导致启动失败
max_connections = 100                   # 兼容字段，当前未强制执行
request_timeout_secs = 30               # 兼容字段，当前未强制执行
response_timeout_secs = 60              # 兼容字段，当前未强制执行
dns_rebinding_protection = false        # 启用后执行精确 Host/Origin 匹配
allowed_hosts = ["localhost:8080", "127.0.0.1:8080"]
allowed_origins = ["http://localhost:8080", "http://127.0.0.1:8080"]

# 缓存配置
[cache]
cache_type = "memory"                   # 缓存类型：memory 或 redis
memory_size = 1000                      # 内存缓存大小（条目数）
redis_url = "redis://localhost:6379"    # Redis 连接 URL（使用 redis 时必需）
key_prefix = ""                         # 缓存键前缀
default_ttl = 3600                      # 默认 TTL（秒）
crate_docs_ttl_secs = 3600              # crate 文档缓存 TTL（秒）
item_docs_ttl_secs = 1800               # 项目文档缓存 TTL（秒）
search_results_ttl_secs = 300           # 搜索结果缓存 TTL（秒）

# 日志配置
[logging]
level = "info"                          # 日志级别：trace/debug/info/warn/error
file_path = "./logs/crates-docs.log"    # 日志文件路径
enable_console = true                   # 启用控制台日志
enable_file = false                     # 启用文件日志
max_file_size_mb = 100                  # 单个日志文件最大大小（MB）
max_files = 10                          # 保留的日志文件数量

# 性能配置
[performance]
http_client_pool_size = 10              # HTTP 客户端连接池大小
http_client_pool_idle_timeout_secs = 90 # 连接池空闲超时（秒）
http_client_connect_timeout_secs = 10   # 连接超时（秒）
http_client_timeout_secs = 30           # 请求超时（秒）
http_client_read_timeout_secs = 30      # 读取超时（秒）
http_client_max_retries = 3             # HTTP 客户端最大重试次数
http_client_retry_initial_delay_ms = 100    # 重试初始延迟（毫秒）
http_client_retry_max_delay_ms = 10000      # 重试最大延迟（毫秒）
cache_max_size = 1000                   # 最大缓存大小
cache_default_ttl_secs = 3600           # 默认缓存 TTL（秒）
rate_limit_per_second = 100             # 兼容字段，当前未接入限流器
concurrent_request_limit = 50           # 兼容字段，当前未接入并发控制
enable_response_compression = false     # 保留 false；true 会导致启动失败
enable_metrics = false                  # 保留 false；true 会导致启动失败
metrics_port = 0                        # 兼容字段；当前无指标端点

# OAuth 配置仅为兼容保留；启用会导致启动失败
[auth.oauth]
enabled = false                         # 启用 OAuth
client_id = ""                          # OAuth 客户端 ID
client_secret = ""                      # OAuth 客户端密钥
authorization_endpoint = ""             # 授权端点 URL
token_endpoint = ""                     # Token 端点 URL
redirect_uri = ""                       # 回调 URI
scopes = []                             # OAuth 作用域

# API Key 认证配置（可选），必须使用 [auth.api_key]
[auth.api_key]
enabled = false                         # 启用 API Key 认证
keys = []                               # API Key 哈希列表（Argon2 PHC 格式），不要存明文 key
header_name = "X-API-Key"               # API Key 请求头名称
query_param_name = "api_key"            # API Key 查询参数名称
allow_query_param = false               # 是否允许查询参数传递
key_prefix = "sk"                       # API Key 前缀（用于生成和校验结构化 key）
```

### 环境变量配置

支持通过环境变量配置，适用于 Docker 部署。

对于 API Key，推荐先生成一次性明文 key，再把生成出的 **hash** 放入配置或环境变量中：

```bash
# 生成新的 API Key（会输出明文 key、key_id 和 hash）
crates-docs generate-api-key --prefix sk

# 服务器配置
CRATES_DOCS_HOST=0.0.0.0
CRATES_DOCS_PORT=8080
CRATES_DOCS_TRANSPORT_MODE=hybrid

# API Key 认证配置
CRATES_DOCS_API_KEY_ENABLED=true
CRATES_DOCS_API_KEYS='$argon2id$...generated_hash...'
CRATES_DOCS_API_KEY_HEADER=X-API-Key
CRATES_DOCS_API_KEY_ALLOW_QUERY=false
CRATES_DOCS_API_KEY_PREFIX=sk
```

### Docker 部署示例

```bash
# 使用环境变量启用 API Key 认证（保存 hash，不保存明文 key）
docker run -d \
  -p 8080:8080 \
  -e CRATES_DOCS_API_KEY_ENABLED=true \
  -e CRATES_DOCS_API_KEYS='$argon2id$...generated_hash...' \
  -e CRATES_DOCS_API_KEY_PREFIX=sk \
  -e CRATES_DOCS_HOST=0.0.0.0 \
  kingingwang/crates-docs:latest
```

### Docker Compose 示例

```yaml
version: '3.8'
services:
  crates-docs:
    image: kingingwang/crates-docs:latest
    ports:
      - "8080:8080"
    environment:
      - CRATES_DOCS_API_KEY_ENABLED=true
      - CRATES_DOCS_API_KEYS=$argon2id$...generated_hash...
      - CRATES_DOCS_API_KEY_PREFIX=sk
      - CRATES_DOCS_HOST=0.0.0.0
      - CRATES_DOCS_PORT=8080
```

### 配置项详细说明

#### `[server]` 服务器配置

| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `name` | string | `"crates-docs"` | 服务器名称 |
| `host` | string | `"127.0.0.1"` | 监听地址，设为 `"0.0.0.0"` 允许外部访问 |
| `port` | number | `8080` | 监听端口 |
| `transport_mode` | string | `"hybrid"` | 传输模式：`stdio`/`http`/`sse`/`hybrid` |
| `enable_sse` | boolean | `true` | 是否启用 SSE 支持 |
| `max_connections` | number | `100` | 兼容字段，当前未强制执行 |
| `allowed_hosts` | array | `["localhost", "127.0.0.1"]` | 允许的 Host 列表（CORS） |
| `allowed_origins` | array | `["http://localhost:*"]` | 允许的 Origin 列表（CORS） |

#### `[cache]` 缓存配置

| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `cache_type` | string | `"memory"` | 缓存类型：`memory` 或 `redis` |
| `memory_size` | number | `1000` | 内存缓存条目数 |
| `redis_url` | string | `null` | Redis 连接 URL |
| `key_prefix` | string | `""` | 缓存键前缀 |
| `crate_docs_ttl_secs` | number | `3600` | crate 文档缓存时间（秒） |
| `item_docs_ttl_secs` | number | `1800` | 项目文档缓存时间（秒） |
| `search_results_ttl_secs` | number | `300` | 搜索结果缓存时间（秒） |

#### `[logging]` 日志配置

| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `level` | string | `"info"` | 日志级别：`trace`/`debug`/`info`/`warn`/`error` |
| `enable_console` | boolean | `true` | 启用控制台输出 |
| `enable_file` | boolean | `false` | 启用文件日志 |
| `file_path` | string | `"./logs/crates-docs.log"` | 日志文件路径 |
| `max_file_size_mb` | number | `100` | 单个日志文件大小限制 |
| `max_files` | number | `10` | 保留的日志文件数 |

#### `[performance]` 性能配置

| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `http_client_pool_size` | number | `10` | HTTP 连接池大小 |
| `http_client_max_retries` | number | `3` | HTTP 请求最大重试次数 |
| `rate_limit_per_second` | number | `100` | 兼容字段，当前未接入限流器 |
| `concurrent_request_limit` | number | `50` | 兼容字段，当前未接入并发控制 |
| `enable_response_compression` | boolean | `false` | 尚未实现；设置为 `true` 会导致启动失败 |
| `enable_metrics` | boolean | `false` | 尚未实现；设置为 `true` 会导致启动失败 |

### 环境变量配置

所有配置项都可以通过环境变量覆盖，环境变量优先级最高：

```bash
# 服务器配置
export CRATES_DOCS_NAME="crates-docs"
export CRATES_DOCS_HOST="0.0.0.0"
export CRATES_DOCS_PORT="8080"
export CRATES_DOCS_TRANSPORT_MODE="hybrid"

# 日志配置
export CRATES_DOCS_LOG_LEVEL="info"
export CRATES_DOCS_ENABLE_CONSOLE="true"
export CRATES_DOCS_ENABLE_FILE="true"

# 缓存配置
export CRATES_DOCS_CACHE_TYPE="memory"
export CRATES_DOCS_CACHE_MEMORY_SIZE="1000"
export CRATES_DOCS_CACHE_REDIS_URL="redis://localhost:6379"

# 性能配置
export CRATES_DOCS_HTTP_CLIENT_POOL_SIZE="10"
export CRATES_DOCS_RATE_LIMIT_PER_SECOND="100"
```

> **注意**：环境变量会覆盖配置文件中的设置。布尔值使用 `"true"` 或 `"false"` 字符串表示。
>
> **API Key 安全建议**：
> - 使用 `crates-docs generate-api-key --prefix sk` 生成新的 key
> - 只保存生成结果中的 **hash**
> - 明文 key 只展示一次，之后请存入你的密钥管理系统
> - 不要把明文 key 直接写入 `config.toml`、Docker Compose 或环境变量示例中

> ⚠️ **重要安全说明**：
> - **API Key 认证**：已在 HTTP/SSE/Hybrid 传输层强制执行（需启用 `auth` feature，默认启用）。启用后，所有 MCP 请求必须携带有效的 `Authorization: Bearer <key>`（`/health` 端点保持开放以便监控）。
> - **OAuth 认证**：已配置但**尚未实现**。启用 OAuth 将在启动时被拒绝。请使用 API Key 认证。
> - **配置变更检测**：配置文件变更会被检测并记录，但所有变更都需要重启服务器才能生效。
> - **性能配置**：`enable_metrics` 和 `enable_response_compression` 设为 `true` 会导致启动失败；速率、并发、请求超时和最大连接数等兼容字段当前未在运行时生效。

### 生成配置文件

使用 CLI 生成默认配置文件：

```bash
# 生成配置文件
crates-docs config --output config.toml

# 强制覆盖已存在的文件
crates-docs config --output config.toml --force
```

## 传输协议

| 模式 | 适用场景 | 端点 |
|------|---------|------|
| `stdio` | MCP 客户端集成（推荐） | 标准输入输出 |
| `http` | 网络服务 | `POST /mcp` |
| `sse` | 向后兼容 | `GET /sse` |
| `hybrid` | 网络服务（推荐） | `/mcp` + `/sse` |

## MCP 端点

- `POST /mcp` - MCP Streamable HTTP 端点
- `GET /sse` - MCP SSE 端点

> 注意：这些是 MCP 协议端点，不是普通的 HTTP API。需要使用 MCP 客户端进行交互。

## API Key 认证使用指南

API Key 认证用于保护 HTTP/SSE/Hybrid 模式下的 MCP 端点，防止未授权访问。启用后，所有 MCP 请求必须携带有效的 API Key，`/health` 端点始终开放以便监控。

### 完整流程概览

```
1. 生成 API Key  →  得到明文 key（客户端用）和 hash（服务端存）
2. 配置服务端    →  将 hash 写入 config.toml 或环境变量
3. 启动服务      →  crates-docs serve --config config.toml
4. 客户端携带 key →  Authorization: Bearer <明文 key>
```

### 第一步：生成 API Key

```bash
crates-docs generate-api-key
```

输出示例：

```
Generated API key successfully.

Plain-text key (show once and store securely):
sk-live-<your-api-key-here>

Key ID:
<key-id>

Store this hash in configuration or secret storage:
$argon2id$v=19$m=47104,t=1,p=1$<salt>$<hash>
```

**⚠️ 重要**：
- **明文 key**（`sk-live-...`）只显示一次，请立即保存到密钥管理器中，服务端无法从 hash 反推出明文
- **hash**（`$argon2id$...`）写入服务端配置，用于验证客户端发来的 key
- 两者不可互换：客户端用明文，服务端存 hash

也可以指定自定义前缀：

```bash
crates-docs generate-api-key --prefix myapp
```

### 第二步：配置服务端

#### 方式 A：配置文件（推荐）

创建或编辑 `config.toml`，将生成的 hash 放入 `keys` 列表：

```toml
[auth.api_key]
enabled = true
keys = ["$argon2id$v=19$m=47104,t=1,p=1$<salt>$<hash>"]
header_name = "X-API-Key"
query_param_name = "api_key"
allow_query_param = false
key_prefix = "sk"
```

支持多个 key（例如为不同客户端分配不同 key）：

```toml
[auth.api_key]
enabled = true
keys = [
  "$argon2id$v=19$m=47104,t=1,p=1$hash1...",
  "$argon2id$v=19$m=47104,t=1,p=1$hash2...",
]
key_prefix = "sk"
```

#### 方式 B：环境变量

```bash
export CRATES_DOCS_API_KEY_ENABLED=true
export CRATES_DOCS_API_KEYS='$argon2id$v=19$m=47104,t=1,p=1$<salt>$<hash>'
export CRATES_DOCS_API_KEY_PREFIX=sk
```

#### 方式 C：CLI 标志

```bash
crates-docs serve --enable-api-key
```

> 注意：CLI 标志仅启用认证开关，key 列表仍需通过配置文件或环境变量提供。

### 第三步：启动服务

```bash
# 使用配置文件
crates-docs serve --config config.toml --mode hybrid --host 0.0.0.0 --port 8080

# 或使用环境变量
export CRATES_DOCS_API_KEY_ENABLED=true
export CRATES_DOCS_API_KEYS='$argon2id$...'
crates-docs serve --mode hybrid
```

启动后日志中会显示 API Key 认证已启用。

### 第四步：客户端使用 API Key

#### curl 测试

```bash
# 使用 Bearer token（直连服务端必须用此方式）
curl -H "Authorization: Bearer sk-live-<your-api-key-here>" \
  -X POST http://127.0.0.1:8080/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

# 未携带 key 的请求会返回 401
curl -X POST http://127.0.0.1:8080/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# → 401 Unauthorized

# /health 端点无需认证
curl http://127.0.0.1:8080/health
```

#### Claude Desktop 配置

```json
{
  "mcpServers": {
    "crates-docs": {
      "url": "http://your-server:8080/mcp",
      "headers": {
        "Authorization": "Bearer sk-live-<your-api-key-here>"
      }
    }
  }
}
```

#### Cursor 配置

编辑 `~/.cursor/mcp.json`：

```json
{
  "mcpServers": {
    "crates-docs": {
      "url": "http://your-server:8080/mcp",
      "headers": {
        "Authorization": "Bearer sk-live-<your-api-key-here>"
      }
    }
  }
}
```

#### 其他 MCP 客户端

任何支持自定义 HTTP header 的 MCP 客户端，添加以下 header 即可：

```
Authorization: Bearer <你的明文 key>
```

### 管理操作

#### 查看已注册的 API Key

```bash
crates-docs list-api-keys
```

#### 吊销 API Key

```bash
# 通过 Key ID 吊销
crates-docs revoke-api-key --key-id <key_id>

# 吊销后需重启服务生效
```

#### 添加新 Key

1. 运行 `crates-docs generate-api-key` 生成新 key
2. 将新 hash 追加到 `config.toml` 的 `keys` 列表中
3. 重启服务

### 常见问题

**Q: 为什么不能用 `X-API-Key` header 直连服务端？**

A: 进程内的 MCP 中间件只识别 `Authorization: Bearer` 头。`X-API-Key` header 需要配合反向代理使用——代理将 `X-API-Key: <k>` 改写为 `Authorization: Bearer <k>` 后转发给后端。详见下方"认证与加密传输"章节。

**Q: 可以通过 URL 查询参数传递 key 吗？**

A: 配置 `allow_query_param = true` 后，反向代理可以支持 `?api_key=<key>` 方式。但直连服务端仍只接受 Bearer token。出于安全考虑，不建议在 URL 中传递 key（会出现在日志和浏览器历史中）。

**Q: 忘记了明文 key 怎么办？**

A: 明文 key 只在生成时显示一次，无法从 hash 反推。如果丢失，需要生成新 key 并替换配置中的 hash。

**Q: 多个客户端可以共用同一个 key 吗？**

A: 可以，但建议为不同客户端分配不同 key，便于独立吊销。

## 认证与加密传输（TLS）

在 HTTP/SSE/Hybrid 服务器模式下，访问控制分为两层，可按需组合：

| 能力 | 由谁提供 | 说明 |
|------|----------|------|
| **认证**（只有持有密钥者可连接） | 进程内置（默认编译进二进制） | 校验 `Authorization: Bearer <key>`，密钥以 Argon2 哈希存储、常量时间比对 |
| **加密**（被监听也无法解密） | 前置反向代理（Caddy/nginx） | 终止 TLS，并把 `X-API-Key: <k>` 改写为 `Authorization: Bearer <k>` |

### 进程内置认证（Bearer）

`auth` 已包含在 `default` features 中，因此标准 `cargo build` / `cargo install`
即具备认证能力。是否真正启用是**运行时开关**，无需重新编译：

- 打开/关闭由 `auth.api_key.enabled` 控制（配置文件、`--enable-api-key`
  CLI 标志或 `CRATES_DOCS_API_KEY_ENABLED` 环境变量），改动后**重启生效**。
- 启用后，每个 MCP 请求都必须携带 `Authorization: Bearer <key>`，否则返回 `401`；
  `/health` 始终开放，便于监控。
- 密钥用 `crates-docs generate-api-key` 生成：配置里保存 **hash**，把**明文 key**
  发给客户端。吊销密钥 = 从 `keys` 移除并重启。

```bash
# 启用后，直连服务端必须使用 Bearer 头：
curl -H "Authorization: Bearer <明文 key>" -X POST http://127.0.0.1:8080/mcp
```

> **为什么是 Bearer 而不是 `X-API-Key`？** 进程内的 MCP 中间件**只**识别
> `Authorization: Bearer` 头，无法读取 `X-API-Key` 或查询参数。`header_name`、
> `allow_query_param` 等配置项仅对前置反向代理有意义（见下）。

### 加密传输与 `X-API-Key`（反向代理）

进程本身只说明文 HTTP，没有 TLS。若服务要对外暴露、需要加密，或希望继续使用
`X-API-Key` 头，请在前面加一层反向代理：它终止 TLS（HTTPS），把
`X-API-Key: <k>` 改写为 `Authorization: Bearer <k>`，再转发到仅监听 127.0.0.1
的后端——后端依旧做密码学校验，构成纵深防御。

```
客户端 --HTTPS, X-API-Key--> 反向代理 --HTTP, Authorization: Bearer--> 127.0.0.1:8080
```


开箱即用的配置（含 Caddy 与 nginx，以及完整步骤）见
**[`docs/reverse-proxy/`](docs/reverse-proxy/README.md)**。使用反向代理时，请把后端
绑定到回环地址（`server.host = "127.0.0.1"`），避免客户端绕过 TLS 直连后端。

## 缓存策略

### 内存缓存（默认）

- 当前实现位于 [`MemoryCache`](src/cache/memory.rs:37)
- 基于 `moka::sync::Cache`，使用 TinyLFU 淘汰策略
- 支持按条目 TTL 过期
- 适用于单实例部署

### Redis 缓存

- 支持分布式部署
- 支持持久化
- 通过 feature flag 启用：`cache-redis`

```bash
cargo build --release --features cache-redis
```

配置示例：

```toml
[cache]
cache_type = "redis"
redis_url = "redis://localhost:6379"
default_ttl = 3600
```

### 缓存 TTL 配置

支持为不同类型的数据配置独立的 TTL：
- `crate_docs_ttl_secs`: crate 文档缓存时间（默认 3600 秒 / 1 小时）
- `item_docs_ttl_secs`: 项目文档缓存时间（默认 1800 秒 / 30 分钟）
- `search_results_ttl_secs`: 搜索结果缓存时间（默认 300 秒 / 5 分钟）

## 部署

### Docker

```bash
# 使用预构建镜像
docker pull kingingwang/crates-docs:latest
docker run -d -p 8080:8080 kingingwang/crates-docs:latest

# 或使用特定版本
docker pull kingingwang/crates-docs:0.3.0
```

### Systemd

创建 `/etc/systemd/system/crates-docs.service`：

```ini
[Unit]
Description=Crates Docs MCP Server
After=network.target

[Service]
Type=simple
User=crates-docs
WorkingDirectory=/opt/crates-docs
ExecStart=/opt/crates-docs/crates-docs serve --config /etc/crates-docs/config.toml
Restart=on-failure

[Install]
WantedBy=multi-user.target
```

```bash
sudo systemctl enable crates-docs
sudo systemctl start crates-docs
```

### 云端部署

> **注意**：本项目是长驻 Rust 服务进程（监听端口并持续运行），不适用于 Vercel / Netlify 等 Serverless 平台。

推荐使用 [Render](https://render.com) 免费层部署（750 小时/月，无需信用卡，原生 Docker 支持）。

> **在线测试地址**：[https://crates-docs.onrender.com/mcp](https://crates-docs.onrender.com/mcp)
>
> 可直接在 MCP 客户端（Claude Desktop、Cursor 等）中配置此地址体验，无需自行部署。
> 该服务部署在 Render 免费层，15 分钟无请求会自动休眠，首次访问需要冷启动。
> 本项目是编译型 Rust 二进制（无 JVM 预热、无数据库初始化），应用本身启动为亚秒级，冷启动延迟主要来自 Render 免费层的容器调度。

云端部署推荐使用 `http` 传输模式（请求/响应模型，兼容性最好）：

```bash
# 任意 Docker 平台一键启动
docker run -d -p 8080:8080 \
  -e CRATES_DOCS_HOST=0.0.0.0 \
  -e CRATES_DOCS_TRANSPORT_MODE=http \
  -e CRATES_DOCS_API_KEY_ENABLED=true \
  -e CRATES_DOCS_API_KEYS=你的密钥 \
  kingingwang/crates-docs:latest
```

完整的 Render 部署分步教程见 **[docs/cloud-deployment.md](docs/cloud-deployment.md)**。

## 开发

```bash
# 构建
cargo build --release

# 运行所有测试
cargo test --all-features

# 运行 clippy 检查
cargo clippy --all-features --all-targets -- -D warnings

# 格式化检查
cargo fmt --check

# 运行完整 CI 流程
cargo clippy --all-features --all-targets -- -D warnings && \
cargo test --all-features && \
cargo fmt --check
```

### Feature Flags

| Feature | 描述 |
|---------|------|
| `default` | 默认启用完整服务端、Stdio、HTTP/SSE、内存缓存、日志和 API Key |
| `server` | 启用 rust-mcp-sdk 服务端能力 |
| `client` | 启用 rust-mcp-sdk 客户端能力 |
| `stdio` | 启用 Stdio 传输 |
| `hyper-server` | 启用 HTTP 服务器 |
| `streamable-http` | 启用 Streamable HTTP |
| `sse` | 启用 SSE 传输 |
| `macros` | 启用 MCP 宏支持 |
| `auth` | 启用 SDK 认证基础设施（OAuth 请求校验尚未实现） |
| `api-key` | 启用 API Key 认证支持（默认启用） |
| `cache-memory` | 启用内存缓存相关支持 |
| `cache-redis` | 启用 Redis 缓存 |
| `tls` | 启用 TLS/SSL 支持 |
| `logging` | 启用日志相关支持 |

## 故障排除

### 端口被占用

```bash
lsof -i :8080
kill -9 <PID>
```

### 网络问题

```bash
curl -I https://docs.rs/
curl -I https://crates.io/
```

### 日志

启用文件日志时，默认日志文件路径为 `./logs/crates-docs.log`。

```toml
[logging]
level = "debug"
```

## 许可证

MIT License

## 贡献

欢迎 Issue 和 Pull Request！

1. Fork 仓库
2. 创建分支 (`git checkout -b feature/amazing-feature`)
3. 提交更改 (`git commit -m 'Add amazing feature'`)
4. 推送分支 (`git push origin feature/amazing-feature`)
5. 创建 Pull Request

### 开发指南

- 所有代码必须通过 `cargo clippy --all-features --all-targets -- -D warnings`
- 所有测试必须通过 `cargo test --all-features`
- 新功能需要添加相应的单元测试
- 遵循现有的代码风格和文档规范

## 致谢

- [rust-mcp-sdk](https://github.com/rust-mcp-stack/rust-mcp-sdk) - MCP SDK
- [docs.rs](https://docs.rs) - Rust 文档服务
- [crates.io](https://crates.io) - Rust 包注册表
- [lru](https://crates.io/crates/lru) - 内存缓存淘汰策略实现

## API 文档

### docs.rs 文档

- **主文档**: [https://docs.rs/crates-docs](https://docs.rs/crates-docs)
- **最新版本**: [https://docs.rs/crates-docs/latest](https://docs.rs/crates-docs/latest)

### 主要类型使用示例

#### CratesDocsServer

```rust
use crates_docs::{AppConfig, CratesDocsServer};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 使用默认配置
    let config = AppConfig::default();
    
    // 创建服务器
    let server = CratesDocsServer::new(config)?;
    
    // 运行 HTTP 服务器
    server.run_http().await?;
    
    Ok(())
}
```

#### 缓存使用

```rust
use std::sync::Arc;
use crates_docs::cache::{Cache, CacheConfig, create_cache};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建内存缓存
    let config = CacheConfig::default();
    let cache: Arc<dyn Cache> = Arc::from(create_cache(&config)?);
    
    // 设置缓存值
    cache.set(
        "key".to_string(),
        "value".to_string(),
        Some(std::time::Duration::from_secs(3600))
    ).await?;
    
    // 获取缓存值
    if let Some(value) = cache.get("key").await {
        println!("Cached value: {}", value);
    }
    
    Ok(())
}
```

#### 工具注册表

```rust
use std::sync::Arc;
use crates_docs::tools::{ToolRegistry, create_default_registry};
use crates_docs::tools::docs::DocService;
use crates_docs::cache::memory::MemoryCache;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 创建缓存和文档服务
    let cache = Arc::new(MemoryCache::new(1000));
    let doc_service = Arc::new(DocService::new(cache)?);
    
    // 创建默认工具注册表
    let registry = create_default_registry(&doc_service);
    
    // 获取所有工具
    let tools = registry.get_tools();
    println!("Registered {} tools", tools.len());
    
    Ok(())
}
```

### 模块文档

- [`crates_docs::cache`](https://docs.rs/crates-docs/latest/crates_docs/cache/index.html) - 缓存模块
- [`crates_docs::config`](https://docs.rs/crates-docs/latest/crates_docs/config/index.html) - 配置模块
- [`crates_docs::error`](https://docs.rs/crates-docs/latest/crates_docs/error/index.html) - 错误处理
- [`crates_docs::server`](https://docs.rs/crates-docs/latest/crates_docs/server/index.html) - 服务器模块
- [`crates_docs::tools`](https://docs.rs/crates-docs/latest/crates_docs/tools/index.html) - MCP 工具
- [`crates_docs::utils`](https://docs.rs/crates-docs/latest/crates_docs/utils/index.html) - 工具函数

## 支持

- [Issues](https://github.com/KingingWang/crates-docs/issues)
- Email: kingingwang@foxmail.com

## Star History

<a href="https://www.star-history.com/?repos=KingingWang%2Fcrates-docs&type=date&legend=top-left">
 <picture>
   <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=KingingWang/crates-docs&type=date&theme=dark&legend=top-left" />
   <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=KingingWang/crates-docs&type=date&legend=top-left" />
   <img alt="Star History Chart" src="https://api.star-history.com/image?repos=KingingWang/crates-docs&type=date&legend=top-left" />
 </picture>
</a>
