refactor(mcp): u6u_* tool 全面 rename 為 arcrun_* + 前綴收斂單一來源

零 u6u 洩漏(leo 要求 1):connector tool 清單只剩 arcrun_*,並移除壞掉/
重複的 u6u_ workflow tool(deploy/execute/get/list_workflows 由 arcrun_
push/run/get/list_workflows 取代);MCP server name 也由 u6u-mcp-server
改 arcrun-mcp-server。

前綴單一真相源(leo 要求 2):新增 mcp/src/brand.ts(TOOL_PREFIX + toolName()
helper),所有 server.tool() 註冊一律走 toolName("suffix"),前綴不再明碼散寫。
未來 rebrand 只改 brand.ts 一行。

- 12 個獨有能力 tool rename(component/tag/gui/search_workflows)
- 4 個重複/壞掉 tool 移除
- inter-tool hint、描述字串、註解內 u6u_ 名字全數更新
- tsc exit 0;vitest 19/19 綠

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015d5jDbuqT5Htwv3Q88XXKk
This commit is contained in:
Claude
2026-07-07 05:21:34 +00:00
parent befc63cfe0
commit 5fc7277c3c
26 changed files with 111 additions and 308 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
* thin-shell-alignment P1issue #11):原本直連 CF KV 讀 `workflow:` 前綴(CfKvClient),
* 有兩個漂移:① 前綴對不上(部署寫 `{apiKey}:wf:` → 永遠列不到新部署)② 薄殼直碰底層儲存
* self-hosted 用戶需 CF API token 才能 list)。改走 cypher `GET /webhooks/named`KV 源,與
* MCP u6u_list_workflows 同源),薄殼不再直連 KV、key 前綴 bug 自然消失。
* MCP arcrun_list_workflows 同源),薄殼不再直連 KV、key 前綴 bug 自然消失。
*/
import chalk from 'chalk';
import ora from 'ora';
+25
View File
@@ -0,0 +1,25 @@
/**
* brand.ts — MCP tool 名前綴的單一真相源(single source of truth)。
*
* 為什麼存在(leo 硬要求 2):
* 歷史上 tool 名前綴(早期 "u6u_"、對外改名後應為 "arcrun_")被明碼散寫在
* ~39 個 tool 檔裡,每次 rebrand 都得逐檔手改、極易漏 → u6u 洩漏給用戶。
* 收斂成這一個常數後,未來再 rebrand「只改這一行」。
*
* 用法:
* import { toolName } from "../brand.js";
* server.tool(toolName("push_workflow"), ...) // → "arcrun_push_workflow"
*
* 語意後綴(push_workflow / list_tags …)保留可讀,只有「品牌前綴」被變數化。
*
* 可用環境變數 ARCRUN_TOOL_PREFIX 覆蓋(測試 / 未來 rebrand 過渡用),預設 "arcrun"。
* Workers runtime 無 process 全域,故用 globalThis 安全取值,取不到就 fallback。)
*/
const envPrefix = (globalThis as { process?: { env?: Record<string, string | undefined> } })
.process?.env?.ARCRUN_TOOL_PREFIX;
export const TOOL_PREFIX = envPrefix || "arcrun";
/** 把語意後綴組成完整 tool 名,例:toolName("whoami") → "arcrun_whoami"。 */
export const toolName = (suffix: string): string => `${TOOL_PREFIX}_${suffix}`;
+1 -1
View File
@@ -10,7 +10,7 @@ export async function handleMcpRequest(
partnerToken: string,
): Promise<Response> {
const transport = new WebStandardStreamableHTTPServerTransport({ sessionIdGenerator: undefined });
const server = new McpServer({ name: "u6u-mcp-server", version: "1.0.0" });
const server = new McpServer({ name: "arcrun-mcp-server", version: "1.0.0" });
registerAllTools(server, env, orgNamespace, partnerToken);
await server.connect(transport);
@@ -1,11 +1,12 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
export function registerCreateTag(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_create_tag",
toolName("create_tag"),
"在當前命名空間下建立新的 tag,用於分類工作流與零件。",
{
name: z.string().describe("Tag 名稱(在當前 org_namespace 下唯一)"),
@@ -1,11 +1,12 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
export function registerDeleteTag(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_delete_tag",
toolName("delete_tag"),
"刪除當前命名空間下的指定 tag。",
{ tag_name: z.string().describe("要刪除的 Tag 名稱") },
async ({ tag_name }) => {
@@ -1,14 +1,15 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
/**
* u6u_get_component
* arcrun_get_component
* Component Registry GET /components/:id
*/
export function registerGetComponent(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_get_component",
toolName("get_component"),
"取得指定零件的完整合約,包含 canonical_id、display_name、category、version、stability、input_schema、output_schema、gherkin_tests、評分統計等。",
{
canonical_id: z.string().describe("零件 canonical_id(如 validate_json"),
@@ -29,7 +30,7 @@ export function registerGetComponent(server: McpServer, env: Env, orgNamespace:
if (response.status === 404) {
return {
content: [{ type: "text", text: `零件 '${canonical_id}' 不存在。可用 u6u_search_components 搜尋相似零件。` }],
content: [{ type: "text", text: `零件 '${canonical_id}' 不存在。可用 arcrun_search_components 搜尋相似零件。` }],
isError: true,
};
}
@@ -1,15 +1,16 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { Env } from "../types.js";
/**
* u6u_get_component_guide
* arcrun_get_component_guide
* Component Registry GET /components/guide
* AI
*/
export function registerGetComponentGuide(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_get_component_guide",
"取得 u6u 零件開發指引(Markdown 格式)。包含 TinyGo 白名單 import、禁止行為、component.contract.yaml 完整範例、本地測試指令。開發新零件前必須先讀取此指引。",
toolName("get_component_guide"),
"取得 arcrun 零件開發指引(Markdown 格式)。包含 TinyGo 白名單 import、禁止行為、component.contract.yaml 完整範例、本地測試指令。開發新零件前必須先讀取此指引。",
{},
async () => {
try {
@@ -1,4 +1,5 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
@@ -17,7 +18,7 @@ interface ActionLogRecord {
export function registerGetGuiContext(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_get_gui_context",
toolName("get_gui_context"),
"查詢用戶在 GUI 上的最近操作記錄,了解用戶的當前意圖與操作上下文。" +
"回傳最近 N 條操作記錄(從新到舊),以及用戶當前所在頁面和正在編輯的 Workflow ID。",
{
+5 -4
View File
@@ -11,6 +11,7 @@
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import type { Env } from "../types.js";
import { cypherFetch, errorResponse, successResponse } from "../lib/cypher-client.js";
@@ -20,7 +21,7 @@ const apiKeyDesc =
export function registerValidateYaml(server: McpServer, env: Env) {
server.tool(
"arcrun_validate_yaml",
toolName("validate_yaml"),
"Dry-run YAML 校驗。不部署、無 side effect。回 {valid, errors?, nodeCount, edgeCount}。**永遠先 call 此 tool 再 push_workflow**,避免反覆 deploy 失敗。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -73,7 +74,7 @@ export function registerValidateYaml(server: McpServer, env: Env) {
export function registerListPausedExecutions(server: McpServer, env: Env) {
server.tool(
"arcrun_list_paused_executions",
toolName("list_paused_executions"),
"列當前 api_key 下所有 paused workflow(等 daemon callback resume 的)。給 debug 用:claude_api 等 async 零件會把 workflow 暫停,此 tool 告訴你哪些還沒回來。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -108,7 +109,7 @@ export function registerListPausedExecutions(server: McpServer, env: Env) {
export function registerGetExecutionTrace(server: McpServer, env: Env) {
server.tool(
"arcrun_get_execution_trace",
toolName("get_execution_trace"),
"看單一 paused workflow 的 state 細節(trace、graph、context、pending_result)。task_id 從 paused 錯誤訊息或 list_paused_executions 取得。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -156,7 +157,7 @@ export function registerGetExecutionTrace(server: McpServer, env: Env) {
export function registerListRecentExecutions(server: McpServer, env: Env) {
server.tool(
"arcrun_list_recent_executions",
toolName("list_recent_executions"),
"列某 workflow 最近 N 次執行 verdict(成功 / 失敗 / duration)。資料來源是 ANALYTICS_KV 90 天保留期。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -1,11 +1,12 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
export function registerListComponents(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_list_components",
toolName("list_components"),
"列出當前命名空間下所有已發佈的零件,可選擇按 tag 篩選。",
{ tag: z.string().optional().describe("按 tag 名稱篩選(選填)") },
async ({ tag }) => {
@@ -1,10 +1,11 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
export function registerListTags(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_list_tags",
toolName("list_tags"),
"列出當前命名空間下所有的 tag。",
{},
async () => {
@@ -1,20 +1,21 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
/**
* u6u_publish_component TinyGo WASM Component Registry
* arcrun_publish_component TinyGo WASM Component Registry
*
* AI
* 1. u6u_get_component_guide
* 1. arcrun_get_component_guide
* 2. TinyGo stdin/stdout JSON I/O
* 3. .wasmbase64
* 4. Registry syscall Gherkin
*/
export function registerPublishComponent(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_publish_component",
"提交 TinyGo WASM 零件至 Component Registry。需提供 component.contract.yaml 內容與編譯後的 .wasm base64。提交前請先呼叫 u6u_get_component_guide 取得開發規範。",
toolName("publish_component"),
"提交 TinyGo WASM 零件至 Component Registry。需提供 component.contract.yaml 內容與編譯後的 .wasm base64。提交前請先呼叫 arcrun_get_component_guide 取得開發規範。",
{
contract: z.object({
canonical_id: z.string().describe("零件功能名稱(小寫底線,如 validate_json"),
+7 -6
View File
@@ -11,6 +11,7 @@
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import type { Env } from "../types.js";
import { cypherFetch, errorResponse, successResponse } from "../lib/cypher-client.js";
@@ -30,7 +31,7 @@ export function registerAllRecipeTools(server: McpServer, env: Env) {
/** arcrun_recipe_search — 搜尋公庫 recipe(同名可多作者,附市場數據)。落空回創作引導。 */
export function registerRecipeSearch(server: McpServer, env: Env) {
server.tool(
"arcrun_recipe_search",
toolName("recipe_search"),
"搜尋公庫 recipeAPI 整合配方)。同 canonical_id 可有多作者版本,各附市場數據(成功/失敗次數),依數據選最佳。找不到時會提示可自己做一個 recipe 投稿成為作者。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -58,7 +59,7 @@ export function registerRecipeSearch(server: McpServer, env: Env) {
/** arcrun_recipe_pull — 從公庫取一份 recipe 寫進自己私庫。 */
export function registerRecipePull(server: McpServer, env: Env) {
server.tool(
"arcrun_recipe_pull",
toolName("recipe_pull"),
"從公庫取一份 recipe 寫進自己私庫(按需取用,非全量同步)。不指定 author 取市場最佳版本。取回後可在 workflow 用 component: <canonical_id>。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -108,7 +109,7 @@ export function registerRecipePull(server: McpServer, env: Env) {
/** arcrun_recipe_submit_p — 把私庫某 recipe 投稿到公庫(新增作者版本)。 */
export function registerRecipeSubmitP(server: McpServer, env: Env) {
server.tool(
"arcrun_recipe_submit_p",
toolName("recipe_submit_p"),
"把私庫某 recipe 投稿到公庫(app-store 模型:新增一個作者版本,不覆蓋別人的)。投稿 = 把 recipe 暴露給全網,需帶 exposure_consent 明示同意。別人能搜到並 pull,市場數據決定它被不被選用。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -160,7 +161,7 @@ export function registerRecipeSubmitP(server: McpServer, env: Env) {
/** arcrun_recipe_push — 上傳/更新私庫 recipe(就地更新自己的版本)。 */
export function registerRecipePush(server: McpServer, env: Env) {
server.tool(
"arcrun_recipe_push",
toolName("recipe_push"),
"上傳一份 recipe 到自己私庫(或就地更新自己既有版本)。recipe = 「http_request + 參數模板」的具名封裝,不需 deploy Worker。要投稿到公庫用 arcrun_recipe_submit_p。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -192,7 +193,7 @@ export function registerRecipePush(server: McpServer, env: Env) {
/** arcrun_recipe_list — 列出自己私庫的 recipe。 */
export function registerRecipeList(server: McpServer, env: Env) {
server.tool(
"arcrun_recipe_list",
toolName("recipe_list"),
"列出自己私庫(本部署)的 recipe。要找公庫的用 arcrun_recipe_search。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -213,7 +214,7 @@ export function registerRecipeList(server: McpServer, env: Env) {
/** arcrun_recipe_delete — 刪除私庫某 recipe。 */
export function registerRecipeDelete(server: McpServer, env: Env) {
server.tool(
"arcrun_recipe_delete",
toolName("recipe_delete"),
"刪除自己私庫某 recipecanonical_id / rec_hash / uuid)。不影響公庫別人的版本。",
{
api_key: z.string().describe(apiKeyDesc),
+3 -3
View File
@@ -8,11 +8,11 @@
*
* 後續 M4 weekly_review workflow 聚合這些 block 產出 arcrun-roadmap。
*
* 命名注意:M5 全面 rename u6u → arcrun 前,本 tool 直接用新名 arcrun_ prefix
* 立下範例。其他 u6u_* tool 等 M5 一次切。
* tool 名前綴走 brand.ts 的 toolName() helpersingle source of truth)。
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
@@ -28,7 +28,7 @@ const ISSUE_TYPES = [
export function registerReportFeedback(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"arcrun_report_feedback",
toolName("report_feedback"),
"AI agent 完成 workflow 任務 / 卡住 / 解掉問題後 **必須** call 此 tool 回報。即使順利也要 call (issue_type=success_story),那是告訴平台「這 pattern 已 work,可推廣」。回饋會寫進 KBDB type=agent-feedback,週報自動聚合產出平台改善 roadmap。",
{
issue_type: z.enum(ISSUE_TYPES).describe(
@@ -1,14 +1,15 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
/**
* u6u_search_components
* arcrun_search_components
* Component Registry GET /components/search?q=...
*/
export function registerSearchComponents(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_search_components",
toolName("search_components"),
"用自然語言語意搜尋零件庫,找出符合需求的零件。例如:「查詢 Google Sheets 資料」、「發送 LINE 訊息」、「驗證 JSON 格式」。回傳零件清單含 canonical_id、描述、評分。",
{
query: z.string().describe("自然語言搜尋詞,如「查詢 Google Sheets 資料」"),
@@ -43,7 +44,7 @@ export function registerSearchComponents(server: McpServer, env: Env, orgNamespa
return {
content: [{
type: "text",
text: `找不到符合「${query}」的零件。可以用 u6u_publish_component 提交新零件。`,
text: `找不到符合「${query}」的零件。可以用 arcrun_publish_component 提交新零件。`,
}],
};
}
@@ -1,15 +1,16 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
/**
* u6u_search_workflows workflow-discovery R2
* arcrun_search_workflows workflow-discovery R2
*
* AI
* cypher GET /workflows/search KBDB /entries/searchentry_type=workflow +
* KBDB Vectorize + capability_hint
*
* rule 07 + + u6u_search_components
* rule 07 + + arcrun_search_components
* flag AI call /
*/
export function registerSearchWorkflows(
@@ -19,7 +20,7 @@ export function registerSearchWorkflows(
partnerToken: string,
) {
server.tool(
"u6u_search_workflows",
toolName("search_workflows"),
"用自然語言找現成的工作流(先查有沒有現成的能做這件事,找到就用,別重造)。例如:「把資料寫進 Google Sheets」、「每天抓 RSS 發通知」、「webhook 轉發到別的 API」。回傳本帳號下符合的工作流清單。",
{
query: z.string().describe("自然語言描述要找的工作流,如「把資料寫進 Google Sheets」"),
@@ -60,7 +61,7 @@ export function registerSearchWorkflows(
return {
content: [{
type: "text",
text: `找不到符合「${query}」的現成工作流。可以用 u6u_deploy_workflow 部署一個新的。${hint}`,
text: `找不到符合「${query}」的現成工作流。可以用 arcrun_push_workflow 部署一個新的。${hint}`,
}],
};
}
+6 -5
View File
@@ -25,6 +25,7 @@
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import type { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
@@ -68,7 +69,7 @@ function parseTags(tagsJson?: string): string[] {
export function registerListSkills(server: McpServer, env: Env) {
server.tool(
"arcrun_list_skills",
toolName("list_skills"),
"列所有 agent-skill blocks(從 arcrun/registry/skills/ 同步進 KBDB)。每個 skill 是個 markdown playbook,描述 AI 面對 X 問題該怎麼想 + 該用哪個 example。回 [{slug, title, tags}]。call get_skill(slug) 拿完整內文。",
{
tag: z.string().optional().describe("optional 標籤過濾。如 'rag' / 'watcher' / 'debug'"),
@@ -115,7 +116,7 @@ export function registerListSkills(server: McpServer, env: Env) {
export function registerGetSkill(server: McpServer, env: Env) {
server.tool(
"arcrun_get_skill",
toolName("get_skill"),
"拿單一 agent-skill 完整 markdown playbook。slug 從 list_skills 取得。",
{
slug: z.string().describe("skill slug,例如 'build_watcher_workflow' / 'rag_with_arcrun'"),
@@ -153,7 +154,7 @@ export function registerGetSkill(server: McpServer, env: Env) {
export function registerListExamples(server: McpServer, env: Env) {
server.tool(
"arcrun_list_examples",
toolName("list_examples"),
"列所有 workflow-example blocks(從 arcrun/registry/examples/ 同步進 KBDB)。每個 example 是可直接 push 的 workflow YAML 範本 + description。回 [{slug, tags}]。call get_example / search_examples 拿細節。",
{
tag: z.string().optional().describe("optional 標籤過濾。如 'rag' / 'cron' / 'llm' / 'webhook'"),
@@ -194,7 +195,7 @@ export function registerListExamples(server: McpServer, env: Env) {
export function registerGetExample(server: McpServer, env: Env) {
server.tool(
"arcrun_get_example",
toolName("get_example"),
"拿單一 workflow-example 完整 YAML + description。slug 從 list_examples / search_examples 取得。可直接拿 YAML 改成你自己的 → push。",
{
slug: z.string().describe("example slug,例如 'rag-search-answer' / 'cron-watcher'"),
@@ -243,7 +244,7 @@ export function registerGetExample(server: McpServer, env: Env) {
export function registerSearchExamples(server: McpServer, env: Env) {
server.tool(
"arcrun_search_examples",
toolName("search_examples"),
"用 use case 關鍵字搜 workflow examples,回最相關 N 個。" +
"注意:基本盤目前是 D1 LIKE 關鍵字搜尋(非語義 embedding;語義是 kbdb-base Phase 1 的 embed 模組,尚未上)。" +
"→ 用具體詞('email'、'cron'、'rag')比整句自然語言命中率高。也會比對 slug/tag。",
@@ -1,11 +1,12 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
export function registerTagResource(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_tag_resource",
toolName("tag_resource"),
"為當前命名空間下的工作流或零件加上 tag 標籤。",
{
resource_type: z.enum(["workflow", "component"]).describe("資源類型:workflow 或 component"),
@@ -1,11 +1,12 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
export function registerUntagResource(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_untag_resource",
toolName("untag_resource"),
"移除當前命名空間下工作流或零件的 tag 標籤。",
{
resource_type: z.enum(["workflow", "component"]).describe("資源類型:workflow 或 component"),
+2 -1
View File
@@ -8,11 +8,12 @@
* MCP orgNamespace+ cypher binding
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { Env } from "../types.js";
export function registerWhoami(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"arcrun_whoami",
toolName("whoami"),
"回報這個 MCP 連線目前生效的身份:綁哪個帳號 / namespace、cypher 連向哪。" +
"部署 / 觸發 / 查 workflow 前先 call 此 tool 確認帳號,**不要自己 curl 猜帳號 URL**(會打到錯帳號)。",
{},
+10 -11
View File
@@ -3,9 +3,8 @@
*
* .agents/specs/llm-interface/ Milestone 2.2
*
* u6u_deploy_workflow /workflows/deploy endpoint
* tool+ u6u_list_workflows / u6u_get_workflow KBDB metadata
* cypher-executor
* deploy_workflow /workflows/deploy endpoint
* list/get workflow KBDB metadata cypher-executor
*
* tool cypher-executor /webhooks/named*
* - arcrun_push_workflow
@@ -14,11 +13,11 @@
* - arcrun_delete_workflow
* - arcrun_run_workflow
*
* u6u_* M5 rename + 退leo 2026-05-16
* AI tool arcrun_* u6u_* deprecation hint
* tool brand.ts toolName() helpersingle source of truth
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { toolName } from "../brand.js";
import { z } from "zod";
import type { Env } from "../types.js";
import { cypherFetch, errorResponse, successResponse } from "../lib/cypher-client.js";
@@ -34,8 +33,8 @@ const apiKeyDesc =
*/
export function registerPushWorkflow(server: McpServer, env: Env) {
server.tool(
"arcrun_push_workflow",
"部署 workflow 到 arcrun(取代 u6u_deploy_workflow,後者打不存在的 endpoint。輸入可以是 YAML 字串或 graph 物件。**建議先 call arcrun_validate_yaml 確認 schema**。",
toolName("push_workflow"),
"部署 workflow 到 arcrun。輸入可以是 YAML 字串或 graph 物件。**建議先 call arcrun_validate_yaml 確認 schema**。",
{
api_key: z.string().describe(apiKeyDesc),
yaml_content: z.string().optional().describe(
@@ -147,7 +146,7 @@ export function registerPushWorkflow(server: McpServer, env: Env) {
export function registerListWorkflows(server: McpServer, env: Env) {
server.tool(
"arcrun_list_workflows",
toolName("list_workflows"),
"列出你 (api_key 對應 namespace) 已部署的所有 workflow。回 [{name}]。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -185,7 +184,7 @@ export function registerListWorkflows(server: McpServer, env: Env) {
export function registerGetWorkflow(server: McpServer, env: Env) {
server.tool(
"arcrun_get_workflow",
toolName("get_workflow"),
"看單一 workflow 的完整定義(graph + config)。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -248,7 +247,7 @@ export function registerGetWorkflow(server: McpServer, env: Env) {
export function registerDeleteWorkflow(server: McpServer, env: Env) {
server.tool(
"arcrun_delete_workflow",
toolName("delete_workflow"),
"刪除 workflow。**不可逆,確認後再做**。會清掉對應 cron index 與 webhook URL。",
{
api_key: z.string().describe(apiKeyDesc),
@@ -292,7 +291,7 @@ export function registerDeleteWorkflow(server: McpServer, env: Env) {
export function registerRunWorkflow(server: McpServer, env: Env) {
server.tool(
"arcrun_run_workflow",
toolName("run_workflow"),
"觸發 workflow 執行。input 物件帶進 trigger context。回 {success, data, trace?, duration_ms}。",
{
api_key: z.string().describe(apiKeyDesc),
+15 -22
View File
@@ -1,21 +1,17 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { Env } from "../types.js";
import { registerSearchComponents } from "./u6u_search_components.js";
import { registerExecuteWorkflow } from "./u6u_execute_workflow.js";
import { registerDeployWorkflow } from "./u6u_deploy_workflow.js";
import { registerPublishComponent } from "./u6u_publish_component.js";
import { registerListWorkflows } from "./u6u_list_workflows.js";
import { registerGetWorkflow } from "./u6u_get_workflow.js";
import { registerSearchWorkflows } from "./u6u_search_workflows.js";
import { registerListComponents } from "./u6u_list_components.js";
import { registerGetComponent } from "./u6u_get_component.js";
import { registerGetComponentGuide } from "./u6u_get_component_guide.js";
import { registerCreateTag } from "./u6u_create_tag.js";
import { registerListTags } from "./u6u_list_tags.js";
import { registerDeleteTag } from "./u6u_delete_tag.js";
import { registerTagResource } from "./u6u_tag_resource.js";
import { registerUntagResource } from "./u6u_untag_resource.js";
import { registerGetGuiContext } from "./u6u_get_gui_context.js";
import { registerSearchComponents } from "./arcrun_search_components.js";
import { registerPublishComponent } from "./arcrun_publish_component.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";
@@ -26,11 +22,7 @@ import { registerWhoami } from "./arcrun_whoami.js";
export function registerAllTools(server: McpServer, env: Env, orgNamespace: string, partnerToken: string) {
registerSearchComponents(server, env, orgNamespace);
registerExecuteWorkflow(server, env, orgNamespace, partnerToken);
registerDeployWorkflow(server, env, orgNamespace);
registerPublishComponent(server, env, orgNamespace);
registerListWorkflows(server, env, orgNamespace, partnerToken); // thin-shell-alignment P1: list 改讀 /webhooks/named
registerGetWorkflow(server, env, orgNamespace);
registerSearchWorkflows(server, env, orgNamespace, partnerToken); // workflow-discovery R2
registerListComponents(server, env, orgNamespace);
registerGetComponent(server, env, orgNamespace);
@@ -41,12 +33,13 @@ export function registerAllTools(server: McpServer, env: Env, orgNamespace: stri
registerTagResource(server, env, orgNamespace);
registerUntagResource(server, env, orgNamespace);
registerGetGuiContext(server, env, orgNamespace);
// LI SDD M1.3: explicit feedback tool (新命名規範 arcrun_*)
// LI SDD M1.3: explicit feedback toolarcrun_* 命名規範)
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)
// 取代既有 u6u_deploy_workflow (打不存在的 /workflows/deploy endpoint)
// 取代早期壞掉的 deploy_workflow打不存在的 /workflows/deploy endpoint),
// 以及重複的 list/get workflow(改直問 cypher-executor 真實狀態,不從 KBDB 撈 metadata)。
registerAllWorkflowCrudTools(server, env);
// LI SDD M3.2: skills + examples lookupKBDB-backed
// 走 sync-registry-to-kbdb.py 把 registry/{skills,examples} 同步進 KBDB
-81
View File
@@ -1,81 +0,0 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
export function registerDeployWorkflow(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_deploy_workflow",
"將工作流 YAML 配置正式部署至雲端引擎,完成註冊與排程設定。",
{
yaml_content: z.string().describe("工作流的 YAML 配置內容")
},
async ({ yaml_content }) => {
try {
if (!env.CYPHER_EXECUTOR) {
return {
content: [{ type: "text", text: "Error: CYPHER_EXECUTOR service binding is not configured." }],
isError: true
};
}
const response = await env.CYPHER_EXECUTOR.fetch("http://cypher-executor/workflows/deploy", {
method: "POST",
headers: { "Content-Type": "application/yaml" },
body: yaml_content
});
if (!response.ok) {
const errorText = await response.text();
return {
content: [{ type: "text", text: `Deployment failed: ${errorText}` }],
isError: true
};
}
const result = await response.json<{ workflow_id?: string; [key: string]: unknown }>();
const workflowId = result.workflow_id ?? crypto.randomUUID();
// Parse workflow name from YAML
const nameMatch = yaml_content.match(/^name:\s*(.+)$/m);
const workflowName = nameMatch ? nameMatch[1].trim() : workflowId;
// Store workflow metadata in KBDB
if (env.KBDB) {
const kbdbResp = await kbdbFetch(env, "/records", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
template: "workflow_metadata",
record_id: `wf-${workflowId}`,
user_id: orgNamespace,
values: {
workflow_id: workflowId,
name: workflowName,
deployed_at: new Date().toISOString(),
org_namespace: orgNamespace
}
})
});
if (!kbdbResp.ok) {
const errText = await kbdbResp.text();
return {
content: [{ type: "text", text: `Deployment succeeded but failed to store metadata: ${errText}` }],
isError: true
};
}
}
return {
content: [{ type: "text", text: `Successfully deployed workflow: ${JSON.stringify(result, null, 2)}` }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Internal Error: ${error instanceof Error ? error.message : String(error)}` }],
isError: true
};
}
}
);
}
-54
View File
@@ -1,54 +0,0 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { Env } from "../types.js";
export function registerExecuteWorkflow(server: McpServer, env: Env, orgNamespace: string, partnerToken: string) {
server.tool(
"u6u_execute_workflow",
"在沙盒環境中即時執行工作流,驗證 triplets 邏輯是否正確。每個 config 鍵對應 triplets 中的節點名,內含 component(零件 canonical_id)、recipe(prompt_recipe:xxx,選用)、與該節點的其他靜態參數。",
{
triplets: z.array(z.string()).describe("工作流三元組,例:['input >> 完成後 >> synth']"),
context: z.record(z.string(), z.any()).describe("初始變數(測試資料 / 上游節點輸出模擬)"),
config: z.record(z.string(), z.record(z.string(), z.any())).optional().describe("每節點配置:{ node_name: { component, recipe?, ...params } }")
},
async ({ triplets, context, config }) => {
try {
if (!env.CYPHER_EXECUTOR) {
return {
content: [{ type: "text", text: "Error: CYPHER_EXECUTOR service binding is not configured." }],
isError: true
};
}
// KI-12 修:改打 /cypher/execute(吃 triplets+config),原 /execute 是吃完整 graph 的舊路徑
// KI-15 修:轉發 partner token 給 cypher-executor,讓 recipe expander 能用 ak_ key 抓 KBDB
const response = await env.CYPHER_EXECUTOR.fetch("http://cypher-executor/cypher/execute", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Arcrun-API-Key": partnerToken
},
body: JSON.stringify({ triplets, context, config })
});
if (!response.ok) {
const errorText = await response.text();
return {
content: [{ type: "text", text: `Execution failed: ${errorText}` }],
isError: true
};
}
const result = await response.json();
return {
content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `Internal Error: ${error instanceof Error ? error.message : String(error)}` }],
isError: true
};
}
}
);
}
-33
View File
@@ -1,33 +0,0 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
export function registerGetWorkflow(server: McpServer, env: Env, orgNamespace: string) {
server.tool(
"u6u_get_workflow",
"取得指定工作流的 metadata,包含名稱、部署時間與 tag 列表。",
{ workflow_id: z.string().describe("工作流 ID") },
async ({ workflow_id }) => {
try {
if (!env.KBDB) {
return { content: [{ type: "text", text: "Error: KBDB service binding unavailable" }], isError: true };
}
const resp = await kbdbFetch(env, `/records/wf-${encodeURIComponent(workflow_id)}`);
if (resp.status === 404) {
return { content: [{ type: "text", text: `Error: Workflow '${workflow_id}' not found` }], isError: true };
}
if (!resp.ok) {
return { content: [{ type: "text", text: `Error querying workflow: ${await resp.text()}` }], isError: true };
}
const record = await resp.json<{ slots: { workflow_id: string; name: string; deployed_at: string; org_namespace: string } }>();
if (record.slots.org_namespace !== orgNamespace) {
return { content: [{ type: "text", text: `Error: Workflow '${workflow_id}' not found` }], isError: true };
}
return { content: [{ type: "text", text: JSON.stringify(record.slots, null, 2) }] };
} catch (error) {
return { content: [{ type: "text", text: `Internal Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
}
}
);
}
-62
View File
@@ -1,62 +0,0 @@
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { Env } from "../types.js";
import { kbdbFetch } from "../lib/kbdb-client.js";
/**
* u6u_list_workflows
*
* thin-shell-alignment P1issue #11 KBDB `/records?template=workflow_metadata`
* CLI `acr list` KV 西 cypher `GET /webhooks/named`
* KV CLI job list KVsearch KBDB entry
* tag KBDB resource_tag workflow
*/
export function registerListWorkflows(server: McpServer, env: Env, orgNamespace: string, partnerToken: string) {
server.tool(
"u6u_list_workflows",
"列出當前命名空間下所有已部署的工作流,可選擇按 tag 篩選。",
{ tag: z.string().optional().describe("按 tag 名稱篩選(選填)") },
async ({ tag }) => {
try {
if (!env.CYPHER_EXECUTOR) {
return { content: [{ type: "text", text: "Error: CYPHER_EXECUTOR service binding is not configured." }], isError: true };
}
// tag 過濾(選填):先從 KBDB resource_tag 查出該 tag 下的 workflow_id 白名單。
let tagFilter: Set<string> | null = null;
if (tag) {
if (!env.KBDB) {
return { content: [{ type: "text", text: "Error: KBDB service binding unavailable (tag 過濾需要)" }], isError: true };
}
const tagResp = await kbdbFetch(env, `/records/search?template=resource_tag&user_id=${encodeURIComponent(orgNamespace)}&tag_name=${encodeURIComponent(tag)}&resource_type=workflow`);
if (!tagResp.ok) {
return { content: [{ type: "text", text: `Error querying tags: ${await tagResp.text()}` }], isError: true };
}
const tagData = await tagResp.json<{ records: Array<{ slots: { resource_id: string } }> }>();
tagFilter = new Set(tagData.records.map(r => r.slots.resource_id));
if (tagFilter.size === 0) return { content: [{ type: "text", text: JSON.stringify([], null, 2) }] };
}
// 主清單:讀 cypher GET /webhooks/namedKV 源,與 CLI acr list 同源)。
const resp = await env.CYPHER_EXECUTOR.fetch("http://cypher-executor/webhooks/named", {
method: "GET",
headers: { "X-Arcrun-API-Key": partnerToken },
});
if (!resp.ok) {
return { content: [{ type: "text", text: `Error listing workflows: ${await resp.text()}` }], isError: true };
}
const data = await resp.json<{ workflows: Array<{ name: string; description?: string; created_at?: string; webhook_url?: string }> }>();
let workflows = data.workflows ?? [];
// tag 過濾用 name 比對(KV 源主鍵=name)。
// ⚠️ 語意債(SDD §4 記):舊 tag 的 resource_id 可能存 UUID(舊 workflow_metadata workflow_id
// 或 name,語意不明確。方向①收斂到 KV(主鍵=name)後,resource_id 應統一為 name。
// 過渡期先用 name 比對(KV 源唯一鍵);舊用 UUID tag 的會在 backfill/re-tag 後對齊。待總管確認 tag 收斂。
if (tagFilter !== null) workflows = workflows.filter(w => tagFilter!.has(w.name));
return { content: [{ type: "text", text: JSON.stringify(workflows, null, 2) }] };
} catch (error) {
return { content: [{ type: "text", text: `Internal Error: ${error instanceof Error ? error.message : String(error)}` }], isError: true };
}
}
);
}