10d150ac2b
leo 2026-08-12:「人類進 Portal 輸入帳密表示你是主人,可以查到你權限所有東西;
AI 透過輸入帳密的 MCP 查詢表示是授權的 AI,可以查到主人允許查的任何東西。」
「掛上 MCP 並輸入帳密,那個動作本身就是授權」⇒ 下游不得再要求第二次認證。
病根(不是金鑰沒同步,是身分沒接住):
oauth/routes.ts 驗完 Portal 帳密只留下 `loginOk = res.ok` 一個布林值,身分當場丟棄,
namespace 改從 `MCP_OWNER_NAMESPACE || "leo"` 拿。於是查詢時手上沒有身分可帶,
只好用 KBDB_INTERNAL_TOKEN 直打 KBDB——那條路繞過 portal 所有庫過濾,
而且不管誰登入都看到同一格、看到全部。CLI 也從不注入 MCP_OWNER_NAMESPACE,
所以那個 "leo" 預設值是每台實例的實際行為,不是理論上的邊角。
修法(走既有那條路,不發明新的):
1. 接住身分:/authorize 解析 /portal/login 回應,把 portal session token +
display_name/role/libraries 存進 authorization code → access token。
/portal/login 補回 session_expires_in,access_token TTL 夾成
min(自己的 TTL, portal session TTL)——不讓「MCP 還連著、底下 session 早死」。
cypher 回 200 但沒給 session_token(舊版)→ 不發碼,不簽一張沒有身分的 token。
2. 攜帶身分:kbdb_* 全部改走 cypher `/portal/data/*`,Authorization 帶登入者的
session。庫過濾/租戶注入/停用即時生效全在 server 側,與人類走 portal 網頁同一道閘。
kbdb_graph_neighbors 因此不再需要 kbdb_base(server 自己知道查哪個庫)。
藏書地圖(含連線時注入 instructions 的那份)同樣只回有權限的庫,快取改 per-session
分格——地圖本身就是情報,不能讓先連上的人把視野留給下一個。
3. fail-closed:舊 token 沒有身分 → 誠實要求重新連線,不偷偷退回服務金鑰那條老路。
服務級憑據(static token / partner key)維持既有 KBDB 直連,arcrun_* 零回歸。
新增 cypher portal 資料面端點(能力長在 API,MCP 只暴露;rule 07):
GET /portal/data/map、/portal/data/map/:library
GET /portal/data/templates、POST /portal/data/templates
GET /portal/data/records/by-template/:t、GET /portal/data/records/:id
POST /portal/data/records
全部:呼叫端自帶 owner_id 一律不生效;越權與不存在同回 404;寫入 owner_id 由 server 定死。
KBDB base:`GET /records/:id` 與 by-template 補回 owner_id 欄位——原本不回,
呼叫端無從判斷「這筆是不是我的」,按 id 直讀等於沒有租戶邊界。
沒動:KBDB fail-closed 閘、任何金鑰、租戶字串仍不下發給呼叫端。
驗證:
mcp tsc 綠;vitest 113/113 綠(改前 48 綠 29 紅)
cypher vitest 400 綠 / 14 紅,14 紅與 base commit a24f291 逐條相同(既有)
kbdb vitest 208 綠 / 5 紅,5 紅同為既有(migrations/*.sql 被 gitignore)
端到端 ◐ 未驗:需部署到 leo21c,那道閘要 leo 親手解(見 PR)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
75 lines
4.7 KiB
TypeScript
75 lines
4.7 KiB
TypeScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||
import { Env } from "../types.js";
|
||
import { registerSearchComponents } from "./arcrun_search_components.js";
|
||
import { registerSearchWorkflows } from "./arcrun_search_workflows.js";
|
||
import { registerListComponents } from "./arcrun_list_components.js";
|
||
import { registerGetComponent } from "./arcrun_get_component.js";
|
||
import { registerGetComponentGuide } from "./arcrun_get_component_guide.js";
|
||
import { registerCreateTag } from "./arcrun_create_tag.js";
|
||
import { registerListTags } from "./arcrun_list_tags.js";
|
||
import { registerDeleteTag } from "./arcrun_delete_tag.js";
|
||
import { registerTagResource } from "./arcrun_tag_resource.js";
|
||
import { registerUntagResource } from "./arcrun_untag_resource.js";
|
||
import { registerGetGuiContext } from "./arcrun_get_gui_context.js";
|
||
import { registerReportFeedback } from "./arcrun_report_feedback.js";
|
||
import { registerAllIntrospectionTools } from "./arcrun_introspection.js";
|
||
import { registerAllWorkflowCrudTools } from "./arcrun_workflow_crud.js";
|
||
import { registerAllSkillExampleTools } from "./arcrun_skills_examples.js";
|
||
import { registerAllRecipeTools } from "./arcrun_recipe.js";
|
||
import { registerAllKbdbDataTools } from "./kbdb_data.js";
|
||
import { registerAllKbdbGraphTools } from "./kbdb_graph.js";
|
||
import { registerAllKbdbMapTools } from "./kbdb_map.js";
|
||
import { registerWhoami } from "./arcrun_whoami.js";
|
||
import type { KnowledgeIdentity } from "../lib/portal-client.js";
|
||
|
||
export function registerAllTools(
|
||
server: McpServer,
|
||
env: Env,
|
||
orgNamespace: string,
|
||
partnerToken: string,
|
||
identity: KnowledgeIdentity,
|
||
) {
|
||
registerSearchComponents(server, env, orgNamespace);
|
||
// 🔴 2026-07-21 leo 拍板停用:零件走 PR、專業等級;recipe/workflow/app 誰都可以做。
|
||
// 零件貢獻**只有一條路=PR 人審**(leo 2026-08-01:「已經沒有 publish 了,
|
||
// 零件等級一律走 PR,這條路封了」;Arcrun#23 已關)。實作路徑已 git rm
|
||
// (arcrun_publish_component.ts),不留死代碼當錯誤環境信號。
|
||
// 想貢獻零件 → repo `Leo/arcrun-components` fork→PR→人審,見 CONTRIBUTING-components.md。
|
||
registerSearchWorkflows(server, env, orgNamespace, partnerToken); // workflow-discovery R2
|
||
registerListComponents(server, env, orgNamespace);
|
||
registerGetComponent(server, env, orgNamespace);
|
||
// registerGetComponentGuide(server, env, orgNamespace); // 同上,2026-07-21 停用
|
||
registerCreateTag(server, env, orgNamespace);
|
||
registerListTags(server, env, orgNamespace);
|
||
registerDeleteTag(server, env, orgNamespace);
|
||
registerTagResource(server, env, orgNamespace);
|
||
registerUntagResource(server, env, orgNamespace);
|
||
registerGetGuiContext(server, env, orgNamespace);
|
||
// LI SDD M1.3: explicit feedback tool(arcrun_* 命名規範)
|
||
registerReportFeedback(server, env, orgNamespace);
|
||
// LI SDD M2.2: introspection tools (validate / paused / trace / recent executions)
|
||
registerAllIntrospectionTools(server, env);
|
||
// LI SDD M2.2: workflow CRUD (push / list / get / delete / run)
|
||
// 取代早期壞掉的 deploy_workflow(打不存在的 /workflows/deploy endpoint),
|
||
// 以及重複的 list/get workflow(改直問 cypher-executor 真實狀態,不從 KBDB 撈 metadata)。
|
||
registerAllWorkflowCrudTools(server, env);
|
||
// LI SDD M3.2: skills + examples lookup(KBDB-backed)
|
||
// 走 sync-registry-to-kbdb.py 把 registry/{skills,examples} 同步進 KBDB
|
||
registerAllSkillExampleTools(server, env);
|
||
// kbdb-base §7.5.i: recipe 公庫/私庫工具(與 CLI 六能力對齊,rule 07 §5 MCP 不落後)
|
||
registerAllRecipeTools(server, env);
|
||
// kbdb-base Phase 9.1: KBDB 資料層薄殼(template/record/query/search,HANDOFF §2)
|
||
// 鐵律:不提供建表/SQL tool,AI 只有 template+slot 可用(類 Supabase 萬用表)
|
||
// 2026-08-12:知識面(kbdb_*)全部改吃 identity——以帳密連線者走 portal 資料面
|
||
// (權限=那個人的權限),服務級憑據維持既有 KBDB 直連。見 lib/portal-client.ts。
|
||
registerAllKbdbDataTools(server, env, identity);
|
||
// issue #68: KBDB graph 查詢薄殼(kbdb_graph_neighbors,調 /q/:ns/graph_neighbors 同步查詢端點)
|
||
// 補齊 D17「KBDB MCP=RAG 套餐」第三模式:關鍵字/語義之外的圖(關係遍歷)
|
||
registerAllKbdbGraphTools(server, env, orgNamespace, identity);
|
||
// library-map SDD M4(Arcrun#39): 藏書地圖薄殼(kbdb_get_map,調 kbdb GET /map//map/:library)
|
||
// retrieval 第一站:先看地圖定位庫,再 search/graph 進庫(design §6)
|
||
registerAllKbdbMapTools(server, env, identity);
|
||
// §7.8 P1 D2: whoami(與 CLI acr whoami 對齊,AI 不繞 CLI 自己 curl 猜帳號)
|
||
registerWhoami(server, env, orgNamespace, identity);
|
||
}
|