语义搜索服务器

一个自动向量化Outline wiki文档的RAG(检索增强生成)+MCP(模型上下文协议)服务器,使AI代理能够进行语义自然语言搜索。

  • by sinjinwoo
  • Python
  • 本地部署
  • 更新于
在 GitHub 查看 sinjinwoo/outlineMcp

使用说明

🌐 语言: 英语 | 韩语


🚀 RAG MCP服务器概述

RAG(检索增强生成)+MCP(模型上下文协议)服务器,自动将Outline wiki中的文档矢量化,以便AI代理(Claude Desktop等)可以运行 语义自然语言搜索 在他们身上。

它 共享自托管Outline堆栈的现有Redis 而不是旋转自己的,所以它下降与 一个Docker Compose文件 并且没有浪费资源。


✨ 主要特点

  • 单容器部署(outline-mcp-vector):FastAPI、Celery(Worker/Beat)和MCP服务器都在一个容器中运行,这使操作变得简单。
  • 基础设施效率:重用Outline现有的Redis容器,隔离在自己的逻辑DB上(db/1)所以这两条队伍永远不会相撞。
  • 智能增量同步:实时webhooks和定期调度程序(默认情况下为每小时一次)协同工作,仅跟踪自上次以来更改或删除的文档 updated_at 游标——每次运行时都不会完全重新嵌入。
  • Gemini钥匙池:注册多个Gemini API密钥,它们被称为round-robin,当达到速率限制时自动故障转移(429)。
  • MCP身份验证是选择Keycloak/OAuth:默认情况下,MCP服务器(流式HTTP)完全打开--set MCP_OAUTH_ENABLED=true (加上发行者/资源/受众)要求使用Keycloak发行的Bearer JWT。此项目只充当OAuth资源服务器;它不会运行或配置Keycloak本身。

🛠 建筑

Outline Stack (existing infra)         outline-net (shared network)
┌──────────────────────────────┐              │
│  [Outline]     [Redis]       │◄─────────────┼──────────────┐
└───────────────────▲──────────┘              │              │
                    │ (reuses logical DB /1)   │               │
┌───────────────────┴─────────────────────────▼──────────────┼
│ outline-mcp-vector (1 Container Stack)                     │              
│  - FastAPI (receives webhooks, responds immediately)        │
│  - Celery Worker & Beat (background chunking/embedding/sched)│
│  - MCP Server (serves the search_knowledge tool over Streamable HTTP) │
└─────────────────────────────────────────────┬──────────────┘
                                              │ (rag-net)
                                      ┌──────▼───────────────────────┐
                                      │ Qdrant (internal vector DB)   │
                                      └──────────────────────────────┘


📦 3分钟快速入门

1.准备大纲并创建webhook

在启动RAG服务器之前,从Outline管理屏幕收集所需内容。

  1. 发布API密钥:在大纲中,转到 设置→ API令牌 并创建新令牌(OUTLINE_API_KEY).
  2. 注册一个webhook并获取其秘密首选 设置→ Webhooks→ 新建webhook,按如下方式注册,并复制 秘密 展示。
  • 统一资源定位符: http://<your-server-ip>:17000/webhook/outline (RAG服务器监听的地址)
  • 事件:选择 documents.create, documents.update, documents.delete
  • 将创建后显示的秘密字符串保存在安全的地方(OUTLINE_WEBHOOK_SECRET).

2.检查Outline的Docker网络

为了让RAG服务器通过内部网络访问Outline和Redis容器,您现有的Outline docker-compose.yml 需要一个明确的外部网络名称(outline-net).

# Add this to your existing Outline docker-compose.yml if it's missing, then restart Outline
services:
  outline:
    networks: [outline-net]
  redis:
    networks: [outline-net]

networks:
  outline-net:
    name: outline-net

3.设置环境变量(.env)

创建一个 .env 将文件放入安装目录中,并填写步骤1中的值以及所需的设置。

# Outline integration (values gathered in step 1)
OUTLINE_API_KEY=ol_api_xxxxxxxxxxxx        # Outline API token
OUTLINE_WEBHOOK_SECRET=your_secret_key     # Secret copied from the Outline webhook screen
OUTLINE_API_URL=http://outline:3000        # Internal Docker-network URL
OUTLINE_PUBLIC_URL=https://wiki.domain.com # Public URL real users hit in their browser

