feat(mcp): serve n8n-skills markdown via MCP Resources#793
Merged
Conversation
Add a SkillResourceRegistry that mirrors UIAppRegistry to expose the
seven n8n-mcp skill bundles (~36 markdown files from the n8n-skills
repo) under a new skill://n8n-mcp/{name}/{file} URI scheme. The MCP
host already wired ListResources/ReadResource for ui://n8n-mcp/{id}
UI apps; both schemes now coexist in the same handlers.
The skill markdown is brought in via npm run sync:skills, which
copies from a sibling n8n-skills/skills/ checkout (overridable via
N8N_SKILLS_SOURCE) into data/skills/. The copy is committed so npm
and Docker artifacts ship the skills without requiring the sibling
repo on the build host. Dockerfile copies data/skills/ alongside
data/nodes.db; package.json files field ships data/skills/**/*.md.
data/skills/ is added to .secretlintignore because the upstream
markdown contains placeholder credentials in code examples — same
treatment as the n8n-docs clone.
A bare skill://n8n-mcp/{name} URI resolves to that skill's SKILL.md
as a convenience. A new resources/templates/list handler advertises
both URI templates so capable clients can construct URIs directly.
text/markdown is used as the mime type throughout. Description for
SKILL.md is parsed from frontmatter; supporting files fall back to
the first heading.
Claude Code users still get the better UX through the existing
n8n-mcp-skills plugin (auto-loaded SKILL frontmatter, description-
based triggering via the Skill tool). MCP Resources reach the
clients that lack a skill system: Cursor, Claude Desktop without
plugins, the OpenAI Agents SDK, and other custom agents — so
the same skill content now reaches every n8n-mcp consumer.
- 15 unit tests cover registry init, frontmatter parsing, URI
resolution (bare and explicit), template listing, and reset
- Live smoke test against the running MCP server: resources/list
returns 38 entries (2 UI apps + 36 skill files); resources/read
resolves bare skill URI to SKILL.md and a specific file URI to
its body; ui://n8n-mcp/operation-result still returns the
operation-result HTML unchanged
Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address code-reviewer feedback on PR #793: - parseFrontmatter now normalizes \r\n → \n before checking the leading fence, so a Windows checkout of n8n-skills does not silently lose all SKILL.md metadata (previously content.startsWith('---\\n') returned false against '---\\r\\n', dropping name + description). - Inline the getByUri simplification — a single Map.get + truthy check replaces the has/get! pattern, no behavior change. - New test for CRLF frontmatter exercises the normalization above. - New traversal test locks the security guarantee on the bare-URI fallback: skill://n8n-mcp/../../../etc/passwd and skill://n8n-mcp/a/../b/SKILL.md both resolve to null, because the bare-name path refuses any remainder containing '/' and the explicit-file path requires an exact Map key match. 17/17 registry tests pass. Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
Test Results Summary📊 ArtifactsGenerated at Mon, 18 May 2026 13:26:37 GMT |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a
SkillResourceRegistrythat mirrorsUIAppRegistryto expose the seven n8n-mcp skill bundles (~36 markdown files from then8n-skillsrepo) through MCP Resources under a newskill://n8n-mcp/{name}/{file}URI scheme. The server already wiredListResources/ReadResourcefor UI apps atui://n8n-mcp/{id}— both schemes now coexist in the same handlers, with a newListResourceTemplateshandler advertising the two URI templates.The skill markdown is brought in via
npm run sync:skills, which copies from a siblingn8n-skills/skills/checkout (overridable viaN8N_SKILLS_SOURCE) intodata/skills/. The copy is committed so the npm artifact and Docker image ship the skills without requiring the sibling repo on the build host.Why
The
n8n-skillsrepo ships only as a Claude Code plugin. That reaches Claude Code users with the plugin installed; every other MCP client — Cursor, Claude Desktop without plugins, the OpenAI Agents SDK, custom agents — had no path to discover or read the same content even though they all consumen8n-mcp. MCP Resources is the standard surface for on-demand markdown context, so a parallelskill://namespace lets every MCP client reach the same skill bundle the plugin provides.This is complementary, not a replacement: Claude Code users still get the better UX via the plugin (auto-loaded SKILL frontmatter, description-based triggering via the
Skilltool). Other clients now get the content viaresources/list+resources/read.Test plan
npm run typecheck— cleannpm test -- tests/unit/mcp/skills/registry.test.ts— 15/15 passingnpm run test:unit— 4854 passing, no regressionsnpm run build— clean,dist/mcp/skills/emittedresources/listreturns 38 entries (2 UI apps + 36 skill files) withtext/markdownmime typeresources/read skill://n8n-mcp/n8n-code-javascriptresolves toSKILL.md(bare URI alias)resources/read skill://n8n-mcp/n8n-validation-expert/FALSE_POSITIVES.mdreturns the file bodyresources/read ui://n8n-mcp/operation-resultstill returns the operation-result HTML unchanged (regression)Notes for reviewers
data/skills/is committed (37 markdown files, ~28k lines). Pulled fromczlonkowski/n8n-skillsvia the newnpm run sync:skillsscript. The script tries two candidate sibling locations and accepts anN8N_SKILLS_SOURCEoverride..secretlintignoreexemptsdata/skills/— the upstream markdown contains placeholder Slack/API tokens (xoxb-your-tokenetc.) in code examples, same as the existingn8n-docs/exemption.2.54.0(minor — additive feature, no breaking changes).Conceived by Romuald Członkowski - https://www.aiadvisors.pl/en
🤖 Generated with Claude Code