feat: skills tools (find, read, create, update)

DE
10h ago
+1236-59

Exposes Alexandria skills as meta tools, the same way firecrawl_find_tools exposes the catalogue.

  • firecrawl_find_skills (query?, limit?) calls firecrawl/find-skills. It lists Firecrawl's skills plus the team's own (team:<slug>, first).
    • Each skill gets a nextTool that reads it.
    • Library skills also get a createTool that copies them into the team's own.
  • firecrawl_read_skill (id, path?, version?) calls firecrawl/read-skill and returns one file in full. Library reads carry a createTool.
  • firecrawl_create_skill (from?, name?, description?, body?, slug?) calls firecrawl/create-skill. It saves a team skill, written out or copied from a listed skill.
  • firecrawl_update_skill (id, baseVersion, name?, description?, body?, enabled?, discoverable?) calls firecrawl/update-skill. A stale baseVersion is a conflict. There is no delete.
  • Behaviour: all four are free, need an API key, and are hidden from keyless sessions (canList). Schemas are strict, so bad arguments are refused before Exchange is called. Create and update answer with a nextTool that reads the saved skill.
  • Scope: full surface only; SEARCH_PROFILE_TOOLS is unchanged. Authenticated server instructions get one sentence on the four tools.

Needs firecrawl/exchange#961 (create-skill and update-skill) deployed for the write tools. Find and read already work in production.

Tests

  • tests/mcp-alexandria-skills.test.mjs, 8 tests:
    • listing and annotations;
    • forwarding of each call;
    • nextTool and createTool links;
    • team-skill reads, current and pinned;
    • write calls and the links they return;
    • strict-schema refusals;
    • keyless hiding.
  • Contract snapshots regenerated with UPDATE_CONTRACT_SNAPSHOTS=1 and no key in the environment. The keyless snapshot matches main.
  • Live check against production with a real key for find and read: firecrawl_find_skills returned the team's skills first, and firecrawl_read_skill returned the full SKILL.md.
  • Local runs of the cloud-HTTP/OAuth transport and search-profile tests fail because port 3001 was already taken by another local app (EADDRINUSE). None of them involve these tools.