Skip to content

feat: skill registry MCP — skill_search + skill_load - #92

Merged
getappz merged 11 commits into
masterfrom
feat/skill-registry
Jul 8, 2026
Merged

feat: skill registry MCP — skill_search + skill_load#92
getappz merged 11 commits into
masterfrom
feat/skill-registry

Conversation

@getappz

@getappz getappz commented Jul 8, 2026

Copy link
Copy Markdown
Owner

What

New crates/skill-registry (MCP-free) + two MCP tools on the existing agentflare server, letting any agent search the machine's installed skills and load one on demand — compressed shadow copies served by default.

  • skill_search(query, limit=5, mode=all|any) — SQLite FTS5 BM25 (name 3.0 / desc 1.0 / tags 2.0), engram-style query sanitization (every token quoted; operators neutralized).
  • skill_load(name, original=false) — shadow-preferred body, source:name qualified names (last-colon split), ambiguity errors list candidates, unknown names hint at skill_search.
  • Sources: claude user/project skills, plugin cache (newest version per plugin+skill), plus codex/cursor/opencode dirs gated on agent-registry detection.
  • Shadow pairing via <!-- compressed-from: ... --> provenance markers; est_tokens reflects the body actually served.
  • Index: own skills.db (data_local_dir), transactional full-replace rebuild, WAL + busy_timeout for concurrent agents, dup-tolerant inserts.
  • Registry persisted across MCP calls (60s refresh debounce).

Testing

  • 268 workspace tests green; skill-registry clippy-clean.
  • Golden-query recall gate: hit@3 = 1.00 (20/20) vs the ≥0.85 bar — BM25 suffices, v2 embeddings stay deferred per the plan.
  • Final whole-branch review + fix pass (dup-tolerant rebuild, WAL/busy_timeout, persistent registry, shadow est_tokens) re-verified: ready to merge.

Notes

Deferred nice-to-haves: path normalization in shadow-marker matching, 2000-char marker scan window, poisoned-lock recovery.

Summary by CodeRabbit

  • New Features
    • Added a local skill registry with BM25 search and ability to load full skill details.
    • Skills support compressed/shadow variants with associated sibling files.
    • Expanded automatic discovery of skills across supported locations.
    • Added MCP tool endpoints for skill search and skill load.
  • Bug Fixes
    • Improved persistence behavior to keep search indexing consistent during refreshes, including duplicate handling.
  • Tests
    • Added search recall “golden” coverage plus unit/integration tests for loading, discovery, and MCP validations.

getappz added 9 commits July 8, 2026 03:12
…meout, persistent registry, shadow est_tokens

- db.rs: rebuild() uses INSERT OR IGNORE so one duplicate (name, source)
  no longer rolls back the whole rebuild and disables skill_search/skill_load;
  skips the fts mirror insert when a row is ignored (tx.changes() == 0).
- db.rs: open_db() sets a 5s busy_timeout and WAL journal mode on the shared
  skills.db to avoid SQLITE_BUSY collisions across concurrent agentflare
  processes.
- mcp_server.rs: AgentflareMcp now owns a persisted, mutex-guarded Registry
  instead of opening a fresh one per call, so ensure_fresh()'s 60s debounce
  actually applies instead of doing a full rescan + rebuild on every call.
- sources.rs: shadow pairing copies the shadow's est_tokens onto the kept
  origin entry, since skill_load serves the shadow body by default.
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f97749d8-4c77-4afa-a3f5-8012ac385a25

📥 Commits

Reviewing files that changed from the base of the PR and between 92bfc3a and 2d4d8c1.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • Cargo.toml
  • crates/skill-registry/src/sources.rs
  • src/mcp_server.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • Cargo.toml
  • src/mcp_server.rs
  • crates/skill-registry/src/sources.rs

📝 Walkthrough

Walkthrough

This PR adds a new skill-registry crate for scanning SKILL.md sources, parsing frontmatter, persisting entries in SQLite with FTS5, searching skills, loading skill content, and exposing the registry through new MCP tools.

Changes

Skill Registry Feature

Layer / File(s) Summary
Workspace and crate manifest setup
Cargo.toml, crates/skill-registry/Cargo.toml
Adds the new crate to the workspace and root dependency set, and defines its manifest, dependencies, and dev-dependency.
Frontmatter parsing
crates/skill-registry/src/frontmatter.rs
Parses YAML frontmatter blocks from SKILL.md text into a typed structure.
Skill source scanning and shadow pairing
crates/skill-registry/src/sources.rs
Defines skill source models and scans flat directories and plugin caches, including version selection, shadow pairing, and default source assembly.
SQLite persistence layer
crates/skill-registry/src/db.rs
Defines the SQLite schema and rebuild logic for storing skills and mirroring them into FTS5.
BM25 search implementation
crates/skill-registry/src/search.rs
Defines match modes and hit records, sanitizes FTS queries, and executes BM25-ranked searches with tests.
Skill loading and Registry facade
crates/skill-registry/src/lib.rs, crates/skill-registry/src/load.rs, crates/skill-registry/tests/golden_queries.rs
Defines load and registry APIs, resolves skill bodies and siblings, re-exports the crate surface, and adds a recall regression test.
MCP server integration
src/mcp_server.rs
Adds skill_search and skill_load, stores a persistent registry in server state, and updates tests for the new constructor path.

