Skip to main content
Glama
MaxPopov
by MaxPopov

wikijs-mcp-google-auth

在现有 Wiki.js 2.5.x 之上包一层 MCP:企业用户用 Google Workspace 登录,然后通过 LLM(claude.ai、Claude Desktop 或任意 MCP 客户端)操作 wiki —— 严格遵守该用户自己的 Wiki.js 权限范围

核心原则:授权判断以 Wiki.js 为唯一事实来源。 MCP 服务器本身没有任何用户、组或权限,也没有全局 API 密钥。每个操作都以该用户自己的原生 Wiki.js JWT 执行,是允许还是拒绝(Groups / Permissions / Page Rules)完全由 Wiki.js 决定。

Google Workspace ──OAuth/OIDC──▶ MCP Server ──signed assertion──▶ Wiki.js
                                     │         auth module "mcpdelegation"
                                     │         → refreshToken() → native JWT
                                     │
 MCP client (claude.ai / Desktop) ◀──┴── tools: search / get / list /
                                          create / update / delete / whoami
                                          (all via GraphQL with the user's JWT)

组件

目录

说明

packages/wikijs-auth-module/

为 Wiki.js 2.5.x 定制的认证模块——验证 MCP 服务器签发的 RS256 断言,并返回原生 Wiki.js JWT(详情

packages/mcp-server/

远程 MCP 服务器(Streamable HTTP):面向 MCP 客户端的 OAuth 2.1 授权服务器,底层基于 Google OIDC + 令牌代理 + 工具

packages/e2e-ui/

仅测试:浏览器 UI 端到端(Playwright)和一个独立的自研假 Google IdP 模拟器

deploy/docker-compose.dev.yml

隔离的测试环境(Wiki.js 2.5.303 + Postgres + ACL 种子数据)——仅用于开发/CI

deploy/docker-compose.e2e.yml

完整 UI 端到端测试栈(假 IdP + Wiki.js + MCP + Playwright)——仅测试用

deploy/docker-compose.prod.yml

生产部署:只包含 MCP 服务器,指向你现有的 Wiki.js

deploy/seed/run.mjs

测试台种子数据入口(底层库是 seed.mjs

Related MCP server: Yandex Wiki MCP

工作原理

  1. MCP 客户端连接到 https://mcp.company.com/mcp,并执行 OAuth 2.1(动态客户端注册 + PKCE)。Google 不支持 DCR,因此 MCP 服务器本身充当客户端侧的授权服务器,而 Google 仅用于认证“人”。Google 的令牌永远不会离开服务器;客户端收到的是 MCP 服务器自己的不透明令牌。Google 登录完成后,用户会看到一个同意屏幕(consent screen),其中标明应用程序名称及其重定向 URI——这是针对“混淆代理”(confused deputy)问题的防御:第三方注册的客户端无法在用户不知情的情况下获取用户令牌;该批准按“用户 × 客户端”分别记忆。

  2. 验证 Google 的 id_token(验签、issaudemail_verified,以及 hd = 你的 Workspace 域名)。

  3. MCP 服务器的令牌代理(token broker)把 Google 身份交换成原生 Wiki.js JWT:它对一段短时有效的 RS256 断言(TTL 60 秒、jti 唯一)签名,然后以 mcpdelegation 策略调用标准 GraphQL mutation authentication.login。Wiki.js 模块验签断言、按邮箱解析用户,并通过标准 refreshToken() 流程返回 JWT。该 JWT 会被缓存,并在过期前续签。

  4. 每次工具调用都会携带 Authorization: Bearer <用户的 JWT> 访问 Wiki.js GraphQL。无权限的页面无法读取或修改,不会出现在搜索结果或列表中——已由端到端测试验证(两个分属不同组的用户构成的 allow / forbidden 矩阵)。

工具

工具

说明

whoami

当前用户的身份 + 其在 Wiki.js 中的组与权限(访问诊断)

search_wiki

全文搜索;结果按当前用户权限过滤

get_page

按 id 或路径获取页面(元数据 + 完整 markdown)

list_pages

列出当前用户可见的页面(支持按路径前缀过滤)

create_page

创建页面(markdown)

update_page

更新页面:读-合并-写,未指定的字段保持不变

delete_page

删除页面(破坏性操作;Wiki.js 会强制检查 delete:pages 权限)


集成到你的 Wiki.js:分步指南

你需要:Wiki.js 2.5.x(已在 2.5.303 上验证)、能访问其文件系统或 Docker 配置;一台具备公网 HTTPS 端点的 MCP 服务器主机;以及你的 Workspace 的 Google Cloud Console 管理员权限。

第 1 步:在 Wiki.js 中安装认证模块

Docker: 给 wiki 服务添加一个 volume,然后重启容器:

services:
  wiki:
    image: ghcr.io/requarks/wiki:2.5.303
    volumes:
      - /opt/wikijs-mcp/wikijs-auth-module:/wiki/server/modules/authentication/mcpdelegation:ro

(本仓库 packages/wikijs-auth-module/ 的内容需要放进 /opt/wikijs-mcp/wikijs-auth-module;注意:目标目录名必须恰好是 mcpdelegation

裸机部署:packages/wikijs-auth-module/ 复制到 <wiki>/server/modules/authentication/mcpdelegation/,然后重启 Wiki.js。

配置完成(第 3 步)后,Wiki.js 日志中会出现:

Authentication Strategy MCP Delegation: [ OK ]

第 2 步:生成断言密钥

openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out mcp-assertion-key.pem
openssl pkey -in mcp-assertion-key.pem -pubout -out mcp-assertion-key.pub.pem

私钥(mcp-assertion-key.pem只能保留在 MCP 服务器主机上;公钥则在下一步配置到 Wiki.js 中。

第 3 步:在 Wiki.js 管理后台配置该策略

管理后台 → Auth → 添加策略 → MCP Delegation

  • 断言公钥(PEM) —— 填写 mcp-assertion-key.pub.pem 中的全部内容;

  • 期望的 Audience / Issuer —— 保持默认值(urn:wikijs:mcp-delegation / urn:wikijs-mcp-google-auth);

  • User Lookup Provider Priority —— 按邮箱查找用户时各个 Provider 的顺序。如果你的同事是通过 Google/OIDC 登录 wiki 的,把它放到最前面(也接受模块键名:googleoidclocal);

  • (可选)自助注册 + 域名白名单 + 自动加组 —— 这样新 Workspace 用户在第一次通过 MCP 发起请求时会被自动创建;

  • 保存。

列表中会显示该策略实例的 key(也就是 MCP 服务器要用到的 WIKIJS_STRATEGY_KEY;如果你是通过界面手动创建的,Wiki.js 会生成一个 uuid —— 复制它即可)。

已开启 TFA 的账号无法通过委托方式使用 —— MCP 服务器会返回一个明确的错误。

第 4 步:创建 Google OAuth 客户端

Google Cloud Console → APIs & Services → Credentials → 创建凭据 → OAuth 客户端 ID

  • 应用类型:Web 应用

  • 授权重定向 URI:https://mcp.company.com/oauth/google/callback(即你的 PUBLIC_URL 再拼接 /oauth/google/callback);

  • OAuth 同意屏幕:类型选择 Internal(仅限你的 Workspace 内部)。

保存得到的 Client ID 和 Client Secret。

第 5 步:部署 MCP 服务器

cd deploy
cp .env.example .env        # fill in the values
mkdir -p keys && cp /path/to/mcp-assertion-key.pem keys/
chmod 644 keys/mcp-assertion-key.pem   # the container runs as non-root node (uid 1000)
docker compose -f docker-compose.prod.yml up -d

容器以非 root 的 node 用户运行——挂载进去的密钥文件必须对它可读(chmod 644);私钥本身的保护仍由宿主机 keys/ 目录的权限来保证。

.env 变量:

变量

MCP_IMAGE

带标签的镜像(当版本更新合并到 main 时,Release on main 工作流会自动发布 ghcr.io/<owner>/wikijs-mcp-server:vX.Y.Z;也可以本地构建:docker build -f packages/mcp-server/Dockerfile -t wikijs-mcp-server:local .

PUBLIC_URL

MCP 服务器的公网 HTTPS 地址

WIKIJS_URL

你的 Wiki.js 地址(建议使用内网地址)

WIKIJS_STRATEGY_KEY

第 3 步得到的策略实例密钥(如果你把它命名为 mcpdelegation,这里就填它)

GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET

来自第 4 步

GOOGLE_ALLOWED_DOMAIN

你的 Workspace 域名,例如 company.com —— 外部账号一律拒绝

在 8000 端口前放一个 TLS 反向代理。最简 nginx 配置:

server {
  listen 443 ssl http2;
  server_name mcp.company.com;
  # ssl_certificate ...; ssl_certificate_key ...;
  location / {
    proxy_pass http://127.0.0.1:8000;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_set_header Host $host;
    proxy_buffering off;          # streamable HTTP
  }
}

验证:运行 curl https://mcp.company.com/healthz 应返回 {"ok":true};再运行 curl https://mcp.company.com/.well-known/oauth-authorization-server 应返回 OAuth 元数据。

第 6 步:接入客户端

claude.ai(Team / Enterprise): Settings → Connectors → Add custom connector → URL 填 https://mcp.company.com/mcp。首次使用时客户端会自动执行 OAuth:客户端注册 → Google 登录 → 完成。

Claude Desktop: Settings → Connectors → 添加自定义连接器并填同一个 URL(旧版本也可以用 mcp-remote)。

MCP Inspector(诊断工具): 运行 npx @modelcontextprotocol/inspector → Transport 选 Streamable HTTP → URL 填 https://mcp.company.com/mcp → 点 Open Auth,完整走一遍流程。

第 7 步:验证

在 LLM 对话中:

  1. “我在 wiki 里能做什么?” → whoami 工具应显示你的邮箱、所属组以及 Wiki.js 中的权限。

  2. 让它查找/打开你有权限的页面 → 正常。

  3. 让它打开一个你无权限的页面 → 会得到明确的拒绝(“Wiki.js denied this operation…”);并且该页面也不会出现在搜索/列表结果中。


本地开发

npm ci
npm run stand:up      # Wiki.js 2.5.303 + Postgres (docker)
npm run stand:seed    # finalize + groups/users/pages + strategy + dev keys
npm test              # unit tests (auth module + OAuth provider)
npm run build && npm run e2e   # in-process e2e: delegation, OAuth, tools — against a live stand
npm run stand:down

测试台账号:admin@example.com/admin1234!john@example.com(Engineering 组,无 /management/* 权限)、kate@example.com(Management 组)。每个 PR 都会运行快速检查(CI:lint + 单元测试 + 构建);重量级的 docker e2e(e2e)和浏览器 ui-e2e 只在推送到 dev/main 时(即合并前)运行,这样不会拖慢 PR 的迭代速度。

按角色运行浏览器 UI 端到端(Playwright)

另一套 docker compose 栈 deploy/docker-compose.e2e.yml 会拉起一个假 Google IdP 模拟器packages/e2e-ui/idp/ —— 一个带角色选择器的登录页,而不是真实 Google IdP)、Wiki.js、MCP 服务器以及一个Playwright 运行器,用来在不同角色(John/Kate/域外用户)下完整驱动浏览器的 OAuth + 授权流程。该模拟器和 Playwright 只会在 e2e 这套栈中启动——它们永远不会被打进 prod/dev 镜像。

C=deploy/docker-compose.e2e.yml
docker compose -f $C build mcp
docker compose -f $C up -d db wiki idp   # no --wait on wiki: the seed script is the readiness gate
docker compose -f $C run --rm seed
docker compose -f $C up -d --wait mcp
docker compose -f $C run --rm playwright     # exit code = test result
docker compose -f $C down -v

它会检查:以某个角色登录 → 同意屏幕显示客户端名称 → 批准 → 返回 whoami 及该角色权限范围内的页面(John 看不到 management/*,Kate 可以);拒绝 → access_denied;域外账号在进入同意步骤之前就会被拒绝。一个独立的 CI 工作流(ui-e2e)会在推送到 dev/main 时执行这些检查。

手动针对测试实例运行 MCP 服务器:

PUBLIC_URL=http://localhost:8000 \
WIKIJS_URL=http://127.0.0.1:3000 \
MCP_ASSERTION_PRIVATE_KEY_FILE=deploy/keys/mcp-assertion-key.pem \
GOOGLE_CLIENT_ID=... GOOGLE_CLIENT_SECRET=... GOOGLE_ALLOWED_DOMAIN=example.com \
npm run dev -w @wikijs-mcp/server

发布

发布是自动的。在 dev 分支上修改根目录 package.json 中的 version,创建一个 devmain 的 PR 并合并即可。随后,Release on main 工作流会在推送到 main 时构建并推送 ghcr.io/<owner>/wikijs-mcp-server:vX.Y.Z(以及 :latest),同时创建 git 标签 vX.Y.Z 和 GitHub Release——全部在这一次运行中完成,只需使用内置的 GITHUB_TOKEN(无需配置 PAT/secret)。如果版本号没有变化,该次运行就是 no-op,因此普通合并到 main 不会产生 release。

要让这生效,需要一次性仓库设置:Settings → Actions → General → 工作流权限 = 读取和写入权限;如果你用规则集(ruleset)保护标签,还要允许 GitHub Actions 创建 v* 标签。

安全说明

  • 断言(Assertion):使用 RS256、TTL 60 秒、唯一的 jti,并提供防重放保护;私钥只存在于 MCP 服务器上。私钥一旦泄露,就意味着能冒充任意 wiki 用户登录——请将其视为根密钥(root secret)并轮换(生成新的密钥对,并更新对应 strategy 中的公钥)。

  • Google 身份:规范标识符是 iss+sub;邮箱只用于查找。hd 域名是从签名的 id_token 中验证的,而不是从参数里取。

  • 混淆代理(confused deputy)防御:在释放授权码之前,用户必须经过一个针对该客户端的同意屏幕(requireConsent 只有在可信的第一方场景中才可以关闭)。这可以防止攻击者通过 DCR 注册自己的 OAuth 客户端后静默获取受害者的令牌。

  • Wiki.js 速率限制authentication.login 为每个 IP 每分钟 5 次,而所有委托登录都来自 MCP 服务器的 IP。broker 会缓存 JWT(默认 30 分钟),并在限流时“尝试等待并重试”,因此在正常运行中无感知;但在大规模接入期间,最多可能出现大约一分钟的延迟。

  • 撤销:标准 OAuth /revoke(按 token 撤销);在 Wiki.js 中停用用户后,下次 JWT 刷新时(≤30 分钟)委托会被中断;删除 SESSION_STORE_FILE 并重启 MCP 服务器,即可一次性清除所有会话。

  • 审计:工具调用会记录为结构化日志(谁、调用了哪个工具、ok/denied),且不包含页面内容。

  • MCP 端点:仅 Bearer 认证,每个 token 120 次请求/分钟,带安全响应头;OAuth 端点受 SDK 内置的速率限制保护。

限制与计划

  • RAG/语义搜索是一个独立的未来服务。接入点已就绪:search_wiki 通过 SearchBackend 接口工作(src/search/ — v1 是原生 Wiki.js 搜索;RAG 服务将接收用户的 Wiki.js JWT 并保留 ACL 模型)。参见 docs/rag-integration.md

  • 当前是单实例 MCP 服务器(FileStore + 内存重放缓存)。高可用需要共享存储(Redis)——对应接口 KVStore 已经拆好。

  • Wiki.js 3.x 的认证机制不同——本模块以 2.5.x 为目标。

许可证

Apache-2.0

A
license - permissive license
Not graded
quality - not tested
A
maintenance

Maintenance

Maintainers
Response time
Release cycle
1Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • MCP-native open-source Notion alternative: read & write pages, databases and kanban boards.

  • Confluence MCP — wraps the Confluence Cloud REST API v2 (OAuth)

  • Google Docs MCP Pack — read, create, and edit Google Docs via OAuth.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MaxPopov/wikijs-mcp-google-auth'

If you have feedback or need assistance with the MCP directory API, please join our Discord server