MCP kbdb_create_record 注入 org_namespace 為 owner_id,配合 base owner-mandatory

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ck8bjuBTLFkiFxqLGHMpqG
This commit is contained in:
Claude
2026-07-05 11:19:47 +00:00
parent 7d5cd06ff5
commit 159d0f0fb6
2 changed files with 33 additions and 8 deletions
+31 -7
View File
@@ -26,11 +26,18 @@ import type { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
import { errorResponse, successResponse } from "../lib/cypher-client.js";
/** 註冊全部 KBDB 資料層工具(kbdb-base Phase 9.1)。不含建表/SQL tool(鐵律)。 */
export function registerAllKbdbDataTools(server: McpServer, env: Env) {
/**
* 註冊全部 KBDB 資料層工具(kbdb-base Phase 9.1)。不含建表/SQL tool(鐵律)。
*
* D28owner-mandatory):base 的 `POST /records` 已把 owner_id 設必填(缺 → 400)。
* MCP 是薄殼、是 caller——owner **由機制注入**org namespace),不靠 AI 記得帶。
* 仿 arcrun_report_feedback:把 orgNamespace 傳進寫入 tool,寫入時當 owner_id。
* 送到 base 的 owner_id 一定非空 → AI 漏不了。
*/
export function registerAllKbdbDataTools(server: McpServer, env: Env, orgNamespace: string) {
registerCreateTemplate(server, env);
registerListTemplates(server, env);
registerCreateRecord(server, env);
registerCreateRecord(server, env, orgNamespace);
registerGetRecord(server, env);
registerQuery(server, env);
registerSearch(server, env);
@@ -92,8 +99,16 @@ export function registerListTemplates(server: McpServer, env: Env) {
);
}
/** kbdb_create_record — 依某 template 填一筆 recordslot → 內容)。 */
export function registerCreateRecord(server: McpServer, env: Env) {
/**
* kbdb_create_record — 依某 template 填一筆 recordslot → 內容)。
*
* D28owner-mandatory):base 的 `POST /records` owner_id 必填(缺 → 400)。
* owner_id 由 MCP **機制注入**orgNamespace),不靠 AI 帶:
* - AI 傳的 owner_id 當「選填覆寫」(想歸到別的租戶/專案時才用)。
* - AI 沒傳 → 用注入的 orgNamespace。
* - 兩者皆空 → 直接回錯(不送必然 400 的請求),保證送到 base 的 owner_id 一定非空。
*/
export function registerCreateRecord(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"kbdb_create_record",
"依某 template 填一筆 record(一列資料)。values 是 {slot名: 內容}slot 名要對得上 template 的 slots。" +
@@ -101,14 +116,23 @@ export function registerCreateRecord(server: McpServer, env: Env) {
{
template: z.string().min(1).describe("template 的 name 或 id"),
values: z.record(z.string()).describe("欄位內容 {slot名: 字串內容},如 {name:'Leo', email:'leo@x.com'}"),
owner_id: z.string().optional().describe("資料歸屬標記(選填,如專案 id / 用戶 id"),
owner_id: z.string().optional().describe("資料歸屬標記(選填;預設用你的 org namespace,想歸到別的租戶/專案才傳"),
},
async ({ template, values, owner_id }) => {
try {
// 機制注入:AI 傳的 owner_id 覆寫,缺則用 orgNamespace。兩者皆空 → 拒送(避免必然 400)。
const resolvedOwner = (owner_id && owner_id.trim()) || orgNamespace;
if (!resolvedOwner) {
return errorResponse(
"owner_missing",
"無法決定 owner_idAI 未傳 owner_id 且 org namespace 為空",
["確認 MCP 連線帶了有效的 Authorizationorg namespace", "或明確傳入 owner_id"],
);
}
const res = await kbdbFetch(env, "/records", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ template, values, owner_id }),
body: JSON.stringify({ template, values, owner_id: resolvedOwner }),
});
if (!res.ok) {
return errorResponse("create_record_failed", `填 record 失敗`, [
+2 -1
View File
@@ -55,7 +55,8 @@ export function registerAllTools(server: McpServer, env: Env, orgNamespace: stri
registerAllRecipeTools(server, env);
// kbdb-base Phase 9.1: KBDB 資料層薄殼(template/record/query/searchHANDOFF §2
// 鐵律:不提供建表/SQL toolAI 只有 template+slot 可用(類 Supabase 萬用表)
registerAllKbdbDataTools(server, env);
// D28: 傳 orgNamespace,寫入 record 時機制注入 owner_idbase owner-mandatoryAI 漏不了)
registerAllKbdbDataTools(server, env, orgNamespace);
// §7.8 P1 D2: whoami(與 CLI acr whoami 對齊,AI 不繞 CLI 自己 curl 猜帳號)
registerWhoami(server, env, orgNamespace);
}