Estimated code review effort: 4 (Complex) | ~60 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and clearly names the main change: adding the skill registry MCP tools.
Description check ✅ Passed It covers the required summary, testing, and reviewer notes content, though the section headings differ from the template.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/skill-registry

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
crates/skill-registry/src/sources.rs (1)

86-91: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Cache the provenance regex instead of recompiling per call.

shadow_origin is invoked once for every claude-user entry during each scan, and Regex::new recompiles the same static pattern each time. Hoist it into a LazyLock (or once_cell) so compilation happens once.

♻️ Proposed change
+use std::sync::LazyLock;
+
+static SHADOW_RE: LazyLock<regex::Regex> =
+    LazyLock::new(|| regex::Regex::new(r"compressed-from:\s+(.+?)\s+\d+B").unwrap());
+
 /// `<!-- compressed-from: <path> <N>B → <M>B, <date> -->` in the first 2000 chars.
 fn shadow_origin(path: &Path) -> Option<PathBuf> {
     let text = std::fs::read_to_string(path).ok()?;
     let head: String = text.chars().take(2000).collect();
-    let re = regex::Regex::new(r"compressed-from:\s+(.+?)\s+\d+B").unwrap();
-    re.captures(&head).map(|c| PathBuf::from(c[1].to_string()))
+    SHADOW_RE.captures(&head).map(|c| PathBuf::from(c[1].to_string()))
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/skill-registry/src/sources.rs` around lines 86 - 91, The shadow_origin
function is recompiling the same regex on every call, which adds unnecessary
overhead during scans. Hoist the compressed-from pattern into a static cached
regex using LazyLock or once_cell, then reuse that compiled value inside
shadow_origin instead of calling Regex::new each time.
src/mcp_server.rs (1)

422-432: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

skill_load test should use an isolated registry path. AgentflareMcp::default() still reaches with_fresh_registry here, so this test opens/creates the shared skills.db and refreshes it on disk. Inject the DB path so the test can use a temp file instead of mutating shared state.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/mcp_server.rs` around lines 422 - 432, The
`skill_load_unknown_name_reports_not_found_with_search_hint` test is still using
`AgentflareMcp::default()`, which points at the shared registry and can mutate
`skills.db` during the run. Update the test to construct `AgentflareMcp` with an
injected temporary DB path instead of relying on `with_fresh_registry`, so
`skill_load` and related registry access use isolated state. Use the existing
`AgentflareMcp`/registry initialization path to wire in the temp file and keep
the assertion on the `skill_search` hint unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/skill-registry/src/sources.rs`:
- Around line 86-91: The shadow_origin function is recompiling the same regex on
every call, which adds unnecessary overhead during scans. Hoist the
compressed-from pattern into a static cached regex using LazyLock or once_cell,
then reuse that compiled value inside shadow_origin instead of calling
Regex::new each time.

In `@src/mcp_server.rs`:
- Around line 422-432: The
`skill_load_unknown_name_reports_not_found_with_search_hint` test is still using
`AgentflareMcp::default()`, which points at the shared registry and can mutate
`skills.db` during the run. Update the test to construct `AgentflareMcp` with an
injected temporary DB path instead of relying on `with_fresh_registry`, so
`skill_load` and related registry access use isolated state. Use the existing
`AgentflareMcp`/registry initialization path to wire in the temp file and keep
the assertion on the `skill_search` hint unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0f29cbf0-7696-44a0-a634-9aae638d42fa

📥 Commits

Reviewing files that changed from the base of the PR and between e8c33df and 92bfc3a.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • Cargo.toml
  • crates/skill-registry/Cargo.toml
  • crates/skill-registry/src/db.rs
  • crates/skill-registry/src/frontmatter.rs
  • crates/skill-registry/src/lib.rs
  • crates/skill-registry/src/load.rs
  • crates/skill-registry/src/search.rs
  • crates/skill-registry/src/sources.rs
  • crates/skill-registry/tests/golden_queries.rs
  • src/mcp_server.rs

@getappz
getappz merged commit fc74f23 into master Jul 8, 2026
10 checks passed
@getappz
getappz deleted the feat/skill-registry branch July 8, 2026 05:29
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 8, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant