doc updates
This commit is contained in:
+15
-5
@@ -16,11 +16,17 @@ The system is complete in three layers:
|
||||
2. Catalog resources provide normalized discovery.
|
||||
3. Zensical builds a static site from those same Markdown sources and the FastAPI app serves it in the FastMCP runtime process.
|
||||
|
||||
For Phase 1, this architecture is anchored by three contracts:
|
||||
This architecture is anchored by three contracts:
|
||||
|
||||
1. Step 1: docs-first authored content contract under `docs/` with strict per-skill ownership.
|
||||
2. Step 2: SKILL.md frontmatter contract with Anthropic fields plus `x-personal-mcp` metadata.
|
||||
3. Step 3: canonical resource URI contract with break-and-replace policy for contract changes.
|
||||
1. Docs-first authored content contract under `docs/` with strict per-skill ownership.
|
||||
2. `SKILL.md` frontmatter contract with Anthropic fields plus `x-personal-mcp` metadata.
|
||||
3. Canonical resource URI contract with break-and-replace policy for contract changes.
|
||||
|
||||
Detailed contract pages:
|
||||
|
||||
1. [Content Contract](./content.md)
|
||||
2. [Frontmatter Contract](./frontmatter.md)
|
||||
3. [URI Contract](./uris.md)
|
||||
|
||||
This architecture keeps authored content human-friendly while preserving machine-stable contracts.
|
||||
|
||||
@@ -62,7 +68,7 @@ Only canonical catalog resources are part of the runtime contract in this phase.
|
||||
|
||||
### Registry Loader
|
||||
|
||||
Phase 2 runtime composition introduces a startup registry loader that reads packaged docs resources using `importlib.resources.files(...)` and `Traversable` APIs.
|
||||
The runtime composition includes a startup registry loader that reads packaged docs resources using `importlib.resources.files(...)` and `Traversable` APIs.
|
||||
|
||||
Loader responsibilities:
|
||||
|
||||
@@ -104,6 +110,8 @@ flowchart TD
|
||||
|
||||
Each skill declares frontmatter in `docs/skills/<skill-id>/SKILL.md`.
|
||||
|
||||
For the full field-level contract, validation model, and FastMCP metadata mapping, see [Frontmatter Contract](./frontmatter.md).
|
||||
|
||||
Anthropic-facing required fields:
|
||||
|
||||
1. name
|
||||
@@ -124,6 +132,8 @@ No `metadata.yaml` sidecar is part of the end-state contract.
|
||||
|
||||
Canonical resource URIs are:
|
||||
|
||||
For the full URI semantics, parameter validation rules, and compatibility policy, see [URI Contract](./uris.md).
|
||||
|
||||
1. resource://skills/<skill_id>/document
|
||||
2. resource://skills/<skill_id>/references/<ref_id>
|
||||
3. resource://catalog/skills_index
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
# Content Contract
|
||||
|
||||
This page defines the authored content contract for the docs-first MCP architecture.
|
||||
|
||||
## Canonical Source Of Truth
|
||||
|
||||
1. All authored Markdown lives under `docs/`.
|
||||
2. MCP resources and static docs are two distribution surfaces of the same authored files.
|
||||
3. No parallel authored markdown is allowed in `src/` or other package-only paths.
|
||||
|
||||
## Canonical Skill Shape
|
||||
|
||||
Each skill is one directory under `docs/skills/`:
|
||||
|
||||
```text
|
||||
docs/
|
||||
skills/
|
||||
<skill-id>/
|
||||
SKILL.md
|
||||
references/
|
||||
... (one or more markdown files, optional nested folders)
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
1. `SKILL.md` is required for every skill.
|
||||
2. `references/` is the only place for skill-specific supporting docs.
|
||||
3. Nested folders inside `references/` are allowed so a skill can reorganize internals without changing global architecture.
|
||||
4. Skill directories are independent ownership boundaries; no cross-skill file writes.
|
||||
|
||||
## File Placement And Ownership Boundaries
|
||||
|
||||
1. Top-level project docs stay in `docs/*.md`.
|
||||
2. Skill docs stay in `docs/skills/<skill-id>/...`.
|
||||
3. A skill may link to other skills, but must not store content inside another skill's directory.
|
||||
4. Server and runtime code may index and serve docs, but must not be the source of authored markdown.
|
||||
|
||||
## Metadata Location Constraint
|
||||
|
||||
1. Skill metadata is embedded in YAML frontmatter in `SKILL.md`.
|
||||
2. No `metadata.yaml` sidecar exists in the end state.
|
||||
3. Reference lookup metadata, including reference id to relative path mappings, is declared from `SKILL.md` frontmatter rather than inferred as a hidden global convention.
|
||||
|
||||
## Skill Id Contract
|
||||
|
||||
`skill-id` is the public identifier and should satisfy all rules below:
|
||||
|
||||
1. Format: lowercase kebab-case only.
|
||||
2. Character set: `a-z`, `0-9`, and `-`.
|
||||
3. Must start with a letter.
|
||||
4. No underscores, spaces, dots, or uppercase characters.
|
||||
5. Directory name should equal `skill-id` in each committed revision.
|
||||
6. Frontmatter `id` should equal directory name in each committed revision.
|
||||
7. Treat `skill-id` as immutable after release; any rename is a breaking replacement and clients must move to the new id.
|
||||
|
||||
Valid examples:
|
||||
|
||||
1. `fastapi-uv-docker`
|
||||
2. `zensical-docs`
|
||||
3. `pytest-scaffolding`
|
||||
|
||||
Invalid examples:
|
||||
|
||||
1. `fastapi_uv_docker`
|
||||
2. `Zensical-Docs`
|
||||
3. `docs.zensical`
|
||||
|
||||
## Invariants
|
||||
|
||||
This contract guarantees:
|
||||
|
||||
1. One authored source tree in `docs/` for both website and MCP.
|
||||
2. One skill directory maps to one skill identity per revision.
|
||||
3. Namespace and slug drift is minimized by keeping directory and frontmatter ids aligned per revision.
|
||||
4. Per-skill reference structure can evolve without changing cross-skill architecture.
|
||||
5. Packaging for stdio is deterministic because authored content is path-stable.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
This contract does not define:
|
||||
|
||||
1. URI versioning policy details.
|
||||
2. The full frontmatter schema.
|
||||
3. Migration instructions from the current architecture.
|
||||
@@ -0,0 +1,309 @@
|
||||
# Frontmatter Contract
|
||||
|
||||
This page defines the `SKILL.md` frontmatter and FastMCP metadata contract.
|
||||
|
||||
## Anthropic Frontmatter Support
|
||||
|
||||
Across Anthropic API and Agent Skills surfaces:
|
||||
|
||||
1. Required fields for custom skill bundles are `name` and `description`.
|
||||
2. `name` must be 1-64 characters, lowercase letters, numbers, and hyphens only, with no XML tags, and must not use the reserved words `anthropic` or `claude`.
|
||||
3. `description` must be 1-1024 characters, non-empty, and contain no XML tags.
|
||||
|
||||
Portable optional fields from the Agent Skills specification:
|
||||
|
||||
1. `license`
|
||||
2. `compatibility`
|
||||
3. `metadata`
|
||||
4. `allowed-tools`
|
||||
|
||||
Claude Code-specific optional fields:
|
||||
|
||||
1. `when_to_use`
|
||||
2. `argument-hint`
|
||||
3. `arguments`
|
||||
4. `disable-model-invocation`
|
||||
5. `user-invocable`
|
||||
6. `allowed-tools`
|
||||
7. `disallowed-tools`
|
||||
8. `model`
|
||||
9. `effort`
|
||||
10. `context`
|
||||
11. `agent`
|
||||
12. `hooks`
|
||||
13. `paths`
|
||||
14. `shell`
|
||||
|
||||
Repository contract decisions:
|
||||
|
||||
1. Treat `name` and `description` as required in all `SKILL.md` files.
|
||||
2. Keep Anthropic-facing semantics in standard fields.
|
||||
3. Keep MCP indexing metadata in a namespaced extension block.
|
||||
4. Preserve forward compatibility by allowing additive optional metadata fields over time.
|
||||
|
||||
## Canonical Frontmatter Schema
|
||||
|
||||
Use this two-layer pattern:
|
||||
|
||||
1. Anthropic layer: top-level fields intended for Anthropic and Agent Skills behavior.
|
||||
2. Repository layer: one namespaced block, `x-personal-mcp`, for MCP catalog and routing metadata.
|
||||
|
||||
Canonical shape:
|
||||
|
||||
```yaml
|
||||
---
|
||||
name: <skill-id>
|
||||
description: <what this skill does and when to use it>
|
||||
|
||||
# Optional Anthropic and Agent Skills fields
|
||||
when_to_use: <extra trigger guidance>
|
||||
allowed-tools: <space-separated string or YAML list>
|
||||
disable-model-invocation: false
|
||||
user-invocable: true
|
||||
license: <optional>
|
||||
compatibility: <optional>
|
||||
|
||||
# Repository-specific metadata
|
||||
x-personal-mcp:
|
||||
id: <skill-id>
|
||||
version: <semver>
|
||||
tags:
|
||||
- <tag>
|
||||
capabilities:
|
||||
- resource://skills/<skill-id>/document
|
||||
depends_on: []
|
||||
references:
|
||||
<ref-id>:
|
||||
path: references/<file>.md
|
||||
mime_type: text/markdown
|
||||
title: <short title>
|
||||
---
|
||||
```
|
||||
|
||||
## Repository Metadata Field Rules
|
||||
|
||||
Rules for `x-personal-mcp`:
|
||||
|
||||
1. `id` is required, must follow the skill id rules from the content contract, and must equal the directory name.
|
||||
2. `version` is required and must be a semantic version string.
|
||||
3. `tags` is optional and should be a list of kebab-case discovery labels.
|
||||
4. `capabilities` is required and lists the MCP URIs the skill publishes.
|
||||
5. `depends_on` is optional and lists other skill ids.
|
||||
6. `references` is an optional map keyed by `ref-id`.
|
||||
|
||||
Reference entry rules:
|
||||
|
||||
1. `ref-id` is lowercase kebab-case.
|
||||
2. `path` is a skill-relative markdown path and must stay inside the same skill directory.
|
||||
3. Nested folders under `references/` are allowed.
|
||||
4. `mime_type` defaults to `text/markdown` when omitted.
|
||||
5. `title` is an optional display label.
|
||||
6. Renaming `ref-id` values is allowed when needed; optional aliases may be used during transitions.
|
||||
|
||||
## Validation Models
|
||||
|
||||
The normative model uses Pydantic v2 with change-friendly validation:
|
||||
|
||||
```python
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from pathlib import PurePosixPath
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
||||
|
||||
SKILL_ID_RE = re.compile(r"^[a-z][a-z0-9-]*$")
|
||||
SEMVER_RE = re.compile(r"^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:[-+][0-9A-Za-z.-]+)?$")
|
||||
|
||||
|
||||
class ReferenceEntry(BaseModel):
|
||||
model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
|
||||
|
||||
path: str
|
||||
mime_type: str = "text/markdown"
|
||||
title: str | None = None
|
||||
|
||||
@field_validator("path")
|
||||
@classmethod
|
||||
def validate_reference_path(cls, value: str) -> str:
|
||||
p = PurePosixPath(value)
|
||||
if p.is_absolute() or ".." in p.parts:
|
||||
raise ValueError("reference path must be a relative in-skill path")
|
||||
if not str(p).startswith("references/"):
|
||||
raise ValueError("reference path must stay under references/")
|
||||
if p.suffix.lower() != ".md":
|
||||
raise ValueError("reference path must target a markdown file")
|
||||
return str(p)
|
||||
|
||||
|
||||
class PersonalMcpMetadata(BaseModel):
|
||||
model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
|
||||
|
||||
id: str
|
||||
version: str
|
||||
tags: list[str] = Field(default_factory=list)
|
||||
capabilities: list[str] = Field(min_length=1)
|
||||
depends_on: list[str] = Field(default_factory=list)
|
||||
references: dict[str, ReferenceEntry] = Field(default_factory=dict)
|
||||
|
||||
@field_validator("id")
|
||||
@classmethod
|
||||
def validate_id(cls, value: str) -> str:
|
||||
if not SKILL_ID_RE.fullmatch(value):
|
||||
raise ValueError("id must be lowercase kebab-case and start with a letter")
|
||||
return value
|
||||
|
||||
@field_validator("version")
|
||||
@classmethod
|
||||
def validate_version(cls, value: str) -> str:
|
||||
if not SEMVER_RE.fullmatch(value):
|
||||
raise ValueError("version must be semver")
|
||||
return value
|
||||
|
||||
@field_validator("depends_on")
|
||||
@classmethod
|
||||
def validate_depends_on(cls, value: list[str]) -> list[str]:
|
||||
for dep in value:
|
||||
if not SKILL_ID_RE.fullmatch(dep):
|
||||
raise ValueError(f"invalid depends_on skill id: {dep}")
|
||||
return value
|
||||
|
||||
@field_validator("references")
|
||||
@classmethod
|
||||
def validate_reference_ids(cls, value: dict[str, ReferenceEntry]) -> dict[str, ReferenceEntry]:
|
||||
for ref_id in value:
|
||||
if not SKILL_ID_RE.fullmatch(ref_id):
|
||||
raise ValueError(f"invalid reference id: {ref_id}")
|
||||
return value
|
||||
|
||||
@model_validator(mode="after")
|
||||
def ensure_primary_capability(self) -> "PersonalMcpMetadata":
|
||||
expected = f"resource://skills/{self.id}/document"
|
||||
if expected not in self.capabilities:
|
||||
raise ValueError(f"capabilities must include {expected}")
|
||||
return self
|
||||
|
||||
|
||||
class SkillFrontmatter(BaseModel):
|
||||
model_config = ConfigDict(extra="ignore", str_strip_whitespace=True)
|
||||
|
||||
name: str = Field(min_length=1, max_length=64)
|
||||
description: str = Field(min_length=1, max_length=1024)
|
||||
when_to_use: str | None = None
|
||||
allowed_tools: str | list[str] | None = Field(default=None, alias="allowed-tools")
|
||||
disallowed_tools: str | list[str] | None = Field(default=None, alias="disallowed-tools")
|
||||
disable_model_invocation: bool | None = Field(default=None, alias="disable-model-invocation")
|
||||
user_invocable: bool | None = Field(default=None, alias="user-invocable")
|
||||
argument_hint: str | None = Field(default=None, alias="argument-hint")
|
||||
arguments: str | list[str] | None = None
|
||||
license: str | None = None
|
||||
compatibility: str | None = None
|
||||
metadata: dict[str, str] | None = None
|
||||
|
||||
x_personal_mcp: PersonalMcpMetadata = Field(alias="x-personal-mcp")
|
||||
|
||||
@field_validator("name")
|
||||
@classmethod
|
||||
def validate_name(cls, value: str) -> str:
|
||||
if not SKILL_ID_RE.fullmatch(value):
|
||||
raise ValueError("name must be lowercase kebab-case and start with a letter")
|
||||
if "anthropic" in value or "claude" in value:
|
||||
raise ValueError("name must not contain reserved words anthropic or claude")
|
||||
return value
|
||||
|
||||
@model_validator(mode="after")
|
||||
def cross_validate(self) -> "SkillFrontmatter":
|
||||
if self.x_personal_mcp.id != self.name:
|
||||
raise ValueError("x-personal-mcp.id must exactly match name")
|
||||
return self
|
||||
|
||||
|
||||
def validate_skill_frontmatter(raw: dict[str, Any], skill_dir_name: str) -> SkillFrontmatter:
|
||||
model = SkillFrontmatter.model_validate(raw)
|
||||
if model.name != skill_dir_name:
|
||||
raise ValueError("frontmatter name must exactly match skill directory name")
|
||||
return model
|
||||
```
|
||||
|
||||
Validation behavior contract:
|
||||
|
||||
1. Validate required core fields and relationships during registry load before FastMCP resource or tool registration.
|
||||
2. Allow unknown additive fields so frontmatter can evolve without blocking startup.
|
||||
3. Treat hard contract violations, including missing required fields, invalid ids, and broken required mappings, as startup errors.
|
||||
4. Treat non-critical compatibility issues as warnings when possible.
|
||||
5. Error messages should include the skill path and failing field for CI readability.
|
||||
|
||||
Projection mode contract for Anthropic API upload pipelines:
|
||||
|
||||
1. Parse with `SkillFrontmatter` first.
|
||||
2. Emit Anthropic-safe frontmatter with standard fields only.
|
||||
3. Serialize repository metadata into standard `metadata` as namespaced keys.
|
||||
4. Preserve the canonical authored source in `x-personal-mcp`; projection output is a build artifact.
|
||||
|
||||
## Anthropic Upload Compatibility Rule
|
||||
|
||||
1. Anthropic documentation guarantees behavior for standard frontmatter fields but does not explicitly guarantee handling of arbitrary unknown top-level keys.
|
||||
2. Publishing pipelines that target strict API compatibility should support a projection mode that emits only standard frontmatter fields for upload.
|
||||
3. In projection mode, repository extension metadata is serialized into the standard `metadata` field as namespaced keys or JSON-encoded values, while source-of-truth authoring remains in `x-personal-mcp`.
|
||||
|
||||
## FastMCP Native Metadata Surfaces
|
||||
|
||||
Resources support native definition metadata:
|
||||
|
||||
1. `name`
|
||||
2. `description`
|
||||
3. `mime_type`
|
||||
4. `tags`
|
||||
5. `annotations`, including `readOnlyHint` and `idempotentHint`
|
||||
6. `icons`
|
||||
7. `meta`
|
||||
8. `version`
|
||||
9. `enabled`, which is deprecated in FastMCP v3 in favor of server-level enable and disable controls
|
||||
|
||||
Resources also support runtime metadata through `ResourceContent.meta` and `ResourceResult.meta`.
|
||||
|
||||
Tools support native definition metadata:
|
||||
|
||||
1. `name`
|
||||
2. `description`
|
||||
3. `tags`
|
||||
4. `annotations`, including `title`, `readOnlyHint`, `destructiveHint`, `idempotentHint`, and `openWorldHint`
|
||||
5. `icons`
|
||||
6. `meta`
|
||||
7. `version`
|
||||
8. `timeout`
|
||||
9. `output_schema`
|
||||
10. `run_in_thread`
|
||||
11. `enabled`, which is deprecated in FastMCP v3 in favor of server-level enable and disable controls
|
||||
|
||||
Tools also support runtime metadata through `ToolResult.meta`.
|
||||
|
||||
## Frontmatter To FastMCP Mapping Contract
|
||||
|
||||
At server startup, map `x-personal-mcp` into FastMCP registration as follows:
|
||||
|
||||
1. `x-personal-mcp.id` defines the canonical URI namespace and identity checks.
|
||||
2. `description` becomes the default description for the primary skill document resource.
|
||||
3. `x-personal-mcp.tags` maps to resource and tool tags.
|
||||
4. `x-personal-mcp.version` maps to resource and tool version metadata.
|
||||
5. `x-personal-mcp.capabilities` becomes the registered URI list and catalog exposure.
|
||||
6. `x-personal-mcp.references[*]` becomes resource templates or concrete resources with `mime_type`, read-only annotations, and `meta` that includes `skill_id`, `ref_id`, and source `path`.
|
||||
7. `x-personal-mcp.depends_on` becomes catalog dependency graph metadata and validation inputs.
|
||||
|
||||
## Invariants
|
||||
|
||||
This contract guarantees:
|
||||
|
||||
1. Anthropic-required frontmatter stays valid for custom skill upload and Claude Code loading.
|
||||
2. MCP-specific metadata remains embedded in `SKILL.md` frontmatter, with no `metadata.yaml` sidecar.
|
||||
3. FastMCP registration uses native metadata fields for resources and tools.
|
||||
4. Reference ids and metadata can evolve with low-friction updates while internal file layout under `references/` stays refactor-friendly.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
This contract does not define:
|
||||
|
||||
1. URI versioning and deprecation rollout policy details.
|
||||
2. Migration script design from existing `metadata.yaml` files.
|
||||
3. Runtime caching and indexing performance tuning.
|
||||
@@ -39,6 +39,9 @@ When the server is running, the health check is available at `/healthz` and the
|
||||
## Architecture
|
||||
|
||||
- [Resource-First Pattern Module Architecture](./architecture.md)
|
||||
- [Content Contract](./content.md)
|
||||
- [Frontmatter Contract](./frontmatter.md)
|
||||
- [URI Contract](./uris.md)
|
||||
- [Static Docs Hosting Pattern](./mcp_layout.md)
|
||||
- [Skill Usage Mechanics](./usage.md)
|
||||
- [Copilot MCP Mechanics](./copilot.md)
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
# FastMCP Greenfield Contracts (Steps 1-5)
|
||||
|
||||
## Step 1: End-State Content Contract
|
||||
|
||||
1. All authored markdown lives under docs/.
|
||||
2. Skill docs live under docs/skills/<skill-id>/.
|
||||
3. Canonical skill shape is:
|
||||
|
||||
```text
|
||||
docs/
|
||||
skills/
|
||||
<skill-id>/
|
||||
SKILL.md
|
||||
references/
|
||||
```
|
||||
|
||||
4. SKILL.md is required for every skill.
|
||||
5. references/ is the only place for skill-specific supporting docs.
|
||||
6. Skill directories are ownership boundaries.
|
||||
7. Skill id rules:
|
||||
- lowercase kebab-case
|
||||
- starts with a letter
|
||||
- directory name matches skill id
|
||||
- SKILL frontmatter id matches directory name
|
||||
|
||||
## Step 2: SKILL Frontmatter and Metadata Contract
|
||||
|
||||
1. name and description are required top-level frontmatter fields.
|
||||
2. Repository indexing metadata lives in x-personal-mcp.
|
||||
3. x-personal-mcp fields:
|
||||
- required: id, version, capabilities
|
||||
- optional: tags, depends_on, references
|
||||
4. references maps stable ref ids to skill-relative markdown paths under references/.
|
||||
5. metadata.yaml sidecars are not part of the canonical model.
|
||||
|
||||
## Step 3: URI Contract and Compatibility Policy
|
||||
|
||||
Canonical URI surface:
|
||||
|
||||
1. resource://catalog/skills_index
|
||||
2. resource://catalog/skills/{skill_id}
|
||||
3. resource://skills/{skill_id}/document
|
||||
4. resource://skills/{skill_id}/references/{ref_id}
|
||||
5. resource://docs/{path*}
|
||||
|
||||
Rules:
|
||||
|
||||
1. skill_id and ref_id are lowercase kebab-case.
|
||||
2. docs path is markdown-only and cannot traverse outside docs/.
|
||||
3. URI families are unversioned and canonical in this phase.
|
||||
4. Breaking changes use direct replacement with no compatibility aliases.
|
||||
|
||||
## Step 4: Docs Registry Loader Contract
|
||||
|
||||
1. Loader uses importlib.resources.files(...) and Traversable APIs.
|
||||
2. Startup validates SKILL frontmatter schema, id invariants, reference integrity, dependency graph, and URI uniqueness.
|
||||
3. Registry is immutable for request-time reads.
|
||||
4. Invalid docs state is a hard startup error.
|
||||
|
||||
## Step 5: Registry-Driven Resource Registration Contract
|
||||
|
||||
1. FastMCP resources are registered from the validated registry.
|
||||
2. RFC6570 templates are used for parameterized routes.
|
||||
3. Docs resources declare explicit MIME types.
|
||||
4. Docs resources include readOnlyHint and idempotentHint annotations.
|
||||
5. Duplicate registrations fail startup via strict duplicate policy.
|
||||
+3
-3
@@ -29,8 +29,10 @@ treeView-beta
|
||||
"docs"
|
||||
"index.md"
|
||||
"architecture.md"
|
||||
"content.md"
|
||||
"frontmatter.md"
|
||||
"mcp_layout.md"
|
||||
"mcp_contract_steps_1_5.md"
|
||||
"uris.md"
|
||||
"skills"
|
||||
"new-skill"
|
||||
"SKILL.md"
|
||||
@@ -153,8 +155,6 @@ When clients cannot attach MCP resources directly, thin catalog tools may retrie
|
||||
|
||||
## URI Compatibility Policy
|
||||
|
||||
This phase is a greenfield break-and-replace baseline.
|
||||
|
||||
1. Canonical URIs are the only supported URIs in this runtime.
|
||||
2. No backward-compatibility aliases or dual registration paths are maintained.
|
||||
3. Contract changes should update clients to canonical URIs directly.
|
||||
|
||||
+6
-4
@@ -1,8 +1,10 @@
|
||||
# Hooking Up a New Skill
|
||||
|
||||
Use this checklist to add a new skill in the Phase 1 docs-first model.
|
||||
Use this checklist to add a new skill in the docs-first model.
|
||||
|
||||
## Step 1 Contract: Canonical Skill Shape
|
||||
For the full contract details, see [Content Contract](./content.md), [Frontmatter Contract](./frontmatter.md), and [URI Contract](./uris.md).
|
||||
|
||||
## Canonical Skill Shape
|
||||
|
||||
Create one skill directory under `docs/skills/`:
|
||||
|
||||
@@ -22,7 +24,7 @@ Rules:
|
||||
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
|
||||
|
||||
`SKILL.md` frontmatter is authoritative for metadata.
|
||||
|
||||
@@ -74,7 +76,7 @@ Reference manifest rules:
|
||||
|
||||
No `metadata.yaml` sidecar is part of this model.
|
||||
|
||||
## Step 3 Contract: URI Surface
|
||||
## URI Surface
|
||||
|
||||
Canonical resource URIs for a skill:
|
||||
|
||||
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
# URI Contract
|
||||
|
||||
This page defines the canonical resource URI contract, template parameter rules, and compatibility policy.
|
||||
|
||||
## Canonical URI Surface
|
||||
|
||||
The public, preferred URIs are:
|
||||
|
||||
1. `resource://catalog/skills_index`
|
||||
2. `resource://catalog/skills/{skill_id}`
|
||||
3. `resource://skills/{skill_id}/document`
|
||||
4. `resource://skills/{skill_id}/references/{ref_id}`
|
||||
5. `resource://docs/{path*}`
|
||||
|
||||
Contract intent:
|
||||
|
||||
1. Catalog URIs are discovery surfaces.
|
||||
2. Skill URIs are the primary per-skill guidance surfaces.
|
||||
3. The docs wildcard URI is a direct authored-markdown access surface under `docs/`.
|
||||
|
||||
## URI Semantics
|
||||
|
||||
### `resource://catalog/skills_index`
|
||||
|
||||
1. Returns a compact list of skill records for discovery.
|
||||
2. Contains one entry per `skill_id`.
|
||||
3. Includes enough metadata for client-side selection, at minimum `id`, `name`, `description`, `tags`, and `capabilities`.
|
||||
|
||||
### `resource://catalog/skills/{skill_id}`
|
||||
|
||||
1. Returns one normalized record for `skill_id`.
|
||||
2. Includes the canonical document URI and declared reference ids.
|
||||
3. Returns not found when `skill_id` does not exist.
|
||||
|
||||
### `resource://skills/{skill_id}/document`
|
||||
|
||||
1. Returns the canonical `SKILL.md` authored content for that skill.
|
||||
2. `skill_id` must satisfy the stable skill id rules from the content contract.
|
||||
|
||||
### `resource://skills/{skill_id}/references/{ref_id}`
|
||||
|
||||
1. Returns one reference document declared in the skill frontmatter references manifest.
|
||||
2. `ref_id` is the stable public handle for that reference document.
|
||||
|
||||
### `resource://docs/{path*}`
|
||||
|
||||
1. Returns authored markdown at a normalized relative path under `docs/`.
|
||||
2. Supports nested paths via RFC6570 wildcard expansion.
|
||||
3. Typical examples include `index.md`, `usage.md`, `skills/<skill-id>/SKILL.md`, and `skills/<skill-id>/references/<file>.md`.
|
||||
|
||||
## Template Parameter And Validation Rules
|
||||
|
||||
### `skill_id`
|
||||
|
||||
1. Lowercase kebab-case.
|
||||
2. Must satisfy the stable skill id rules from the content contract.
|
||||
|
||||
### `ref_id`
|
||||
|
||||
1. Lowercase kebab-case.
|
||||
2. Must be declared in the skill's references manifest.
|
||||
|
||||
### `path*`
|
||||
|
||||
1. Relative POSIX path only.
|
||||
2. No leading slash.
|
||||
3. No `..` traversal segments.
|
||||
4. Resolves only inside `docs/`.
|
||||
5. Markdown-only in the end state, meaning `.md` files.
|
||||
|
||||
## URI Versioning Policy
|
||||
|
||||
Default rule:
|
||||
|
||||
1. Keep URIs unversioned by default.
|
||||
2. Allow URI and payload updates when they improve clarity or implementation simplicity.
|
||||
|
||||
Breaking-change rule:
|
||||
|
||||
1. Breaking changes use direct replacement of the canonical URI family.
|
||||
2. No compatibility aliases or dual URI families are maintained.
|
||||
|
||||
FastMCP version metadata usage:
|
||||
|
||||
1. Resource `version` metadata may be used for implementation and version discovery.
|
||||
2. URI readability and maintainability remain the primary contract.
|
||||
|
||||
## Reference Id Compatibility Policy
|
||||
|
||||
`ref_id` is the public identifier for a reference document, separate from file path.
|
||||
|
||||
Rules:
|
||||
|
||||
1. Prefer keeping `ref_id` stable when practical.
|
||||
2. File paths may change without URI churn as long as the mapped `ref_id` still resolves.
|
||||
3. If a reference is renamed, introduce a new `ref_id` and treat the old one as retired.
|
||||
4. Avoid reusing retired `ref_id` values for unrelated content.
|
||||
|
||||
## Invariants
|
||||
|
||||
This contract guarantees:
|
||||
|
||||
1. One canonical URI pattern per core capability surface.
|
||||
2. Fast, low-friction URI evolution through direct replacement of canonical URIs.
|
||||
3. A single canonical catalog URI family with no alias maintenance overhead.
|
||||
4. Reference mappings can evolve with minimal churn.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
This contract does not define:
|
||||
|
||||
1. Implementation-specific transform wiring details, such as `VersionFilter`, mounts, or provider composition.
|
||||
2. Migration script mechanics for auto-generating aliases.
|
||||
3. Authorization policy design for URI-level access control.
|
||||
+3
-1
@@ -48,9 +48,11 @@ nav = [
|
||||
{ "Home" = "index.md" },
|
||||
{ "Guide" = [
|
||||
{ "Arch" = "architecture.md" },
|
||||
{ "Content" = "content.md" },
|
||||
{ "Frontmatter" = "frontmatter.md" },
|
||||
{ "URIs" = "uris.md" },
|
||||
{ "MCP" = "mcp_layout.md" },
|
||||
{ "Copilot" = "copilot.md" },
|
||||
{ "Contracts 1-5" = "mcp_contract_steps_1_5.md" },
|
||||
{ "Usage" = "usage.md" },
|
||||
{ "Future Work" = "future_work.md" },
|
||||
{ "New Skill" = "new_skill.md" },
|
||||
|
||||
Reference in New Issue
Block a user