diff --git a/src/personal_mcp/main.py b/src/personal_mcp/main.py index ab8b43e..31e6392 100644 --- a/src/personal_mcp/main.py +++ b/src/personal_mcp/main.py @@ -1,7 +1,14 @@ from fastmcp import FastMCP from personal_mcp.catalog.server import catalog_server +from personal_mcp.skills.fastapi_async_sqlalchemy_modernization.server import ( + fastapi_async_sqlalchemy_modernization_server, +) from personal_mcp.skills.fastapi_uv_docker.server import fastapi_uv_docker_server +from personal_mcp.skills.nicegui.server import nicegui_server +from personal_mcp.skills.nicegui_ui_customization.server import ( + nicegui_ui_customization_server, +) from personal_mcp.skills.pytest_scaffolding.server import pytest_scaffolding_server from personal_mcp.skills.python_logging_dictconfig.server import ( python_logging_dictconfig_server, @@ -10,6 +17,12 @@ from personal_mcp.skills.python_logging_dictconfig.server import ( mcp = FastMCP("personal-mcp") mcp.mount(catalog_server, namespace="catalog") +mcp.mount( + fastapi_async_sqlalchemy_modernization_server, + namespace="fastapi_async_sqlalchemy_modernization", +) +mcp.mount(nicegui_server, namespace="nicegui") +mcp.mount(nicegui_ui_customization_server, namespace="nicegui_ui_customization") 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") diff --git a/src/personal_mcp/skills/document_loader.py b/src/personal_mcp/skills/document_loader.py new file mode 100644 index 0000000..e861ee5 --- /dev/null +++ b/src/personal_mcp/skills/document_loader.py @@ -0,0 +1,22 @@ +from pathlib import Path + + +def _repo_root() -> Path: + return Path(__file__).resolve().parents[3] + + +def load_skill_document(*, skill_id: str, skill_slug: str) -> dict[str, str]: + """Load the canonical skill markdown document for an MCP skill.""" + document_path = _repo_root() / "skills" / skill_slug / "SKILL.md" + if not document_path.exists(): + raise FileNotFoundError( + f"Missing skill document for '{skill_id}': {document_path}" + ) + + return { + "id": skill_id, + "uri": f"resource://skills/{skill_id}/document", + "format": "markdown", + "source_path": str(document_path), + "content": document_path.read_text(encoding="utf-8"), + } diff --git a/src/personal_mcp/skills/fastapi_async_sqlalchemy_modernization/__init__.py b/src/personal_mcp/skills/fastapi_async_sqlalchemy_modernization/__init__.py new file mode 100644 index 0000000..3246599 --- /dev/null +++ b/src/personal_mcp/skills/fastapi_async_sqlalchemy_modernization/__init__.py @@ -0,0 +1 @@ +"""FastAPI async SQLAlchemy modernization skill server.""" diff --git a/src/personal_mcp/skills/fastapi_async_sqlalchemy_modernization/metadata.yaml b/src/personal_mcp/skills/fastapi_async_sqlalchemy_modernization/metadata.yaml new file mode 100644 index 0000000..178fdb4 --- /dev/null +++ b/src/personal_mcp/skills/fastapi_async_sqlalchemy_modernization/metadata.yaml @@ -0,0 +1,12 @@ +id: fastapi-async-sqlalchemy-modernization +name: FastAPI Async SQLAlchemy Modernization +version: 1.0.0 +description: Create a step-by-step modernization plan for an existing FastAPI app using SQLAlchemy async patterns. +tags: + - fastapi + - sqlalchemy + - async + - modernization +capabilities: + - resource://skills/fastapi-async-sqlalchemy-modernization/document +depends_on: [] diff --git a/src/personal_mcp/skills/fastapi_async_sqlalchemy_modernization/server.py b/src/personal_mcp/skills/fastapi_async_sqlalchemy_modernization/server.py new file mode 100644 index 0000000..c4e5f2e --- /dev/null +++ b/src/personal_mcp/skills/fastapi_async_sqlalchemy_modernization/server.py @@ -0,0 +1,18 @@ +from fastmcp import FastMCP + +from personal_mcp.skills.document_loader import load_skill_document + +fastapi_async_sqlalchemy_modernization_server = FastMCP( + "fastapi-async-sqlalchemy-modernization" +) + + +@fastapi_async_sqlalchemy_modernization_server.resource( + "resource://skills/fastapi-async-sqlalchemy-modernization/document" +) +def skill_document() -> dict[str, str]: + """Return the canonical Markdown document for this skill.""" + return load_skill_document( + skill_id="fastapi-async-sqlalchemy-modernization", + skill_slug="fastapi-async-sqlalchemy-modernization", + ) diff --git a/src/personal_mcp/skills/fastapi_uv_docker/metadata.yaml b/src/personal_mcp/skills/fastapi_uv_docker/metadata.yaml index 1a7af7b..ac47a0a 100644 --- a/src/personal_mcp/skills/fastapi_uv_docker/metadata.yaml +++ b/src/personal_mcp/skills/fastapi_uv_docker/metadata.yaml @@ -7,8 +7,5 @@ tags: - 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 + - resource://skills/fastapi-uv-docker/document depends_on: [] diff --git a/src/personal_mcp/skills/fastapi_uv_docker/server.py b/src/personal_mcp/skills/fastapi_uv_docker/server.py index 3cd7e68..1599992 100644 --- a/src/personal_mcp/skills/fastapi_uv_docker/server.py +++ b/src/personal_mcp/skills/fastapi_uv_docker/server.py @@ -1,88 +1,14 @@ -from pathlib import Path - from fastmcp import FastMCP +from personal_mcp.skills.document_loader import load_skill_document + 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]: - """Return a compact migration checklist for FastAPI + uv + Docker.""" - return [ - f"Current state: {current_state}", - "Create src/ package layout for the FastAPI app.", - "Manage dependencies with uv and keep uv.lock committed.", - "Use an app factory and lifespan hooks.", - "Add /healthz endpoint for operational checks.", - "Build with a multi-stage Dockerfile and run as non-root user.", - ] +@fastapi_uv_docker_server.resource("resource://skills/fastapi-uv-docker/document") +def skill_document() -> dict[str, str]: + """Return the canonical Markdown document for this skill.""" + return load_skill_document( + skill_id="fastapi-uv-docker", + skill_slug="fastapi-uv-docker", + ) diff --git a/src/personal_mcp/skills/nicegui/__init__.py b/src/personal_mcp/skills/nicegui/__init__.py new file mode 100644 index 0000000..39d075a --- /dev/null +++ b/src/personal_mcp/skills/nicegui/__init__.py @@ -0,0 +1 @@ +"""NiceGUI architecture skill server.""" diff --git a/src/personal_mcp/skills/nicegui/metadata.yaml b/src/personal_mcp/skills/nicegui/metadata.yaml new file mode 100644 index 0000000..bbfbe20 --- /dev/null +++ b/src/personal_mcp/skills/nicegui/metadata.yaml @@ -0,0 +1,12 @@ +id: nicegui +name: NiceGUI +version: 1.0.0 +description: Design and scaffold a production-ready NiceGUI plus FastAPI application architecture. +tags: + - nicegui + - fastapi + - ui + - architecture +capabilities: + - resource://skills/nicegui/document +depends_on: [] diff --git a/src/personal_mcp/skills/nicegui/server.py b/src/personal_mcp/skills/nicegui/server.py new file mode 100644 index 0000000..40bf38e --- /dev/null +++ b/src/personal_mcp/skills/nicegui/server.py @@ -0,0 +1,11 @@ +from fastmcp import FastMCP + +from personal_mcp.skills.document_loader import load_skill_document + +nicegui_server = FastMCP("nicegui") + + +@nicegui_server.resource("resource://skills/nicegui/document") +def skill_document() -> dict[str, str]: + """Return the canonical Markdown document for this skill.""" + return load_skill_document(skill_id="nicegui", skill_slug="nicegui") diff --git a/src/personal_mcp/skills/nicegui_ui_customization/__init__.py b/src/personal_mcp/skills/nicegui_ui_customization/__init__.py new file mode 100644 index 0000000..57c7d1c --- /dev/null +++ b/src/personal_mcp/skills/nicegui_ui_customization/__init__.py @@ -0,0 +1 @@ +"""NiceGUI UI customization skill server.""" diff --git a/src/personal_mcp/skills/nicegui_ui_customization/metadata.yaml b/src/personal_mcp/skills/nicegui_ui_customization/metadata.yaml new file mode 100644 index 0000000..9ec9111 --- /dev/null +++ b/src/personal_mcp/skills/nicegui_ui_customization/metadata.yaml @@ -0,0 +1,12 @@ +id: nicegui-ui-customization +name: NiceGUI UI Customization +version: 1.0.0 +description: Design and implement production NiceGUI interfaces with reusable components and event-driven interactions. +tags: + - nicegui + - ui + - customization + - frontend +capabilities: + - resource://skills/nicegui-ui-customization/document +depends_on: [] diff --git a/src/personal_mcp/skills/nicegui_ui_customization/server.py b/src/personal_mcp/skills/nicegui_ui_customization/server.py new file mode 100644 index 0000000..6d7e98b --- /dev/null +++ b/src/personal_mcp/skills/nicegui_ui_customization/server.py @@ -0,0 +1,16 @@ +from fastmcp import FastMCP + +from personal_mcp.skills.document_loader import load_skill_document + +nicegui_ui_customization_server = FastMCP("nicegui-ui-customization") + + +@nicegui_ui_customization_server.resource( + "resource://skills/nicegui-ui-customization/document" +) +def skill_document() -> dict[str, str]: + """Return the canonical Markdown document for this skill.""" + return load_skill_document( + skill_id="nicegui-ui-customization", + skill_slug="nicegui-ui-customization", + ) diff --git a/src/personal_mcp/skills/pytest_scaffolding/metadata.yaml b/src/personal_mcp/skills/pytest_scaffolding/metadata.yaml index 6883d8e..f4e9e77 100644 --- a/src/personal_mcp/skills/pytest_scaffolding/metadata.yaml +++ b/src/personal_mcp/skills/pytest_scaffolding/metadata.yaml @@ -7,8 +7,5 @@ tags: - testing - python capabilities: - - resource://skills/pytest_scaffolding/overview - - resource://skills/pytest_scaffolding/rules - - resource://skills/pytest_scaffolding/checklist - - resource://skills/pytest_scaffolding/references + - resource://skills/pytest-scaffolding/document depends_on: [] diff --git a/src/personal_mcp/skills/pytest_scaffolding/server.py b/src/personal_mcp/skills/pytest_scaffolding/server.py index bdfdf50..7aad2cb 100644 --- a/src/personal_mcp/skills/pytest_scaffolding/server.py +++ b/src/personal_mcp/skills/pytest_scaffolding/server.py @@ -1,90 +1,14 @@ -from pathlib import Path - from fastmcp import FastMCP +from personal_mcp.skills.document_loader import load_skill_document + 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: - """Return a minimal test scaffold plan for a target scope.""" - return ( - f"MVP pytest scaffold for {target_scope}:\n" - "1. Mirror the source subtree under tests/.\n" - "2. Add one happy-path test and one edge-case test per core module.\n" - "3. Keep fast tests isolated from integration/external dependencies.\n" - "4. Use uv run pytest as the canonical runner." +@pytest_scaffolding_server.resource("resource://skills/pytest-scaffolding/document") +def skill_document() -> dict[str, str]: + """Return the canonical Markdown document for this skill.""" + return load_skill_document( + skill_id="pytest-scaffolding", + skill_slug="pytest-scaffolding", ) diff --git a/src/personal_mcp/skills/python_logging_dictconfig/metadata.yaml b/src/personal_mcp/skills/python_logging_dictconfig/metadata.yaml index f1ad7cd..f78ee6c 100644 --- a/src/personal_mcp/skills/python_logging_dictconfig/metadata.yaml +++ b/src/personal_mcp/skills/python_logging_dictconfig/metadata.yaml @@ -7,8 +7,5 @@ tags: - 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 + - resource://skills/python-logging-dictconfig/document depends_on: [] diff --git a/src/personal_mcp/skills/python_logging_dictconfig/server.py b/src/personal_mcp/skills/python_logging_dictconfig/server.py index aa12a58..20bcdda 100644 --- a/src/personal_mcp/skills/python_logging_dictconfig/server.py +++ b/src/personal_mcp/skills/python_logging_dictconfig/server.py @@ -1,94 +1,16 @@ -from pathlib import Path - from fastmcp import FastMCP +from personal_mcp.skills.document_loader import load_skill_document + python_logging_dictconfig_server = FastMCP("python-logging-dictconfig") -_REFERENCE_PATH = ( - Path(__file__).resolve().parents[4] - / "skills" - / "python-logging-dictconfig" - / "references" - / "python-logging-docs.md" + +@python_logging_dictconfig_server.resource( + "resource://skills/python-logging-dictconfig/document" ) - - -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: - """Return a minimal dictConfig template for application startup.""" - normalized = level.upper() - return { - "version": 1, - "disable_existing_loggers": False, - "formatters": { - "standard": { - "format": "%(asctime)s %(levelname)s %(name)s: %(message)s", - } - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "level": normalized, - "formatter": "standard", - "stream": "ext://sys.stdout", - } - }, - "root": { - "level": normalized, - "handlers": ["console"], - }, - } +def skill_document() -> dict[str, str]: + """Return the canonical Markdown document for this skill.""" + return load_skill_document( + skill_id="python-logging-dictconfig", + skill_slug="python-logging-dictconfig", + )