usage docs

This commit is contained in:
John Lancaster
2026-06-18 23:50:37 -05:00
parent 1254cc5432
commit 59c638c634
5 changed files with 514 additions and 0 deletions
+121
View File
@@ -4,6 +4,8 @@ from typing import Any
import yaml
from fastmcp import FastMCP
from personal_mcp.skills.document_loader import load_skill_document
catalog_server = FastMCP("catalog")
@@ -49,6 +51,66 @@ def _normalized_patterns() -> list[dict[str, Any]]:
]
def _matches_query(pattern: dict[str, Any], query: str) -> bool:
if not query:
return True
lowered = query.strip().lower()
if not lowered:
return True
haystack = " ".join(
[
str(pattern.get("id", "")),
str(pattern.get("namespace", "")),
str(pattern.get("name", "")),
str(pattern.get("description", "")),
" ".join(str(tag) for tag in pattern.get("tags", [])),
]
).lower()
return lowered in haystack
def _matches_tags(pattern: dict[str, Any], tags: list[str] | None) -> bool:
if not tags:
return True
requested = [tag.strip().lower() for tag in tags if tag and tag.strip()]
if not requested:
return True
pattern_tags = {str(tag).lower() for tag in pattern.get("tags", [])}
return all(tag in pattern_tags for tag in requested)
def _resolve_skill_slug(*, skill_id: str, namespace: str, metadata: dict[str, Any]) -> str:
candidates: list[str] = []
slug = metadata.get("slug")
if isinstance(slug, str) and slug.strip():
candidates.append(slug.strip())
candidates.extend(
[
skill_id,
namespace.replace("_", "-"),
namespace,
]
)
seen: set[str] = set()
for candidate in candidates:
if candidate in seen:
continue
seen.add(candidate)
try:
load_skill_document(skill_id=skill_id, skill_slug=candidate)
return candidate
except FileNotFoundError:
continue
return skill_id
@catalog_server.resource("resource://catalog/skills_index")
def skills_index() -> dict[str, Any]:
"""Return a compact discovery index for all available pattern modules."""
@@ -74,3 +136,62 @@ def patterns_by_id() -> dict[str, Any]:
for pattern in _normalized_patterns():
indexed[pattern["id"]] = pattern
return {"patterns_by_id": indexed}
@catalog_server.tool
def search_patterns(
query: str = "",
tags: list[str] | None = None,
skip: int = 0,
limit: int = 20,
) -> dict[str, Any]:
"""Search normalized pattern metadata with optional tags and pagination."""
normalized_skip = max(skip, 0)
normalized_limit = min(max(limit, 1), 100)
matches = [
pattern
for pattern in _normalized_patterns()
if _matches_query(pattern, query) and _matches_tags(pattern, tags)
]
page = matches[normalized_skip : normalized_skip + normalized_limit]
return {
"patterns": page,
"total": len(matches),
"skip": normalized_skip,
"limit": normalized_limit,
}
@catalog_server.tool
def get_pattern_by_id(id: str) -> dict[str, Any]:
"""Return one normalized pattern by stable id."""
for pattern in _normalized_patterns():
if pattern["id"] == id:
return {"found": True, "pattern": pattern}
return {"found": False, "id": id}
@catalog_server.tool
def get_skill_document_by_id(skill_id: str) -> dict[str, Any]:
"""Return the canonical skill document payload for a stable skill id."""
registry = _load_skill_registry()
for namespace, entry in registry.items():
metadata = entry.get("metadata", {})
pattern_id = metadata.get("id", namespace)
if pattern_id != skill_id:
continue
skill_slug = _resolve_skill_slug(
skill_id=skill_id,
namespace=namespace,
metadata=metadata,
)
return {
"found": True,
"document": load_skill_document(skill_id=skill_id, skill_slug=skill_slug),
}
return {"found": False, "id": skill_id}