# AI and vector DB settings
GOOGLE_API_KEYS=key1,key2,key3             # Gemini API keys (comma-separated for a round-robin pool)
QDRANT__SERVICE__API_KEY=strong_qdrant_key # Any string you like, used to authenticate to Qdrant

# DNS rebinding protection (Host/Origin header validation). Comma-separate your deployed
# domain(s); leave unset to keep this off (pre-existing behavior). Only relevant against
# malicious webpages attacking through a browser — non-browser clients (curl, Claude
# Desktop's remote connector) don't send an Origin header and aren't affected either way.
# MCP_ALLOWED_HOSTS=mcp.your-domain.com
# MCP_ALLOWED_ORIGINS=https://mcp.your-domain.com

# MCP auth — off by default (server is fully open). Set MCP_OAUTH_ENABLED=true and fill in
# the rest to require a Keycloak-issued Bearer JWT instead. This server is a Resource Server
# only; it validates against your Keycloak realm's JWKS, it doesn't run Keycloak itself
# (see docs/keycloak-reference-compose.yml for a throwaway realm to test against).
# MCP_OAUTH_ENABLED=true
# MCP_OAUTH_ISSUER_URL=https://keycloak.your-domain.com/realms/myrealm
# MCP_OAUTH_RESOURCE_URL=https://mcp.your-domain.com/mcp
# MCP_OAUTH_AUDIENCE=outline-mcp-client

请参阅MCP规范 授权指南 了解OAuth背后的概念 MCP_OAUTH_* 以上变量(资源服务器与授权服务器、受保护的资源元数据等)。

