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:
@@ -26,11 +26,18 @@ import type { Env } from "../types.js";
|
|||||||
import { kbdbFetch } from "../lib/kbdb-client.js";
|
import { kbdbFetch } from "../lib/kbdb-client.js";
|
||||||
import { errorResponse, successResponse } from "../lib/cypher-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(鐵律)。
|
||||||
|
*
|
||||||
|
* D28(owner-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);
|
registerCreateTemplate(server, env);
|
||||||
registerListTemplates(server, env);
|
registerListTemplates(server, env);
|
||||||
registerCreateRecord(server, env);
|
registerCreateRecord(server, env, orgNamespace);
|
||||||
registerGetRecord(server, env);
|
registerGetRecord(server, env);
|
||||||
registerQuery(server, env);
|
registerQuery(server, env);
|
||||||
registerSearch(server, env);
|
registerSearch(server, env);
|
||||||
@@ -92,8 +99,16 @@ export function registerListTemplates(server: McpServer, env: Env) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** kbdb_create_record — 依某 template 填一筆 record(slot → 內容)。 */
|
/**
|
||||||
export function registerCreateRecord(server: McpServer, env: Env) {
|
* kbdb_create_record — 依某 template 填一筆 record(slot → 內容)。
|
||||||
|
*
|
||||||
|
* D28(owner-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(
|
server.tool(
|
||||||
"kbdb_create_record",
|
"kbdb_create_record",
|
||||||
"依某 template 填一筆 record(一列資料)。values 是 {slot名: 內容},slot 名要對得上 template 的 slots。" +
|
"依某 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"),
|
template: z.string().min(1).describe("template 的 name 或 id"),
|
||||||
values: z.record(z.string()).describe("欄位內容 {slot名: 字串內容},如 {name:'Leo', email:'leo@x.com'}"),
|
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 }) => {
|
async ({ template, values, owner_id }) => {
|
||||||
try {
|
try {
|
||||||
|
// 機制注入:AI 傳的 owner_id 覆寫,缺則用 orgNamespace。兩者皆空 → 拒送(避免必然 400)。
|
||||||
|
const resolvedOwner = (owner_id && owner_id.trim()) || orgNamespace;
|
||||||
|
if (!resolvedOwner) {
|
||||||
|
return errorResponse(
|
||||||
|
"owner_missing",
|
||||||
|
"無法決定 owner_id:AI 未傳 owner_id 且 org namespace 為空",
|
||||||
|
["確認 MCP 連線帶了有效的 Authorization(org namespace)", "或明確傳入 owner_id"],
|
||||||
|
);
|
||||||
|
}
|
||||||
const res = await kbdbFetch(env, "/records", {
|
const res = await kbdbFetch(env, "/records", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json" },
|
headers: { "Content-Type": "application/json" },
|
||||||
body: JSON.stringify({ template, values, owner_id }),
|
body: JSON.stringify({ template, values, owner_id: resolvedOwner }),
|
||||||
});
|
});
|
||||||
if (!res.ok) {
|
if (!res.ok) {
|
||||||
return errorResponse("create_record_failed", `填 record 失敗`, [
|
return errorResponse("create_record_failed", `填 record 失敗`, [
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ export function registerAllTools(server: McpServer, env: Env, orgNamespace: stri
|
|||||||
registerAllRecipeTools(server, env);
|
registerAllRecipeTools(server, env);
|
||||||
// kbdb-base Phase 9.1: KBDB 資料層薄殼(template/record/query/search,HANDOFF §2)
|
// kbdb-base Phase 9.1: KBDB 資料層薄殼(template/record/query/search,HANDOFF §2)
|
||||||
// 鐵律:不提供建表/SQL tool,AI 只有 template+slot 可用(類 Supabase 萬用表)
|
// 鐵律:不提供建表/SQL tool,AI 只有 template+slot 可用(類 Supabase 萬用表)
|
||||||
registerAllKbdbDataTools(server, env);
|
// D28: 傳 orgNamespace,寫入 record 時機制注入 owner_id(base owner-mandatory,AI 漏不了)
|
||||||
|
registerAllKbdbDataTools(server, env, orgNamespace);
|
||||||
// §7.8 P1 D2: whoami(與 CLI acr whoami 對齊,AI 不繞 CLI 自己 curl 猜帳號)
|
// §7.8 P1 D2: whoami(與 CLI acr whoami 對齊,AI 不繞 CLI 自己 curl 猜帳號)
|
||||||
registerWhoami(server, env, orgNamespace);
|
registerWhoami(server, env, orgNamespace);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user