forked from EduCraft/curriculum-project-hub
feat(hub): built-in PBank 题库 capability + role tools (v0.0.42)
Register pbank as an ADR-0027 external capability with org-scoped username/password envelopes, readiness via /login, and in-process cph_hub MCP tools (search/get/get_many) that materialize sources under the run workspace. Extend the capability secret payload for docmind vs pbank kinds, admin capabilities UI, role tool umbrella `pbank`, and the pbank-problem-report skill. Credentials never reach the Agent process.
This commit is contained in:
+51
-38
@@ -15,6 +15,9 @@ export const CPH_HUB_MCP_TOOL_IDS = [
|
||||
"feishu_download_resource",
|
||||
"request_approval",
|
||||
"convert_pdf_to_md",
|
||||
"pbank_search_problems",
|
||||
"pbank_get_problem",
|
||||
"pbank_get_many_problems",
|
||||
] as const;
|
||||
|
||||
export type CphHubMcpToolId = (typeof CPH_HUB_MCP_TOOL_IDS)[number];
|
||||
@@ -24,44 +27,51 @@ export interface ClaudeSdkToolConfig {
|
||||
readonly allowedTools: readonly string[];
|
||||
}
|
||||
|
||||
const ROLE_TOOL_TO_CLAUDE_BUILT_INS = new Map<string, readonly string[]>([
|
||||
["read_file", ["Read"]],
|
||||
["write_file", ["Write"]],
|
||||
["list_files", ["Glob"]],
|
||||
["search_files", ["Grep"]],
|
||||
["bash", ["Bash"]],
|
||||
const ROLE_TOOL_TO_CLAUDE_BUILT_INS: Readonly<Record<string, readonly string[]>> = {
|
||||
read_file: ["Read"],
|
||||
write_file: ["Write"],
|
||||
list_files: ["Glob"],
|
||||
search_files: ["Grep"],
|
||||
bash: ["Bash"],
|
||||
// ADR-0017 replaced cph custom tools with Bash commands. Granting either
|
||||
// cph role tool therefore exposes the SDK Bash tool; cph-only Bash narrowing
|
||||
// would need a separate command-policy layer.
|
||||
["cph_check", ["Bash"]],
|
||||
["cph_build", ["Bash"]],
|
||||
["web_fetch", ["WebFetch"]],
|
||||
["web_search", ["WebSearch"]],
|
||||
["Read", ["Read"]],
|
||||
["Write", ["Write"]],
|
||||
["Bash", ["Bash"]],
|
||||
["Glob", ["Glob"]],
|
||||
["Grep", ["Grep"]],
|
||||
["WebFetch", ["WebFetch"]],
|
||||
["WebSearch", ["WebSearch"]],
|
||||
]);
|
||||
cph_check: ["Bash"],
|
||||
cph_build: ["Bash"],
|
||||
web_fetch: ["WebFetch"],
|
||||
web_search: ["WebSearch"],
|
||||
Read: ["Read"],
|
||||
Write: ["Write"],
|
||||
Bash: ["Bash"],
|
||||
Glob: ["Glob"],
|
||||
Grep: ["Grep"],
|
||||
WebFetch: ["WebFetch"],
|
||||
WebSearch: ["WebSearch"],
|
||||
};
|
||||
|
||||
const ROLE_TOOL_TO_CPH_HUB_MCP_TOOL = new Map<string, CphHubMcpToolId>([
|
||||
["send_file", "send_file"],
|
||||
["feishu_read_context", "feishu_read_context"],
|
||||
["feishu_download_resource", "feishu_download_resource"],
|
||||
["request_approval", "request_approval"],
|
||||
["convert_pdf_to_md", "convert_pdf_to_md"],
|
||||
["mcp__cph_hub__send_file", "send_file"],
|
||||
["mcp__cph_hub__feishu_read_context", "feishu_read_context"],
|
||||
["mcp__cph_hub__feishu_download_resource", "feishu_download_resource"],
|
||||
["mcp__cph_hub__request_approval", "request_approval"],
|
||||
["mcp__cph_hub__convert_pdf_to_md", "convert_pdf_to_md"],
|
||||
]);
|
||||
const ROLE_TOOL_TO_CPH_HUB_MCP_TOOLS: Readonly<Record<string, readonly CphHubMcpToolId[]>> = {
|
||||
send_file: ["send_file"],
|
||||
feishu_read_context: ["feishu_read_context"],
|
||||
feishu_download_resource: ["feishu_download_resource"],
|
||||
request_approval: ["request_approval"],
|
||||
convert_pdf_to_md: ["convert_pdf_to_md"],
|
||||
pbank: ["pbank_search_problems", "pbank_get_problem", "pbank_get_many_problems"],
|
||||
pbank_search_problems: ["pbank_search_problems"],
|
||||
pbank_get_problem: ["pbank_get_problem"],
|
||||
pbank_get_many_problems: ["pbank_get_many_problems"],
|
||||
"mcp__cph_hub__send_file": ["send_file"],
|
||||
"mcp__cph_hub__feishu_read_context": ["feishu_read_context"],
|
||||
"mcp__cph_hub__feishu_download_resource": ["feishu_download_resource"],
|
||||
"mcp__cph_hub__request_approval": ["request_approval"],
|
||||
"mcp__cph_hub__convert_pdf_to_md": ["convert_pdf_to_md"],
|
||||
"mcp__cph_hub__pbank_search_problems": ["pbank_search_problems"],
|
||||
"mcp__cph_hub__pbank_get_problem": ["pbank_get_problem"],
|
||||
"mcp__cph_hub__pbank_get_many_problems": ["pbank_get_many_problems"],
|
||||
};
|
||||
|
||||
const SUPPORTED_ROLE_TOOLS = new Set([
|
||||
...ROLE_TOOL_TO_CLAUDE_BUILT_INS.keys(),
|
||||
...ROLE_TOOL_TO_CPH_HUB_MCP_TOOL.keys(),
|
||||
...Object.keys(ROLE_TOOL_TO_CLAUDE_BUILT_INS),
|
||||
...Object.keys(ROLE_TOOL_TO_CPH_HUB_MCP_TOOLS),
|
||||
]);
|
||||
|
||||
export function claudeSdkToolConfigForRole(roleTools: readonly string[] | undefined): ClaudeSdkToolConfig {
|
||||
@@ -77,13 +87,12 @@ export function claudeSdkToolConfigForRole(roleTools: readonly string[] | undefi
|
||||
const allowedTools: string[] = [];
|
||||
for (const roleTool of roleTools) {
|
||||
assertSupportedRoleTool(roleTool);
|
||||
for (const tool of ROLE_TOOL_TO_CLAUDE_BUILT_INS.get(roleTool) ?? []) {
|
||||
for (const tool of ROLE_TOOL_TO_CLAUDE_BUILT_INS[roleTool] ?? []) {
|
||||
pushUnique(builtIns, tool);
|
||||
pushUnique(allowedTools, tool);
|
||||
}
|
||||
|
||||
const mcpTool = ROLE_TOOL_TO_CPH_HUB_MCP_TOOL.get(roleTool);
|
||||
if (mcpTool !== undefined) {
|
||||
for (const mcpTool of ROLE_TOOL_TO_CPH_HUB_MCP_TOOLS[roleTool] ?? []) {
|
||||
pushUnique(allowedTools, claudeMcpToolName(mcpTool));
|
||||
}
|
||||
}
|
||||
@@ -97,8 +106,9 @@ export function cphHubMcpToolsForRole(roleTools: readonly string[] | undefined):
|
||||
const tools: CphHubMcpToolId[] = [];
|
||||
for (const roleTool of roleTools) {
|
||||
assertSupportedRoleTool(roleTool);
|
||||
const mcpTool = ROLE_TOOL_TO_CPH_HUB_MCP_TOOL.get(roleTool);
|
||||
if (mcpTool !== undefined) pushUnique(tools, mcpTool);
|
||||
for (const mcpTool of ROLE_TOOL_TO_CPH_HUB_MCP_TOOLS[roleTool] ?? []) {
|
||||
pushUnique(tools, mcpTool);
|
||||
}
|
||||
}
|
||||
return tools;
|
||||
}
|
||||
@@ -108,7 +118,10 @@ export function roleToolsAllow(roleTools: readonly string[] | undefined, roleToo
|
||||
for (const configured of roleTools) {
|
||||
assertSupportedRoleTool(configured);
|
||||
if (configured === roleTool) return true;
|
||||
if (ROLE_TOOL_TO_CPH_HUB_MCP_TOOL.get(configured) === roleTool) return true;
|
||||
const mapped = ROLE_TOOL_TO_CPH_HUB_MCP_TOOLS[configured];
|
||||
if (mapped !== undefined && mapped.includes(roleTool as CphHubMcpToolId)) return true;
|
||||
// Umbrella: role tool "pbank" allows any pbank_* MCP or role tool.
|
||||
if (configured === "pbank" && roleTool.startsWith("pbank")) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user