skill-central
Local MCP Server for Cross-IDE AI Skill Distribution
skill-central is a local MCP (Model Context Protocol) server that communicates with AI IDEs (Cursor, Windsurf, Claude Code, etc.) via the Stdio protocol, enabling cross-IDE AI skill (prompts/tools) distribution and reuse.
A skill is a structured prompt or tool definition — organised by topic, managed in layers, matched by tags. You manage your AI's capability boundaries the same way you manage code.
Since v0.2.0 skill-central ships a complete local CRUD CLI, a Hono-based web board for in-browser editing, and a remote install path from GitHub raw URLs and npm packages.
Table of Contents
- Quick Start
- Architecture
- CLI Commands
- Local CRUD
- Web Board
- Remote Install
- JSON-RPC API
- Skill File Format
- Tag Composition
- Layered Override
- Config Resolution Order
- IDE Integration
- Custom Skill Development
- Deployment & Troubleshooting
- Documentation
- Pre-release Testing
- Trusted Publishing / 受信任的发布
- Manual Publishing / 手动发布
- Development
- License
Quick Start
Note:
skill-centralis a template repository. You can either use the pre-published package (@bobcgn/skill-central) directly, or clone/fork this repository to publish your own custom MCP server.
Install / Initialize
# If using the default published service:
npx @bobcgn/skill-central init
# If you published your own package:
# npx your-package-name init
# If running from a cloned repo locally:
# npm run build && npm link
# skill-central init
This scaffolds a .skills/ directory with 4 layered skill directories and a skill-central.yaml config file.
Open the web board
# Using the default published service
npx @bobcgn/skill-central board
Prints http://127.0.0.1:5417/ and opens a Hono dashboard in your terminal — browse, preview, edit, and restore skills from the browser. Use board --cli (or --no-web) for the terminal-table fallback.
Start the MCP server
npx @bobcgn/skill-central mcp
The server listens on stdin for JSON-RPC messages and writes responses to stdout. All diagnostic output goes to stderr.
Architecture
┌────────────────────────────────────────────────────────┐
│ AI IDE (Cursor / Windsurf / etc.) │
│ │ Stdio (JSON-RPC) │
└────────────────────────┼────────────────────────────────┘
│
┌────────────────────────┼────────────────────────────────┐
│ skill-central ▼ │
│ ┌────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ CLI │ │ Protocol │ │ Core │ │
│ │ mcp/init │→│ handler.ts │→│ engine │ │
│ │ add/list │ │ prompts.ts │ │ override- │ │
│ │ show/... │ │ tools.ts │ │ tree │ │
│ │ install │ └─────────────────┘ │ composer │ │
│ │ board │ ┌─────────────────┐ └──────┬──────┘ │
│ │ (Hono) │ │ Web board │ │ │
│ │ │ │ server.ts │ ┌──────▼───────┐ │
│ │ │ │ router.ts │ │ Storage │ │
│ │ │ │ backup.ts │ │ reader.ts │ │
│ └────────────┘ └─────────────────┘ │ parser.ts │ │
│ │ config.ts │ │
│ └──────────────┘ │
└──────────────────────────────────────────────────────────┘
Default Skill Layers
| Layer | Priority | Scope |
|---|---|---|
01-global |
10 | Universal context — applies to every interaction |
02-workflows |
20 | Cross-cutting workflows (debugging, review, planning) |
03-domains |
30 | Domain-specific knowledge (infra, security, data) |
04-tech-stack/languages |
40 | Language conventions (TypeScript, Python, Kotlin…) |
04-tech-stack/frameworks |
40 | Framework conventions (React, Vue, Spring…) |
Higher priority wins on id collision. See Layered Override.
CLI Commands
npx @bobcgn/skill-central <command>
| Command | Description |
|---|---|
mcp |
Start the Stdio MCP Server (IDE-facing, silent on stdout) |
board |
Open the web dashboard (default) or print terminal table (--cli) |
init |
Scaffold .skills/ with sample skills and skill-central.yaml |
add |
Create a new skill definition (auto-selects layer from tags) |
list |
List loaded skills (filters: --layer, --tag, --type) |
show <id> |
Print full skill details + prompt body |
remove <id> |
Delete a skill definition file (with --layer ambiguity guard) |
validate <files…> |
Parse + validate one or more skill files |
doctor |
Scan layers for missing dirs, parse errors, collisions, backups |
install <source> |
Install a skill from github: or npm: URL |
update [id] |
Re-fetch installed skill(s); preserves original scope |
uninstall <id> |
Remove an installed skill (file + lock entry) |
After global install:
npm install -g @bobcgn/skill-central
skill-central init
skill-central board # opens web dashboard
skill-central mcp # or start the MCP server
See docs/en/cli-reference.md / docs/ch/cli-reference.md for the full flag reference.
Local CRUD
# Create a skill — layer inferred from tags
skill-central add review-pr \
--name "PR Review" \
--description "Review pull requests against team conventions" \
--tags "review,workflow,git" \
--prompt-file ./review.md
# Inspect
skill-central list --tag review
skill-central show review-pr
# Validate before committing
skill-central validate .skills/02-workflows/review-pr.yaml
# Clean up
skill-central remove review-pr --force
add writes the YAML to a layer directory chosen by:
- an explicit
--layerflag (always wins) - an existing file with the same id (idempotent re-add)
- tag-based inference via
LAYER_RULES - fallback to
02-workflowsif no tags match
doctor is your diagnostic safety net:
$ skill-central doctor
...
▸ Layers
01-global ✓ 1 file
02-workflows ✓ 9 files
03-domains ✓ 3 files
04-tech-stack ✓ 3 files
▸ ✓ All skill files parse cleanly
▸ ✓ No id collisions
▸ ✓ No orphan backups
Web Board
skill-central board starts a local Hono server and prints a URL:
✓ skill-central web board
http://127.0.0.1:5417/
Press Ctrl+C to stop.
The dashboard shows skills grouped by layer in the sidebar. Click any skill to preview its prompt body in the detail pane. Click Edit to open an in-browser textarea; Save writes the change back to disk via PUT /api/skills/:id.
Each save moves the previous content to <file>.yaml.bak.<ISO-no-colons>. Concurrent edits are caught by sha256 mismatch — the server returns 409 with the current content if your expected sha doesn't match.
Security
- Loopback-only by default.
--host 0.0.0.0requires--i-understand-nonlocalbecause the web board has no authentication. - Port conflict retry.
+1..+10from the requested port. - Static asset path traversal.
GET /*resolves underdist/web/;..returns 404.
Full HTTP API and edit-flow walkthrough in docs/en/web-board.md / docs/ch/web-board.md.
Remote Install
Install a skill from a remote URL into your local layers:
# From a GitHub raw file (any branch / tag / SHA)
skill-central install \
github:BobcGn/skill-central/.skills/02-workflows/review-pr.yaml@v1.0.0
# From an npm package (requires skill-central.paths in its package.json)
skill-central install npm:@bobcgn/some-skills@1.2.3
# Re-fetch on demand
skill-central update # all installed
skill-central update review-pr # one
# Tear down
skill-central uninstall review-pr --purge-backups
Every install writes a ~/.skill-central/lock.json entry recording the source URL, resolved version, sha256, layer, and absolute file path. update re-fetches, compares sha256, and only writes when the upstream has actually changed.
Security
- HTTPS-only.
http://URLs are rejected. - No loopback hosts. Tarball sources pointing at
localhost,127.0.0.0/8,::1, or0.0.0.0are rejected (tar-slip / SSRF mitigation). - Tar-slip defence. npm tarball entries must start with
package/;..and\are rejected. - sha256 verification. Every install + update computes and stores the sha256.
Full grammar and manifest conventions in docs/en/remote-sources.md / docs/ch/remote-sources.md.
JSON-RPC API
prompts/list
List all prompt-type skills.
// Request
{"jsonrpc":"2.0","id":1,"method":"prompts/list"}
// Response
{"result":{"prompts":[
{"name":"architectural-mindset","description":"Before writing code, always reason..."},
{"name":"debugging-expert","description":"Systematic debugging..."},
{"name":"container-infra","description":"Docker, Nginx, and infra deployment..."}
]}}
prompts/get
Single skill lookup:
{"jsonrpc":"2.0","id":1,"method":"prompts/get","params":{"name":"container-infra"}}
Template interpolation is supported: placeholders like {{name}} in the skill's prompt field are replaced with argument values:
{"method":"prompts/get","params":{"name":"my-skill","arguments":{"name":"Alice"}}}
Tag-based composition (combine multiple skills, low→high priority):
{"method":"prompts/get","params":{"name":"skills:compose","arguments":{"tags":"global,debug,docker"}}}
tools/list / tools/call
{"jsonrpc":"2.0","id":1,"method":"tools/list"}
{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"commit-conventions","arguments":{"type":"feat","summary":"add login page"}}}
Tool arguments are validated against the skill's inputSchema — missing required fields or type mismatches return an error result with isError: true.
See docs/en/mcp-protocol.md / docs/ch/mcp-protocol.md for the full schema.
Skill File Format
Skills can be defined in YAML (recommended) or JSON.
# .skills/04-tech-stack/languages/typescript.yaml
id: typescript-conventions
name: TypeScript Conventions
description: TypeScript coding standards for the project
type: prompt
tags:
- typescript
prompt: |
You are an expert TypeScript developer. Follow these conventions:
## Code Style
- Use strict mode — always enable `strict: true` in tsconfig.
- Prefer interfaces over type aliases for object shapes.
- Use explicit return types on public functions.
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | ✓ | Globally unique identifier (kebab-case). Used as prompt name. |
name |
string | ✓ | Human-readable label. |
description |
string | ✓ | One-line description. |
type |
"prompt" / "tool" |
✓ | Skill type. |
tags |
string[] | Categorisation tags for composition + layer inference. | |
prompt |
string | for prompt | Markdown instructions. Supports {{placeholder}} interpolation. |
inputSchema |
object | for tool | JSON Schema input definition. Validated on tool call. |
arguments |
object[] | Declared arguments (informational, for IDE UI). | |
version |
string | Semver for change tracking. |
Full schema reference in docs/en/skill-schema.md / docs/ch/skill-schema.md.
Tag Composition
When an IDE calls skills:compose, the engine:
- Matches all skills whose tags overlap the requested tag set
- Sorts by layer priority (ascending — low first)
- Concatenates prompt content separated by
---
Example — tags: "debug,docker" triggers:
[docker, nginx, infra, devops] ← container-infra (priority 30)
matched via "docker"
[debug, fix, error] ← debugging-expert (priority 20)
matched via "debug"
Result: debugging guidance first, then infra standards — layered from concrete workflow to domain knowledge, combined in priority order.
Layered Override
When multiple layers define the same id, the highest-priority entry wins. The skill-central.yaml produced by init looks like:
layers:
- name: "01-global"
path: ".skills/01-global"
priority: 10
- name: "02-workflows"
path: ".skills/02-workflows"
priority: 20
- name: "03-domains"
path: ".skills/03-domains"
priority: 30
- name: "04-tech-stack"
path: ".skills/04-tech-stack"
priority: 40
This lets teams share a common skill repository while allowing individuals to layer custom overrides — no file copying required.
User-level baseline (v0.2.0)
add --user and install (default scope) write to ~/.skill-central/skills/. The four sub-directories mirror the project 1:1, with priorities 5 / 15 / 25 / 35 — always below the project, so a project can always shadow a global baseline. See docs/en/layered-override.md / docs/ch/layered-override.md.
Config Resolution Order
1. ~/.skill-central/config.yaml ← machine-wide defaults
2. <project>/skill-central.yaml ← per-project (overrides same-named layers)
3. Built-in fallback ← { name: "project", path: ".skills", priority: 100 }
Layers with the same name are merged (later sources overwrite path/priority).
IDE Integration
Connect skill-central to your AI IDE as an MCP tool.
Tip: If you published your own version from this template, replace
@bobcgn/skill-centralwith your own npm package name. If you linked the repo locally, useskill-central mcpas the command.
Cursor
.cursor/mcp.json:
{
"mcpServers": {
"skill-central": {
"command": "npx",
"args": ["-y", "@bobcgn/skill-central", "mcp"]
}
}
}
Windsurf
.windsurf/mcp_config.json:
{
"mcpServers": {
"skill-central": {
"command": "npx",
"args": ["-y", "@bobcgn/skill-central", "mcp"]
}
}
}
Claude Code
claude mcp add skill-central -- npx -y @bobcgn/skill-central mcp
Custom Skill Development
Option A — add (recommended)
skill-central add typescript-conventions \
--name "TypeScript Conventions" \
--description "Coding standards for this project" \
--tags "typescript,lang-ts" \
--prompt-file ./ts-conventions.md
Layer inference picks 04-tech-stack/languages/ from the typescript tag. Verify with:
skill-central show typescript-conventions
Option B — write YAML by hand
mkdir -p .skills/04-tech-stack/languages
.skills/04-tech-stack/languages/typescript.yaml:
id: typescript-conventions
name: TypeScript Conventions
description: TypeScript coding standards
type: prompt
tags:
- typescript
prompt: |
You are an expert TypeScript developer...
Verify and use
skill-central doctor # catch parse errors + collisions
skill-central show typescript-conventions # inspect the rendered result
Refer to .skills/04-tech-stack/_template.yaml for a complete annotated example.
Deployment & Troubleshooting
This section is critical. MCP clients (Cursor, Windsurf, Claude Code, etc.) communicate with the server process over stdio (standard input/output). stdout is the exclusive JSON-RPC data channel. Any non-protocol data on stdout causes the connection to fail silently — no error, no warning, just an empty tool list. The following two issues are the most common "invisible killers."
Issue 1: npx Interactive Prompt Blocks Startup
Symptom
When you start the server with npx @bobcgn/skill-central mcp in your MCP config, npx may print an interactive prompt on stdout if the package isn't cached locally:
Need to install the following packages:
@bobcgn/skill-central@0.3.0
Ok to proceed? (y)
Why It's Fatal
This prompt appears on stdout. The MCP client is waiting for a valid JSON-RPC response but receives plain text instead. Strict parsers (used by Claude Code, Cursor, etc.) immediately判定 the connection as failed and silently discard the entire tool list — your IDE won't show any error, it just sees "no tools available."
Solution
Option A (Recommended): Add -y to args
The -y flag tells npx to auto-confirm installation, bypassing the interactive prompt:
{
"mcpServers": {
"skill-central": {
"command": "npx",
"args": ["-y", "@bobcgn/skill-central", "mcp"]
}
}
}
Note:
-ymust be the first element in the args array, before the package name.
Option B (Recommended for production): Run directly with node
Bypass npx entirely to eliminate the possibility of interactive prompts:
{
"mcpServers": {
"skill-central": {
"command": "node",
"args": ["/absolute/path/to/skill-central/dist/index.js", "mcp"]
}
}
}
Find the absolute path:
# Global install path
npm root -g
# Or find the specific package
npm list -g @bobcgn/skill-central --depth=0
Option C: Pre-install globally
npm install -g @bobcgn/skill-central
Then use skill-central mcp directly as the command in your config — no npx needed.
Issue 2: console.log Pollutes stdout (Strictly Prohibited)
Symptom
Developers instinctively add debug logging:
// ❌ NEVER do this
console.log("Starting MCP server...");
console.log("Loaded 12 skills");
console.log("Connection established");
Why It's Fatal
In MCP stdio mode, stdout is a pure JSON-RPC channel. The client reads every line from stdout expecting valid JSON-RPC messages. A single line of non-JSON text (like "Starting MCP server...") immediately breaks the protocol frame, causing:
- JSON parser throws an exception
- Client disconnects
- Tool list returns empty
- In some implementations, previously valid messages are also discarded
Solution
Hard rule: ALL debug/status output MUST use console.error() — never output non-protocol data to stdout.
// ✅ Correct: output to stderr, does not affect JSON-RPC channel
console.error("[skill-central] Starting MCP server...");
console.error("[skill-central] Loaded 12 skills");
// ❌ Wrong: pollutes stdout, breaks JSON-RPC protocol
console.log("Starting MCP server...");
Third-party libraries using console.log are equally dangerous. If a dependency you import uses console.log internally, it will also pollute stdout. skill-central includes built-in protection in MCP mode (redirecting console.log to stderr), but if you build on this project for二次开发, you must be vigilant about this risk.
Architecture: Why stdio Mode Is So Fragile
┌──────────────────────────────────────────────────────────┐
│ MCP Client (Cursor / Windsurf / Claude) │
│ │
│ Read stdout ──→ JSON Parser ──→ Tool / Prompt List │
│ Write stdin ──→ JSON-RPC Request │
└──────────────┬───────────────────────┬───────────────────┘
│ stdin │ stdout
▼ ▲
┌──────────────────────────────────────────────────────────┐
│ MCP Server Process │
│ │
│ stdin ──→ Protocol Handler ──→ JSON-RPC Response ──→ stdout
│ │
│ ⚠️ Any console.log / process log / npx prompt │
│ will leak into stdout and break the protocol frame │
└──────────────────────────────────────────────────────────┘
The core constraint: stdio is a byte stream channel with no message boundaries. The JSON-RPC protocol uses newlines to delimit messages, and the client parses line by line. Once a single line is not valid JSON, all subsequent messages will be misaligned or discarded outright. That's why a single console.log can kill an entire connection.
Contrast with HTTP mode: If MCP ever supports HTTP/SSE transport, this problem is greatly mitigated (HTTP has natural message boundaries and multiplexing). But under the current stdio mode, this is an iron rule every MCP service developer must strictly follow.
Quick Diagnostic Reference
| Symptom | Likely Cause | Fix |
|---|---|---|
| Server starts but no response / empty tool list | stdout contaminated with non-JSON data | Check all console.log calls — change to console.error(). Confirm -y is in npx args. |
| IDE can't connect | Wrong command in MCP config | Use npx -y @bobcgn/skill-central mcp or specify an absolute path. |
| First connection works, fails on restart | npx cache exists but package version updated | Clear npx cache: npx clear-npx-cache, or use -y to force update. |
| Skills not loading | YAML syntax error | Run skill-central doctor to see parse errors with file paths. |
| Tag composition returns empty | Tags missing or mismatched | Verify skill YAML has tags:. Use list --tag X to confirm. Pass comma-separated: "tags":"kmp,android". |
| Tool call returns error | Missing or invalid arguments | Check the skill's inputSchema.required field. Arguments are validated against declared types. |
| Web board refuses to bind 0.0.0.0 | Footgun guard | Pass --i-understand-nonlocal if you really mean it. |
install github:... returns 404 |
Wrong path / branch | Verify the path exists on the remote at that ref. |
Package X has no "skill-central.paths" |
npm package author omitted the manifest field | Author must declare "skill-central": { "paths": [...] } in package.json. |
| Web board shows "web assets not found" | Forgot npm run build:web |
Run the build step, then retry board. |
Documentation
Detailed reference pages live under docs/:
docs/en/cli-reference.md/docs/ch/cli-reference.md— every command, every flagdocs/en/web-board.md/docs/ch/web-board.md— web dashboard walkthrough + APIdocs/en/remote-sources.md/docs/ch/remote-sources.md— source URL grammar + manifestdocs/en/skill-schema.md/docs/ch/skill-schema.md—SkillSchemafield referencedocs/en/layered-override.md/docs/ch/layered-override.md— layer mechanicsdocs/en/mcp-protocol.md/docs/ch/mcp-protocol.md— JSON-RPC examples
Release history in CHANGELOG.md. Pre-publish verification checklist: docs/en/release-testing.md / docs/ch/release-testing.md. Recommended release path (tag push → npm publish with provenance + GitHub Release, all automated): docs/en/trusted-publishing.md / docs/ch/trusted-publishing.md. Fallback if Trusted Publishing is not yet configured: docs/en/manual-publishing.md / docs/ch/manual-publishing.md.
Development
# Clone and set up
git clone https://github.com/BobcGn/skill-central.git
cd skill-central
npm install
# Dev commands
npm run dev:mcp # MCP server in watch mode
npm run dev:board # web board dashboard
npm run dev:init # (re)generate sample skills
# Build
npm run build # tsc → dist/
npm run build:web # copy static frontend to dist/web/
# Type-check only
npx tsc --noEmit
Tech Stack
| Component | Choice |
|---|---|
| Runtime | Node.js 22+ (ESM) |
| Language | TypeScript 5.8 (ES2022, NodeNext) |
| MCP SDK | @modelcontextprotocol/sdk ^1.9.0 |
| CLI | commander ^14.0.0 |
| YAML | js-yaml ^4.1.1 |
| Web server | hono ^4.12 + @hono/node-server ^2.0 |
| Tarball | tar-stream ^3.2 (npm install support) |
| Dev Runner | tsx ^4.19.3 |
License
MIT
Use as a Template & Sync Across Devices (Best Practice)
The most robust way to manage and sync your AI skills across multiple devices is to use this repository as a template for a private skill repository.
1. Create your private repository
- Click the "Use this template" button at the top of this repository to create your own private repository.
- Clone it to your local machine:
git clone https://github.com/YOUR_USERNAME/skill-central.git - Enter the directory:
cd skill-central
2. Isolate your skills in a dedicated branch
To avoid conflicts when pulling future engine updates from the upstream repository, keep the main branch clean (it acts as the engine template) and store your personal skills in a separate branch (e.g., my-skills):
# Create and switch to your skills branch
git checkout -b my-skills
# Modify .gitignore to allow tracking your skills
# Remove or comment out the '.skills/' and 'skill-central.yaml' lines in .gitignore
# Add your skills and commit
git add .gitignore .skills/ skill-central.yaml
git commit -m "feat: track personal skills"
git push -u origin my-skills
3. Initialize and Link on a New Device
When setting up a new device, follow these steps to perfectly restore your environment:
# 1. Clone your private repository
git clone https://github.com/YOUR_USERNAME/skill-central.git ~/Projects/skill-central
cd ~/Projects/skill-central
# 2. Switch to your skills branch
git checkout my-skills
# 3. Install dependencies and build
npm install
npm run build && npm run build:web
# 4. Expose the command globally
npm link
# 5. Set as your global skill library (CRITICAL for IDEs like Claude to find your skills)
ln -s "$(pwd)" ~/.skill-central
After step 5, any MCP client (like Claude Desktop) will automatically discover your skills when it launches skill-central mcp, no matter what directory it runs from.