变量 必填? 默认 备注
OUTLINE_API_KEY 必需的 - 概述API令牌(设置→ API和应用程序
OUTLINE_WEBHOOK_SECRET 必需的 -- 概述webhook签名密钥。如果留空,则完全跳过webhook签名验证——不建议
OUTLINE_BASE_URL 必需的 -- 大纲的公共URL;也是退路 OUTLINE_API_URL/OUTLINE_PUBLIC_URL 在......下面
GOOGLE_API_KEYS 必需的† - Comma-separated Gemini API密钥池,带故障转移的循环
QDRANT__SERVICE__API_KEY 必需的 -- 任何字符串;也传递给Qdrant容器本身 service.api_key
GEMINI_API_KEY 可选 -- ¹如果不需要,可选择单键回退 GOOGLE_API_KEYS'泳池
OUTLINE_API_URL 可选 = OUTLINE_BASE_URL 实际Outline API调用的内部Docker-network URL
OUTLINE_PUBLIC_URL 可选 = OUTLINE_BASE_URL 用于构建搜索结果中显示的文档链接的公共URL
GEMINI_EMBEDDING_DIM 可选 3072 输出向量维度
GEMINI_TIMEOUT_MS 可选 30000 每个请求Gemini超时,毫秒
QDRANT_URL 可选 http://localhost:6333
QDRANT_TIMEOUT_SECONDS 可选 10 按请求Qdrant超时,秒
MCP_HOST 可选 0.0.0.0
MCP_PORT 可选 8080
MCP_ALLOWED_HOSTS 可选 (空→ 保护关闭) 用于DNS重新绑定保护的逗号分隔的主机允许列表
MCP_ALLOWED_ORIGINS 可选 (空) 逗号分隔的Origin允许列表,相同的保护
MCP_OAUTH_ENABLED 可选 false 打开Keycloak/OAuth承载令牌身份验证;服务器完全打开,同时 false
MCP_OAUTH_ISSUER_URL 如果需要 MCP_OAUTH_ENABLED=true -- Keycloak领域颁发者URL
MCP_OAUTH_RESOURCE_URL 如果需要 MCP_OAUTH_ENABLED=true -- 此服务器自己的外部URL,包括 /mcp 路径
MCP_OAUTH_AUDIENCE 如果需要 MCP_OAUTH_ENABLED=true -- 必须与 aud 声明您的Keycloak客户端问题
MCP_OAUTH_JWKS_URL 可选 {issuer}/protocol/openid-connect/certs 仅当您的IdP不使用Keycloak的默认路径时才需要
REDIS_URL 可选 redis://redis:6379/1 共享Outline的Redis容器,逻辑DB 1
SYNC_INTERVAL_SECONDS 可选 3600 Celery Beat的增量同步间隔

为设置Keycloak客户端 MCP_OAUTH_*: 在Keycloak管理控制台中,在您指向的领域上 MCP_OAUTH_ISSUER_URL at--

  1. 一个全新的领域没有自己的用户——领域创建向导不会要求用户 master 您使用do登录控制台的realm管理员帐户 不 结转。如果有人会以交互方式登录这个领域(例如Claude基于浏览器的OAuth流,而不是纯粹的机器对机器) client_credentials 设置),首先创建一个真实用户: 用户 → 创建新用户,然后该用户的 凭证 tab → 设置密码.
  2. 客户 → 创建客户端 → 给它一个客户端ID(这与您要输入的值相同 MCP_OAUTH_AUDIENCE).如果此客户端将用于浏览器登录流,请同时启用 标准流程 在其“设置”选项卡上,并将调用者自己的OAuth回调添加到 有效的重定向URI (克劳德的 https://claude.ai/api/mcp/auth_callback)--跳过此步骤,Keycloak只允许用户成功登录 然后 拒绝重定向回,这看起来像是登录失败,但实际上不是。
  3. 点击该客户端→ 客户端作用域 tab → 打开它 <client-id>-dedicated 范围。
  4. 添加映射器 → 按配置 → 观众,然后设置 包括客户受众 对于同一个客户来说,这就是实际盖章的地方 aud 索赔;如果没有它,发行的代币将携带Keycloak的默认值 aud (account)相反,每个请求都失败了,签名相邻但受众与401不匹配。
  5. 保存,然后通过解码新发布的令牌(例如在jwt.io)并检查来确认它是否有效 aud 实际上包含您的客户端ID——一个保存为空受众字段的映射器不会添加任何内容,这很容易被忽略。

还有一个特定的gotcha MCP_OAUTH_RESOURCE_URL:它必须是一个地址 客户 实际上可以访问——如果该客户端是克劳德,它正在Anthropic的云中运行,而不是在你的机器上。指向此 localhost 表示401响应 WWW-Authenticate 标头通告了一个无法访问的元数据URL,因此OAuth发现会自动失败。如果你正在对本地开发服务器(例如ngrok)进行隧道传输,这需要是隧道的实际公共HTTPS URL,每次自由层隧道重启时,它都会发生变化。另外,如果你在docker compose后面运行这个, docker compose restart 重用容器的原始环境,不会获取更新的环境 .env --使用 docker compose up -d 而是重新创建它。

4.运行Docker Compose

创建一个 docker-compose.yml 把下面的内容提出来。

version: '3.8'

services:
  qdrant:
    image: qdrant/qdrant:latest
    expose:
      - "6333"
    environment:
      - QDRANT__SERVICE__API_KEY
    volumes:
      - qdrant_data:/qdrant/storage
    networks:
      - rag-net
    healthcheck:
      test: ["CMD-SHELL", "bash -c 'echo -e \"GET /healthz HTTP/1.1\\r\\nHost: localhost\\r\\nConnection: close\\r\\n\\r\\n\" > /dev/tcp/localhost/6333' || exit 1"]
      interval: 10s
      timeout: 5s
      retries: 5

  outline-mcp-vector:
    image: sjw0066/outline-mcp-vector:latest
    env_file: .env
    environment:
      - QDRANT_URL=http://qdrant:6333
      - REDIS_URL=redis://redis:6379/1 # Shares Outline's Redis container (logical DB 1)
      - MCP_HOST=0.0.0.0
      - MCP_PORT=8080
    ports:
      - "17000:8000"   # FastAPI port (webhook, sync)
      - "17080:8080"   # FastMCP Streamable HTTP port
    volumes:
      - sync_state:/data
    networks:
      - rag-net
      - outline-net    # Joins Outline's existing network
    depends_on:
      qdrant:
        condition: service_healthy

networks:
  rag-net:
    driver: bridge
  outline-net:
    external: true     # References the already-running Outline network

volumes:
  qdrant_data:
  sync_state:

docker compose up -d

💡 备注:容器启动后,它检测到Qdrant向量数据库为空,并自动拉入Outline的所有现有文档进行初始完全同步。


🔗 外部客户端设置指南(HTTPS/流式HTTP)

如何通过Nginx反向代理和家庭服务器域上的SSL证书从家庭服务器外的PC安全连接。

Nginx反向代理配置(必填)

因此,FastMCP的Streamable HTTP传输使用的流式响应不会被切断,请确保您的域 proxy_pass RAG服务器的块映射 港口 17080 并禁用代理缓冲。

server {
    listen 443 ssl;
    server_name mcp.your-domain.com;

    ssl_certificate /path/to/fullchain.pem;
    ssl_certificate_key /path/to/privkey.pem;

    location / {
        # ⭐ Key part: forward to the MCP port (17080) exposed by docker-compose.
        proxy_pass http://localhost:17080;
        
        # Required to keep the streaming connection alive in real time
        proxy_buffering off;
        proxy_cache off;
        proxy_set_header Connection '';
        chunked_transfer_encoding off;
        
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

2.克劳德桌面设置

默认情况下(MCP_OAUTH_ENABLED 未设置)MCP服务器处理任何请求,根本不进行身份验证检查——在VPN/专用网络后面很好,但任何直接到达URL的人都可以搜索你的知识库,所以如果可以从开放的互联网上访问,就把它放在某个东西后面(反向代理IP分配列表、VPN等)。在该模式下,只需将Claude Desktop指向URL:

{
  "mcpServers": {
    "outline-knowledge-base": {
      "url": "https://mcp.your-domain.com/mcp"
    }
  }
}

如果你打开 MCP_OAUTH_ENABLED=true (Keycloak),添加连接器 高级设置→ OAuth客户端ID/OAuth客户端密码 填写Keycloak客户端的凭据,而不是将其留空。Claude Desktop在技术上可以发现服务器的OAuth元数据,并自行尝试动态客户端注册,但Keycloak的默认“受信任的主机”客户端注册策略拒绝来自它不知道的主机的匿名DCR——在实践中,无论哪种方式,你都需要一个预先注册的客户端,与下面的Claude Code设置相同(包括注册Claude的回调, https://claude.ai/api/mcp/auth_callback,作为a 有效的重定向URI 该客户)。一旦添加了凭据,第一个连接将引导您完成正常的浏览器登录/同意流程——没有静态令牌可以粘贴到配置中。看 docs/keycloak-reference-compose.yml 如果你想在本地测试,可以使用一次性Keycloak领域。无法运行基于浏览器的OAuth流的客户端可以直接将已颁发的Keycloak访问令牌作为 Authorization: Bearer <token> 头球

3.克劳德代码(CLI)设置

关闭OAuth后,像添加其他远程HTTP服务器一样添加它:

claude mcp add --transport http outline-knowledge-base https://mcp.your-domain.com/mcp

随着 MCP_OAUTH_ENABLED=true,跳过动态客户端注册,注册一个预配置的客户端——Keycloak的默认“受信任的主机”客户端注册策略拒绝来自它不知道的主机的匿名DCR(像ngrok这样的普通开发隧道会遇到这个问题),Claude Code自己的OAuth回调是一个本地环回URL(http://localhost:PORT/callback),与Claude Desktop的不同(https://claude.ai/api/mcp/auth_callback)--如果同时使用这两个,则都需要在同一Keycloak客户端上注册。

  1. 在Keycloak中,添加 http://localhost:8080/callback (或您在下面选择的任何端口)到该客户的 有效的重定向URI,以及已经存在的任何其他客户端的回调。
  2. 使用该客户端的凭据注册服务器:
    claude mcp add-json outline-knowledge-base \
      '{"type":"http","url":"https://mcp.your-domain.com/mcp","oauth":{"clientId":"your-client-id","callbackPort":8080}}' \
      --client-secret
    
    (提示输入密码;它存储在您的系统密钥链/凭据存储中,而不是 .mcp.json)
  3. claude mcp login outline-knowledge-base (或 /mcp 在会话内)通过浏览器登录运行。

⚙️ 手动同步和管理API

用于直接控制索引,超出后台自动同步(默认情况下每小时一次)。

  • 触发增量同步: POST http://localhost:17000/sync/outline
  • 强制进行全面重新索引: POST http://localhost:17000/sync/outline?full=true
  • 检查同步状态: GET http://localhost:17000/sync/status

如果这个开源项目对你有用,请用⭐️ 星!请在“问题”选项卡中留下任何问题。