mirror of
https://github.com/chainbase-labs/Agentkey.git
synced 2026-09-20 14:20:23 +08:00
65fb2f8181
## Summary
Fixes the silent-update-failure mode where Claude Desktop (and any MCP
client without a Bash tool) gets stuck on whatever skill version shipped
at first install. On this developer's Desktop the skill had been frozen
at `0.1.2` since April — no upgrade ever fired.
Root cause is structural: SKILL.md Step 0's update check uses an inline
` ```bash ``` ` block. Claude Code executes it; Desktop reads it as
documentation. So the entire upgrade flow is dead code on Desktop. This
PR routes the version check through the MCP server instead (always-on,
available to every client), and tightens a couple of correctness bugs in
the existing install/uninstall path while we're here.
Companion PR: chainbase-labs/AgentKey-Server (server-side
`agentkey_skill_meta` tool).
## What's in here
1. **Protocol** (`protocol/skill-meta-v1.md` +
`skill-meta-v1.schema.json` + 4 fixtures) — versioned,
additive-evolution wire format for an MCP meta tool that returns
`{skill_version_latest, client_detected, update_command, update_doc_url,
…}`. Spec lives in this repo (single source of truth); server vendors a
copy and CI on both sides diffs them.
2. **SKILL.md** — Step 0 now has 0.A (beacon, cross-client) → 0.B
(inline bash, Code-only compat) → 0.C (MCP tool sanity check). Step B
branches every persistence option on whether Bash is available, with
explicit no-Bash fallback text that tells the user what didn't get saved
and the exact terminal command to persist it manually. Step C points the
non-shell fallback at GitHub Releases (we don't have a docs site).
3. **install/uninstall scripts** — `npx skills remove
chainbase-labs/agentkey` was the wrong invocation: the CLI takes the
skill name (`agentkey`), exits 0 on no-match, and made the uninstaller
falsely report success. Same class of silent-success bug in `install.sh`
when `git clone` fails mid-run. Both fixed; added post-install
filesystem verification.
4. **README / README_zh** — accurate per-client update story, including
a one-time bootstrap command for users currently stuck on a pre-1.4.0
skill on Desktop.
5. **CI** (`protocol-validate.yml`) — every fixture validates against
the schema, schema rejects 4 known-bad payloads (regression guard), spec
doc references every fixture (forces docs ↔ artifact sync).
6. **`docs/SERVER-IMPLEMENTATION.md`** — handoff doc for the server PR.
## How verified
- 4/4 fixtures pass schema; 4/4 bad payloads correctly rejected
- All cross-references in spec doc resolve
- `verify-version-sync` awk still extracts `1.3.0` from SKILL.md
frontmatter
- Companion server PR exercises the actual MCP handshake (initialize +
tools/list + tools/call); response is valid v1 JSON
- Real GitHub Releases fetch + ETag caching works on the server side
## Test plan
- [ ] CI green (`protocol-validate.yml` and `verify-version-sync.yml`
both pass)
- [ ] Companion server PR merged + new `@agentkey/mcp` published
- [ ] Release-please cuts `v1.4.0` from this branch
- [ ] On Claude Code: existing inline-bash Step 0 still fires for users
on `v1.3.x`; they get prompted to update normally
- [ ] On Claude Desktop with a pre-1.4.0 skill: user runs the README
bootstrap command once to land `v1.4.0`; from that point on, every
subsequent version is auto-discovered via the meta tool
- [ ] On Cursor / Codex: meta tool returns the `npx skills update -g
agentkey` recipe; user upgrades via shell
## Notes for the reviewer
- This is **additive**: Claude Code's existing inline-bash path is
unchanged, so no regression risk there. The protocol's
`protocol_version: 1` + immortal `update_doc_url` fallback make future
v2 servers safely degradable for v1 skills.
- Claude Desktop deliberately has no `update_command` recipe yet —
Desktop installs skills into a sandboxed `~/Library/Application
Support/Claude/local-agent-mode-sessions/skills-plugin/<UUID>/...` path
that no external CLI can reach, and we don't have a first-party
installer script. The skill rule's "no command → point at GitHub
Releases" fallback handles this until one exists. Adding a Desktop
recipe later is a non-breaking change (one row in the server's `RECIPES`
map).
85 lines
3.3 KiB
YAML
85 lines
3.3 KiB
YAML
name: protocol-validate
|
|
|
|
# Validates that every example fixture under protocol/ conforms to
|
|
# skill-meta-v1.schema.json. Catches the case where someone updates the schema
|
|
# but forgets to update the fixtures (or vice versa). When @agentkey/mcp
|
|
# eventually ships its vendored copy of the same schema, a second job here will
|
|
# diff against it to detect drift in the other direction.
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'protocol/**'
|
|
- '.github/workflows/protocol-validate.yml'
|
|
pull_request:
|
|
paths:
|
|
- 'protocol/**'
|
|
- '.github/workflows/protocol-validate.yml'
|
|
|
|
jobs:
|
|
validate-fixtures:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Validate every example fixture against the schema
|
|
run: |
|
|
set -euo pipefail
|
|
shopt -s nullglob
|
|
fixtures=(protocol/example-*.json)
|
|
if [ ${#fixtures[@]} -eq 0 ]; then
|
|
echo "::error::no fixtures found under protocol/example-*.json"
|
|
exit 1
|
|
fi
|
|
for f in "${fixtures[@]}"; do
|
|
echo "=== $f ==="
|
|
npx -y ajv-cli@5 validate \
|
|
--spec=draft2020 \
|
|
-s protocol/skill-meta-v1.schema.json \
|
|
-d "$f"
|
|
done
|
|
|
|
- name: Schema must reject known bad payloads (regression guard)
|
|
run: |
|
|
set -euo pipefail
|
|
tmp=$(mktemp -d)
|
|
# protocol_version must be 1
|
|
echo '{"protocol_version":2,"skill_version_latest":"1.0.0","client_detected":"claude","update_doc_url":"https://x"}' > "$tmp/bad-v2.json"
|
|
# update_command requires update_command_kind (dependentRequired)
|
|
echo '{"protocol_version":1,"skill_version_latest":"1.0.0","client_detected":"claude","update_doc_url":"https://x","update_command":"echo hi"}' > "$tmp/bad-no-kind.json"
|
|
# version string must not have a 'v' prefix
|
|
echo '{"protocol_version":1,"skill_version_latest":"v1.0.0","client_detected":"claude","update_doc_url":"https://x"}' > "$tmp/bad-vprefix.json"
|
|
# client_detected must be lowercase short identifier
|
|
echo '{"protocol_version":1,"skill_version_latest":"1.0.0","client_detected":"Claude Desktop","update_doc_url":"https://x"}' > "$tmp/bad-caps.json"
|
|
|
|
fail=0
|
|
for f in "$tmp"/bad-*.json; do
|
|
if npx -y ajv-cli@5 validate --spec=draft2020 -s protocol/skill-meta-v1.schema.json -d "$f" >/dev/null 2>&1; then
|
|
echo "::error::$f should have been rejected by the schema but wasn't"
|
|
fail=1
|
|
else
|
|
echo "✓ correctly rejected $(basename "$f")"
|
|
fi
|
|
done
|
|
exit $fail
|
|
|
|
spec-cross-references:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
- name: Spec doc references all fixtures
|
|
run: |
|
|
set -euo pipefail
|
|
# Every fixture file should be mentioned in the spec's "See also" block,
|
|
# so adding a new fixture without doc-cross-referencing it fails CI.
|
|
missing=0
|
|
for f in protocol/example-*.json; do
|
|
base=$(basename "$f")
|
|
if ! grep -q "$base" protocol/skill-meta-v1.md; then
|
|
echo "::error file=protocol/skill-meta-v1.md::fixture $base is not referenced in the spec"
|
|
missing=1
|
|
fi
|
|
done
|
|
exit $missing
|