implemented steps 1-5
This commit is contained in:
+106
-165
@@ -1,178 +1,119 @@
|
||||
# Hooking Up a New Skill
|
||||
|
||||
Use this checklist after generating a new skill under `docs/skills/<slug>/`.
|
||||
Use this checklist to add a new skill in the Phase 1 docs-first model.
|
||||
|
||||
## Step 1 Contract: Canonical Skill Shape
|
||||
|
||||
Create one skill directory under `docs/skills/`:
|
||||
|
||||
```text
|
||||
docs/
|
||||
skills/
|
||||
<skill-id>/
|
||||
SKILL.md
|
||||
references/
|
||||
... (optional markdown files, nested folders allowed)
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
1. `SKILL.md` is required.
|
||||
2. All skill-specific supporting docs live under `references/`.
|
||||
3. Skill directories are ownership boundaries; no cross-skill writes.
|
||||
4. `skill-id` is lowercase kebab-case and should remain stable.
|
||||
|
||||
## Step 2 Contract: SKILL.md Frontmatter
|
||||
|
||||
`SKILL.md` frontmatter is authoritative for metadata.
|
||||
|
||||
Required top-level fields:
|
||||
|
||||
1. `name`
|
||||
2. `description`
|
||||
3. `x-personal-mcp`
|
||||
|
||||
Required `x-personal-mcp` fields:
|
||||
|
||||
1. `id`
|
||||
2. `version`
|
||||
3. `capabilities`
|
||||
|
||||
Optional `x-personal-mcp` fields:
|
||||
|
||||
1. `tags`
|
||||
2. `depends_on`
|
||||
3. `references`
|
||||
|
||||
Canonical frontmatter template:
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: <skill-id>
|
||||
description: <what this skill does and when to use it>
|
||||
|
||||
x-personal-mcp:
|
||||
id: <skill-id>
|
||||
version: 1.0.0
|
||||
tags: []
|
||||
capabilities:
|
||||
- resource://skills/<skill-id>/document
|
||||
depends_on: []
|
||||
references:
|
||||
<ref-id>:
|
||||
path: references/<file>.md
|
||||
mime_type: text/markdown
|
||||
title: <optional short title>
|
||||
---
|
||||
```
|
||||
|
||||
Reference manifest rules:
|
||||
|
||||
1. `ref-id` is lowercase kebab-case.
|
||||
2. `path` is skill-relative and must stay under `references/`.
|
||||
3. Reference paths are markdown files.
|
||||
|
||||
No `metadata.yaml` sidecar is part of this model.
|
||||
|
||||
## Step 3 Contract: URI Surface
|
||||
|
||||
Canonical resource URIs for a skill:
|
||||
|
||||
1. `resource://skills/<skill_id>/document`
|
||||
2. `resource://skills/<skill_id>/references/<ref_id>`
|
||||
|
||||
Canonical discovery URIs:
|
||||
|
||||
1. `resource://catalog/skills_index`
|
||||
2. `resource://catalog/skills/{skill_id}`
|
||||
|
||||
Docs passthrough URI:
|
||||
|
||||
1. `resource://docs/{path*}`
|
||||
|
||||
Compatibility rule:
|
||||
|
||||
1. Keep URI families unversioned by default.
|
||||
2. For breaking changes, update clients to the canonical replacement URIs directly.
|
||||
|
||||
## Checklist
|
||||
|
||||
1. Create the authored docs content.
|
||||
Add `docs/skills/<slug>/SKILL.md` and any companion files under `docs/skills/<slug>/references/`.
|
||||
|
||||
2. Choose the three names up front.
|
||||
Use a docs slug like `fastapi-uv-docker`, a resource id like `fastapi-uv-docker`, and a Python package name like `fastapi_uv_docker`.
|
||||
|
||||
3. Add the runtime package.
|
||||
Create `src/personal_mcp/skills/<python_namespace>/` with `__init__.py`, `server.py`, and `metadata.yaml`.
|
||||
|
||||
4. Expose the document resource in `server.py`.
|
||||
Follow the existing pattern: create a `FastMCP` instance, register `resource://skills/<skill-id>/document`, and return the shared loader result.
|
||||
For the default layout, load `docs/skills/<slug>/SKILL.md`.
|
||||
For special cases, set `document_path` in `metadata.yaml` to a repo-relative Markdown file and load from metadata instead of hardcoding a path in the server.
|
||||
|
||||
5. Register the catalog metadata.
|
||||
In `metadata.yaml`, add the skill `id`, `name`, `version`, `description`, `tags`, `capabilities`, and `depends_on`. The `capabilities` list should include `resource://skills/<skill-id>/document`.
|
||||
|
||||
6. Mount the skill in the root server.
|
||||
Import the new server in `src/personal_mcp/mcp.py` and add an `mcp.mount(...)` call with the Python namespace.
|
||||
|
||||
7. Let the loader and catalog do the rest.
|
||||
The document loader reads canonical Markdown from `docs/skills/<slug>/SKILL.md` by default, or from `metadata.yaml`'s optional `document_path` override when present. The catalog discovers metadata from `src/personal_mcp/skills/*/metadata.yaml` automatically.
|
||||
|
||||
8. Rebuild and smoke-test.
|
||||
Run `uv run zensical build` to publish the docs site, then run a quick Python check or start the app to confirm the new resource loads.
|
||||
|
||||
## Discovery Tool Policy
|
||||
|
||||
To keep behavior consistent across MCP clients and Copilot session types, follow this boundary:
|
||||
|
||||
1. Keep per-skill servers resource-only.
|
||||
2. Keep discovery/query tools centralized in the catalog server.
|
||||
3. Keep canonical content in `docs/skills/<slug>/SKILL.md` and expose it through `resource://skills/<skill-id>/document`.
|
||||
|
||||
### Do
|
||||
|
||||
1. Add or update `metadata.yaml` fields (`id`, `description`, `tags`, `capabilities`) so catalog discovery quality stays high.
|
||||
2. Use `document_path` when a skill should expose a Markdown file outside `docs/skills/<slug>/SKILL.md`.
|
||||
3. Use catalog resources as the primary discovery surface.
|
||||
4. Add thin, read-only catalog tools only when client behavior needs a fallback path.
|
||||
|
||||
### Don't
|
||||
|
||||
1. Do not add duplicate discovery tools to each skill package.
|
||||
2. Do not duplicate canonical skill guidance in tool descriptions.
|
||||
3. Do not create mutating catalog tools for skill discovery.
|
||||
|
||||
## Minimal Shape
|
||||
|
||||
- Docs content: `docs/skills/<slug>/SKILL.md`
|
||||
- Optional references: `docs/skills/<slug>/references/*.md`
|
||||
- Runtime package: `src/personal_mcp/skills/<python_namespace>/`
|
||||
- Resource URI: `resource://skills/<skill-id>/document`
|
||||
1. Create `docs/skills/<skill-id>/SKILL.md`.
|
||||
2. Add optional references under `docs/skills/<skill-id>/references/`.
|
||||
3. Populate frontmatter with `name`, `description`, and `x-personal-mcp` metadata.
|
||||
4. Ensure `x-personal-mcp.id` equals `name` and directory `<skill-id>`.
|
||||
5. Ensure `capabilities` includes `resource://skills/<skill-id>/document`.
|
||||
6. If references are exposed, declare each `ref-id` in `x-personal-mcp.references`.
|
||||
|
||||
## Quick Validation
|
||||
|
||||
1. Confirm the Markdown document resolves through the loader.
|
||||
`uv run python -c "from personal_mcp.skills.document_loader import load_skill_document; print(load_skill_document(skill_id='<skill-id>', skill_slug='<slug>')['source_path'])"`
|
||||
1. Confirm docs build succeeds:
|
||||
|
||||
2. Confirm the docs build still works.
|
||||
`uv run zensical build`
|
||||
|
||||
## server.py Template
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
from personal_mcp.skills.document_loader import load_skill_document
|
||||
|
||||
<python_namespace>_server = FastMCP("<skill-id>")
|
||||
|
||||
|
||||
@<python_namespace>_server.resource("resource://skills/<skill-id>/document")
|
||||
def skill_document() -> dict[str, str]:
|
||||
"""Return the canonical Markdown document for this skill."""
|
||||
return load_skill_document(
|
||||
skill_id="<skill-id>",
|
||||
skill_slug="<slug>",
|
||||
)
|
||||
```bash
|
||||
uv run zensical build
|
||||
```
|
||||
|
||||
## metadata.yaml Template
|
||||
2. Confirm tests succeed:
|
||||
|
||||
```yaml
|
||||
id: <skill-id>
|
||||
name: <Human Readable Name>
|
||||
version: 1.0.0
|
||||
description: <One sentence describing what the skill provides.>
|
||||
tags:
|
||||
- <tag-one>
|
||||
- <tag-two>
|
||||
document_path: <optional repo-relative path to a markdown file>
|
||||
capabilities:
|
||||
- resource://skills/<skill-id>/document
|
||||
depends_on: []
|
||||
```bash
|
||||
uv run pytest -q
|
||||
```
|
||||
|
||||
Omit `document_path` when the canonical document is `docs/skills/<slug>/SKILL.md`.
|
||||
|
||||
## Root Mount Template
|
||||
|
||||
Add an import in `src/personal_mcp/mcp.py`:
|
||||
|
||||
```python
|
||||
from personal_mcp.skills.<python_namespace>.server import <python_namespace>_server
|
||||
```
|
||||
|
||||
Add a mount call:
|
||||
|
||||
```python
|
||||
mcp.mount(<python_namespace>_server, namespace="<python_namespace>")
|
||||
```
|
||||
|
||||
## Example Scaffold
|
||||
|
||||
For a new skill called `sqlmodel-patterns`:
|
||||
|
||||
1. Docs content lives in `docs/skills/sqlmodel-patterns/SKILL.md`.
|
||||
2. The Python package lives in `src/personal_mcp/skills/sqlmodel_patterns/`.
|
||||
3. The resource id is `sqlmodel-patterns`.
|
||||
|
||||
Example `server.py`:
|
||||
|
||||
```python
|
||||
from fastmcp import FastMCP
|
||||
|
||||
from personal_mcp.skills.document_loader import load_skill_document
|
||||
|
||||
sqlmodel_patterns_server = FastMCP("sqlmodel-patterns")
|
||||
|
||||
|
||||
@sqlmodel_patterns_server.resource("resource://skills/sqlmodel-patterns/document")
|
||||
def skill_document() -> dict[str, str]:
|
||||
"""Return the canonical Markdown document for this skill."""
|
||||
return load_skill_document(
|
||||
skill_id="sqlmodel-patterns",
|
||||
skill_slug="sqlmodel-patterns",
|
||||
)
|
||||
```
|
||||
|
||||
Example `metadata.yaml`:
|
||||
|
||||
```yaml
|
||||
id: sqlmodel-patterns
|
||||
name: SQLModel Patterns
|
||||
version: 1.0.0
|
||||
description: Provide reusable patterns for building apps with SQLModel.
|
||||
tags:
|
||||
- sqlmodel
|
||||
- python
|
||||
- patterns
|
||||
capabilities:
|
||||
- resource://skills/sqlmodel-patterns/document
|
||||
depends_on: []
|
||||
```
|
||||
|
||||
Example `mcp.py` additions:
|
||||
|
||||
```python
|
||||
from personal_mcp.skills.sqlmodel_patterns.server import sqlmodel_patterns_server
|
||||
|
||||
mcp.mount(sqlmodel_patterns_server, namespace="sqlmodel_patterns")
|
||||
```
|
||||
|
||||
## Bootstrap Sequence
|
||||
|
||||
1. Create `docs/skills/<slug>/SKILL.md`.
|
||||
2. Copy the `server.py` template into `src/personal_mcp/skills/<python_namespace>/server.py`.
|
||||
3. Copy the `metadata.yaml` template into `src/personal_mcp/skills/<python_namespace>/metadata.yaml`.
|
||||
4. Add `__init__.py` in the new package directory.
|
||||
5. Import and mount the server in `src/personal_mcp/mcp.py`.
|
||||
6. Run the validation commands above.
|
||||
|
||||
Reference in New Issue
Block a user