From ec873b56ca5f823a780ec9ff3b86f6780efeca93 Mon Sep 17 00:00:00 2001 From: x x Date: Fri, 4 Sep 2026 20:39:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(protocol):=20=E5=AE=9A=E4=B9=89=E9=80=9A?= =?UTF-8?q?=E7=94=A8=20Skill=20=E4=B8=8A=E4=B8=8B=E6=96=87=E5=A5=91?= =?UTF-8?q?=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mcpcontract/contract_test.go | 21 +++++++++++++++++++++ mcpcontract/output.go | 18 ++++++++++++++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/mcpcontract/contract_test.go b/mcpcontract/contract_test.go index 6b34337..af1fc69 100644 --- a/mcpcontract/contract_test.go +++ b/mcpcontract/contract_test.go @@ -96,6 +96,27 @@ func TestContextHasExplicitLocalAndFleetProfiles(t *testing.T) { if _, ok := localProperties["skills"]; !ok { t.Fatal("local context is missing skills") } + commonSkills, ok := localProperties["common_skills"].(map[string]any) + if !ok { + t.Fatal("local context is missing common_skills") + } + commonProperties := commonSkills["properties"].(map[string]any) + for _, name := range []string{"root", "total", "truncated", "items"} { + if _, ok := commonProperties[name]; !ok { + t.Fatalf("common_skills is missing %s", name) + } + } + for _, required := range local["required"].([]string) { + if required == "common_skills" { + t.Fatal("common_skills must remain optional for rolling compatibility with older AgentDock nodes") + } + } + nodes := fleet["properties"].(map[string]any)["nodes"].(map[string]any) + node := nodes["items"].(map[string]any) + nodeContext := node["properties"].(map[string]any)["context"].(map[string]any) + if _, ok := nodeContext["properties"].(map[string]any)["common_skills"]; !ok { + t.Fatal("fleet node context is missing common_skills") + } runtimeSchema, ok := localProperties["runtime"].(map[string]any) if !ok { t.Fatal("local context is missing runtime") diff --git a/mcpcontract/output.go b/mcpcontract/output.go index 9e4b84e..394fb40 100644 --- a/mcpcontract/output.go +++ b/mcpcontract/output.go @@ -160,6 +160,19 @@ func localContextProperties(includeShared bool) map[string]any { "file": stringProperty("skill:// URI for the active SKILL.md."), "bundled": booleanProperty("Whether the Skill is bundled by AgentDock."), }, "required": []string{"name", "description", "file"}, "additionalProperties": false, } + commonSkill := map[string]any{ + "type": "object", "properties": map[string]any{ + "name": stringProperty("Common Skill name."), "description": stringProperty("Short capability description."), + "file": stringProperty("Host path to the common SKILL.md."), + }, "required": []string{"name", "description", "file"}, "additionalProperties": false, + } + commonSkills := strictObject(map[string]any{ + "root": stringProperty("Common Agent Skills root path."), + "total": integerProperty("Total valid common Skills discovered before truncation."), + "truncated": booleanProperty("Whether the common Skill index was truncated."), + "items": map[string]any{"type": "array", "items": commonSkill}, + }, "root", "total", "truncated", "items") + commonSkills["description"] = "Lower-priority common Agent Skill capability index; installed AgentDock Skills take precedence on conflicts." dynamicItem := contextItemSchema(true) indexItem := contextItemSchema(false) warning := map[string]any{ @@ -168,8 +181,9 @@ func localContextProperties(includeShared bool) map[string]any { }, "required": []string{"source", "message"}, "additionalProperties": false, } props := map[string]any{ - "skills": map[string]any{"type": "array", "description": "Installed document Skill capability index.", "items": skill}, - "dynamic_mcp": map[string]any{"type": "array", "description": "Enabled dynamic MCP server capability index.", "items": dynamicItem}, + "skills": map[string]any{"type": "array", "description": "Installed document Skill capability index.", "items": skill}, + "common_skills": commonSkills, + "dynamic_mcp": map[string]any{"type": "array", "description": "Enabled dynamic MCP server capability index.", "items": dynamicItem}, "acp": strictObject(map[string]any{ "enabled": booleanProperty("Whether ACP is enabled."), "agent": stringProperty("Configured ACP agent name."), "description": stringProperty("Short ACP usage orientation."), }, "enabled", "agent", "description"),