Compare commits
2 Commits
dbaaad8df8
...
485b93b3c9
| Author | SHA1 | Date | |
|---|---|---|---|
| 485b93b3c9 | |||
| d54f427112 |
@@ -4,6 +4,7 @@ version = "0.1.0"
|
||||
requires-python = ">=3.12"
|
||||
dependencies = [
|
||||
"fastmcp>=2.10.0",
|
||||
"pyyaml>=6.0.2",
|
||||
"zensical>=0.0.45",
|
||||
]
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from personal_mcp.catalog.server import catalog_server
|
||||
|
||||
__all__ = ["catalog_server"]
|
||||
@@ -0,0 +1,76 @@
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
import yaml
|
||||
from fastmcp import FastMCP
|
||||
|
||||
catalog_server = FastMCP("catalog")
|
||||
|
||||
|
||||
def _skills_dir() -> Path:
|
||||
return Path(__file__).resolve().parents[1] / "skills"
|
||||
|
||||
|
||||
def _load_skill_registry() -> dict[str, Any]:
|
||||
registry: dict[str, Any] = {}
|
||||
for metadata_path in sorted(_skills_dir().glob("*/metadata.yaml")):
|
||||
with metadata_path.open("r", encoding="utf-8") as handle:
|
||||
metadata = yaml.safe_load(handle) or {}
|
||||
skill_key = metadata_path.parent.name
|
||||
registry[skill_key] = {
|
||||
"namespace": skill_key,
|
||||
"metadata": metadata,
|
||||
}
|
||||
return registry
|
||||
|
||||
|
||||
def _normalize_pattern(namespace: str, metadata: dict[str, Any]) -> dict[str, Any]:
|
||||
pattern_id = metadata.get("id", namespace)
|
||||
capabilities = metadata.get("capabilities", [])
|
||||
return {
|
||||
"id": pattern_id,
|
||||
"namespace": namespace,
|
||||
"name": metadata.get("name", pattern_id),
|
||||
"version": metadata.get("version", "0.1.0"),
|
||||
"description": metadata.get("description", ""),
|
||||
"tags": metadata.get("tags", []),
|
||||
"depends_on": metadata.get("depends_on", []),
|
||||
"capabilities": capabilities,
|
||||
# Expose resources explicitly for clients that treat resources as the primary interface.
|
||||
"resources": capabilities,
|
||||
}
|
||||
|
||||
|
||||
def _normalized_patterns() -> list[dict[str, Any]]:
|
||||
registry = _load_skill_registry()
|
||||
return [
|
||||
_normalize_pattern(namespace, entry["metadata"])
|
||||
for namespace, entry in registry.items()
|
||||
]
|
||||
|
||||
|
||||
@catalog_server.resource("resource://catalog/skills_index")
|
||||
def skills_index() -> dict[str, Any]:
|
||||
"""Return a compact discovery index for all available pattern modules."""
|
||||
return {"patterns": _normalized_patterns()}
|
||||
|
||||
|
||||
@catalog_server.resource("resource://catalog/skills_details")
|
||||
def skills_details() -> dict[str, Any]:
|
||||
"""Return full metadata for all mounted pattern modules."""
|
||||
return {"patterns": _load_skill_registry()}
|
||||
|
||||
|
||||
@catalog_server.resource("resource://catalog/patterns")
|
||||
def patterns() -> dict[str, Any]:
|
||||
"""Return normalized pattern records for resource-first clients."""
|
||||
return {"patterns": _normalized_patterns()}
|
||||
|
||||
|
||||
@catalog_server.resource("resource://catalog/patterns_by_id")
|
||||
def patterns_by_id() -> dict[str, Any]:
|
||||
"""Return normalized pattern records indexed by stable pattern id."""
|
||||
indexed: dict[str, Any] = {}
|
||||
for pattern in _normalized_patterns():
|
||||
indexed[pattern["id"]] = pattern
|
||||
return {"patterns_by_id": indexed}
|
||||
@@ -1,5 +1,6 @@
|
||||
from fastmcp import FastMCP
|
||||
|
||||
from personal_mcp.catalog.server import catalog_server
|
||||
from personal_mcp.skills.fastapi_uv_docker.server import fastapi_uv_docker_server
|
||||
from personal_mcp.skills.pytest_scaffolding.server import pytest_scaffolding_server
|
||||
from personal_mcp.skills.python_logging_dictconfig.server import (
|
||||
@@ -8,6 +9,7 @@ from personal_mcp.skills.python_logging_dictconfig.server import (
|
||||
|
||||
mcp = FastMCP("personal-mcp")
|
||||
|
||||
mcp.mount(catalog_server, namespace="catalog")
|
||||
mcp.mount(pytest_scaffolding_server, namespace="pytest_scaffolding")
|
||||
mcp.mount(python_logging_dictconfig_server, namespace="python_logging_dictconfig")
|
||||
mcp.mount(fastapi_uv_docker_server, namespace="fastapi_uv_docker")
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
id: fastapi-uv-docker
|
||||
name: FastAPI uv Docker
|
||||
version: 1.0.0
|
||||
description: Provide fast migration guidance to FastAPI plus uv plus Docker.
|
||||
tags:
|
||||
- fastapi
|
||||
- uv
|
||||
- docker
|
||||
capabilities:
|
||||
- resource://skills/fastapi_uv_docker/overview
|
||||
- resource://skills/fastapi_uv_docker/rules
|
||||
- resource://skills/fastapi_uv_docker/checklist
|
||||
- resource://skills/fastapi_uv_docker/references
|
||||
depends_on: []
|
||||
|
||||
@@ -1,7 +1,79 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastmcp import FastMCP
|
||||
|
||||
fastapi_uv_docker_server = FastMCP("fastapi-uv-docker")
|
||||
|
||||
_REFERENCE_DIR = (
|
||||
Path(__file__).resolve().parents[4]
|
||||
/ "skills"
|
||||
/ "fastapi-uv-docker"
|
||||
/ "references"
|
||||
)
|
||||
_REFERENCE_FILES = {
|
||||
"fastapi_best_practices": _REFERENCE_DIR / "fastapi-best-practices.md",
|
||||
"uv_project_layout": _REFERENCE_DIR / "uv-project-layout.md",
|
||||
"uvicorn_settings": _REFERENCE_DIR / "uvicorn-settings.md",
|
||||
"docker_cloud_native": _REFERENCE_DIR / "docker-cloud-native.md",
|
||||
}
|
||||
|
||||
|
||||
def _load_reference_bundle() -> dict[str, str]:
|
||||
bundle: dict[str, str] = {}
|
||||
for key, path in _REFERENCE_FILES.items():
|
||||
bundle[key] = path.read_text(encoding="utf-8")
|
||||
return bundle
|
||||
|
||||
|
||||
@fastapi_uv_docker_server.resource("resource://skills/fastapi_uv_docker/overview")
|
||||
def fastapi_overview() -> dict:
|
||||
"""Return high-level intent for FastAPI plus uv plus Docker migration."""
|
||||
return {
|
||||
"id": "fastapi-uv-docker",
|
||||
"intent": "Migrate projects toward cloud-native FastAPI architecture managed by uv.",
|
||||
"focus": [
|
||||
"src package layout",
|
||||
"uv-based dependency and lock management",
|
||||
"container-ready runtime conventions",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@fastapi_uv_docker_server.resource("resource://skills/fastapi_uv_docker/rules")
|
||||
def fastapi_rules() -> dict:
|
||||
"""Return migration rules for stable FastAPI service architecture."""
|
||||
return {
|
||||
"rules": [
|
||||
"Prefer src layout and installable package boundaries.",
|
||||
"Use app factory and lifespan hooks for startup/shutdown ownership.",
|
||||
"Keep runtime configuration in environment-backed settings.",
|
||||
"Use uv lockfile and avoid drift between local and container environments.",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@fastapi_uv_docker_server.resource("resource://skills/fastapi_uv_docker/checklist")
|
||||
def fastapi_checklist() -> dict:
|
||||
"""Return a compact migration checklist for FastAPI plus uv plus Docker."""
|
||||
return {
|
||||
"checklist": [
|
||||
"Audit current project manager, layout, and runtime settings.",
|
||||
"Establish uv project metadata and lockfile ownership.",
|
||||
"Implement app factory, lifespan, and health endpoints.",
|
||||
"Create multi-stage Docker image with non-root runtime user.",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@fastapi_uv_docker_server.resource("resource://skills/fastapi_uv_docker/references")
|
||||
def fastapi_references() -> dict:
|
||||
"""Return bundled references for detailed implementation guidance."""
|
||||
return {
|
||||
"sources": {key: str(path) for key, path in _REFERENCE_FILES.items()},
|
||||
"format": "markdown",
|
||||
"content": _load_reference_bundle(),
|
||||
}
|
||||
|
||||
|
||||
@fastapi_uv_docker_server.tool()
|
||||
def fastapi_uv_docker_mvp_checklist(current_state: str = "bare python project") -> list[str]:
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
id: pytest-scaffolding
|
||||
name: Pytest Scaffolding
|
||||
version: 1.0.0
|
||||
description: Scaffold a maintainable pytest structure quickly.
|
||||
tags:
|
||||
- pytest
|
||||
- testing
|
||||
- python
|
||||
capabilities:
|
||||
- resource://skills/pytest_scaffolding/overview
|
||||
- resource://skills/pytest_scaffolding/rules
|
||||
- resource://skills/pytest_scaffolding/checklist
|
||||
- resource://skills/pytest_scaffolding/references
|
||||
depends_on: []
|
||||
|
||||
@@ -1,7 +1,82 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastmcp import FastMCP
|
||||
|
||||
pytest_scaffolding_server = FastMCP("pytest-scaffolding")
|
||||
|
||||
_REFERENCE_PATH = (
|
||||
Path(__file__).resolve().parents[4]
|
||||
/ "skills"
|
||||
/ "pytest-scaffolding"
|
||||
/ "references"
|
||||
/ "pytest-docs.md"
|
||||
)
|
||||
|
||||
|
||||
def _load_reference_text() -> str:
|
||||
return _REFERENCE_PATH.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
@pytest_scaffolding_server.resource("resource://skills/pytest_scaffolding/overview")
|
||||
def pytest_overview() -> dict:
|
||||
"""Return the high-level intent and boundaries of this pattern module."""
|
||||
return {
|
||||
"id": "pytest-scaffolding",
|
||||
"intent": "Design maintainable pytest structure before deep test implementation.",
|
||||
"focus": [
|
||||
"hierarchical test layout",
|
||||
"dependency-aware fixture boundaries",
|
||||
"fast local feedback loops",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@pytest_scaffolding_server.resource("resource://skills/pytest_scaffolding/rules")
|
||||
def pytest_rules() -> dict:
|
||||
"""Return method rules to keep test architecture consistent."""
|
||||
return {
|
||||
"rules": [
|
||||
"Mirror major source package boundaries under tests/.",
|
||||
"Use explicit markers for integration, smoke, and external tests.",
|
||||
"Keep global fixtures lightweight; place expensive fixtures in subtree conftest files.",
|
||||
"Prefer deterministic unit tests and isolate slow or external dependencies.",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@pytest_scaffolding_server.resource("resource://skills/pytest_scaffolding/checklist")
|
||||
def pytest_checklist() -> dict:
|
||||
"""Return an execution checklist for first-pass test scaffolding."""
|
||||
return {
|
||||
"checklist": [
|
||||
"Map source modules to initial test modules.",
|
||||
"Classify each module as unit, integration, or smoke.",
|
||||
"Create baseline fixtures in tests/conftest.py.",
|
||||
"Register markers in pyproject.toml.",
|
||||
"Validate collection and run fast path first.",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@pytest_scaffolding_server.resource("resource://skills/pytest_scaffolding/references")
|
||||
def pytest_references() -> dict:
|
||||
"""Return canonical reference material for this pattern module."""
|
||||
return {
|
||||
"source": str(_REFERENCE_PATH),
|
||||
"format": "markdown",
|
||||
"content": _load_reference_text(),
|
||||
}
|
||||
|
||||
|
||||
@pytest_scaffolding_server.prompt()
|
||||
def scaffold_pytest_prompt(target_scope: str = "src/") -> str:
|
||||
"""Prompt template for planning pytest scaffolding for a target scope."""
|
||||
return (
|
||||
"Create a minimal pytest scaffold plan. "
|
||||
f"Target scope: {target_scope}. "
|
||||
"Return directory mapping, marker suggestions, and the first three tests to write."
|
||||
)
|
||||
|
||||
|
||||
@pytest_scaffolding_server.tool()
|
||||
def propose_pytest_mvp_tree(target_scope: str = "src/") -> str:
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
id: python-logging-dictconfig
|
||||
name: Python Logging DictConfig
|
||||
version: 1.0.0
|
||||
description: Provide minimal logging.config.dictConfig setup guidance.
|
||||
tags:
|
||||
- logging
|
||||
- python
|
||||
- observability
|
||||
capabilities:
|
||||
- resource://skills/python_logging_dictconfig/overview
|
||||
- resource://skills/python_logging_dictconfig/rules
|
||||
- resource://skills/python_logging_dictconfig/checklist
|
||||
- resource://skills/python_logging_dictconfig/references
|
||||
depends_on: []
|
||||
|
||||
@@ -1,7 +1,71 @@
|
||||
from pathlib import Path
|
||||
|
||||
from fastmcp import FastMCP
|
||||
|
||||
python_logging_dictconfig_server = FastMCP("python-logging-dictconfig")
|
||||
|
||||
_REFERENCE_PATH = (
|
||||
Path(__file__).resolve().parents[4]
|
||||
/ "skills"
|
||||
/ "python-logging-dictconfig"
|
||||
/ "references"
|
||||
/ "python-logging-docs.md"
|
||||
)
|
||||
|
||||
|
||||
def _load_reference_text() -> str:
|
||||
return _REFERENCE_PATH.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
@python_logging_dictconfig_server.resource("resource://skills/python_logging_dictconfig/overview")
|
||||
def logging_overview() -> dict:
|
||||
"""Return high-level purpose and intended outcomes for logging configuration."""
|
||||
return {
|
||||
"id": "python-logging-dictconfig",
|
||||
"intent": "Centralize Python logging setup with dictConfig during application startup.",
|
||||
"focus": [
|
||||
"single startup configuration",
|
||||
"consistent formatting and level policy",
|
||||
"named module loggers without local configuration",
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
@python_logging_dictconfig_server.resource("resource://skills/python_logging_dictconfig/rules")
|
||||
def logging_rules() -> dict:
|
||||
"""Return implementation rules for predictable logging behavior."""
|
||||
return {
|
||||
"rules": [
|
||||
"Call dictConfig once at startup, not in leaf modules.",
|
||||
"Use logging.getLogger(__name__) in all modules.",
|
||||
"Avoid basicConfig in application packages.",
|
||||
"Set disable_existing_loggers to False unless intentional suppression is required.",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@python_logging_dictconfig_server.resource("resource://skills/python_logging_dictconfig/checklist")
|
||||
def logging_checklist() -> dict:
|
||||
"""Return a startup checklist for logging configuration rollout."""
|
||||
return {
|
||||
"checklist": [
|
||||
"Define formatter, handlers, and root logger in one LOGGING dict.",
|
||||
"Wire configure_logging() in startup path only once.",
|
||||
"Verify expected output at DEBUG/INFO/WARNING/ERROR levels.",
|
||||
"Tune noisy third-party loggers intentionally.",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@python_logging_dictconfig_server.resource("resource://skills/python_logging_dictconfig/references")
|
||||
def logging_references() -> dict:
|
||||
"""Return canonical reference material for dictConfig-based logging."""
|
||||
return {
|
||||
"source": str(_REFERENCE_PATH),
|
||||
"format": "markdown",
|
||||
"content": _load_reference_text(),
|
||||
}
|
||||
|
||||
|
||||
@python_logging_dictconfig_server.tool()
|
||||
def logging_dictconfig_template(level: str = "INFO") -> dict:
|
||||
|
||||
@@ -752,12 +752,14 @@ version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "fastmcp" },
|
||||
{ name = "pyyaml" },
|
||||
{ name = "zensical" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "fastmcp", specifier = ">=2.10.0" },
|
||||
{ name = "pyyaml", specifier = ">=6.0.2" },
|
||||
{ name = "zensical", specifier = ">=0.0.45" },
|
||||
]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user