8bcf7296d5
REQ-223: mcp/atelier/server.py plugin-registry MCP server (stdio, D-135). NovaAtelierServer wraps MCPServer (SDK v2, D-137) if installed; degrades to _ToolRegistry fallback if SDK absent (testable in CI without SDK). plugins/principles.py (lookup_principle, list_domains, matrix_lookup) + plugins/validation.py (validate_against_principles — agentic validation beyond Wiz/Checkmarx/Mend). 4 tools, 2 plugins. REQ-224: mcp/atelier/vendor/ pinned Atelier v0.3.6 (D-136) — core/ first-principles, domains/security/first-principles, review/agent-checklist, matrix/principles-matrix. vendor/VERSION.md + scripts/update_atelier_vendor.sh for intentional upgrades. mcp/atelier/README.md (tools, architecture, running, vendoring, extensibility, transport). REQ-225: tests/test_atelier_mcp.py — 16 tests, all pass. Covers: plugin discovery (both loaded), 4 tools registered, lookup_security_P4 (+P1, unknown domain/principle), list_domains (19, security-relevant, ui-ux-not), matrix_lookup (security 10 P-rules, unknown), validation (good-passes, bad-secret-fails, bad-swallowed-error-fails, bad-obfuscated-names-fails, result-structure). ---ci--- project: acdl phase: 5 milestone: v1.18 status: execute requirements: covered: [REQ-223, REQ-224, REQ-225] partial: [] ---/ci---
99 lines
4.4 KiB
Python
99 lines
4.4 KiB
Python
"""mcp/atelier/plugins/principles.py — principle lookup, domain listing, matrix lookup.
|
|
|
|
Implements 3 MCP tools (REQ-223):
|
|
- atelier.lookup_principle(domain, principle_id) → principle text + core C-rule
|
|
- atelier.list_domains() → 19 domains with P-rule counts + Nova-relevance
|
|
- atelier.matrix_lookup(domain) → domain→core principle mapping
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import re
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
_VENDOR = Path(__file__).resolve().parent.parent / "vendor"
|
|
|
|
DOMAINS = [
|
|
{"domain": "api", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "security", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "data", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "testing", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "performance", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "observability", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "errors", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "documentation", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "concurrency", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "devops", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "infrastructure-as-code", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "kubernetes", "p_rules": 10, "nova_relevant": False},
|
|
{"domain": "gitops-operators", "p_rules": 10, "nova_relevant": False},
|
|
{"domain": "ai-ml", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "i18n", "p_rules": 10, "nova_relevant": False},
|
|
{"domain": "compliance", "p_rules": 10, "nova_relevant": True},
|
|
{"domain": "edge", "p_rules": 10, "nova_relevant": False},
|
|
{"domain": "messaging", "p_rules": 10, "nova_relevant": False},
|
|
{"domain": "ui-ux", "p_rules": 10, "nova_relevant": False},
|
|
]
|
|
|
|
_MATRIX = {
|
|
"security": [
|
|
{"p": "P1", "core": "C1", "title": "Boundary Validation"},
|
|
{"p": "P2", "core": "C1, C8", "title": "Least Privilege"},
|
|
{"p": "P3", "core": "C1", "title": "Defense in Depth"},
|
|
{"p": "P4", "core": "C1, C7", "title": "Secrets Never Exposed"},
|
|
{"p": "P5", "core": "C1", "title": "Authenticated by Default"},
|
|
{"p": "P6", "core": "C1", "title": "Encrypted in Transit and at Rest"},
|
|
{"p": "P7", "core": "C1, C7", "title": "Auditable Actions"},
|
|
{"p": "P8", "core": "C1, C8", "title": "Patched Dependencies"},
|
|
{"p": "P9", "core": "C1, C6", "title": "Isolated Blast Radius"},
|
|
{"p": "P10", "core": "C1", "title": "Secure by Default"},
|
|
],
|
|
}
|
|
|
|
|
|
def register(mcp: Any) -> None:
|
|
"""Register the principles tools with the MCP server (or fallback registry)."""
|
|
|
|
@mcp.tool()
|
|
def atelier_lookup_principle(domain: str, principle_id: str) -> dict[str, Any]:
|
|
"""Look up an Atelier principle by domain + P-rule ID (e.g., 'security', 'P4').
|
|
|
|
Returns the principle title, text, and the core C-rule(s) it derives from.
|
|
"""
|
|
fp = _VENDOR / "domains" / domain / "first-principles.md"
|
|
if not fp.exists():
|
|
return {"error": f"domain '{domain}' not found in vendored Atelier"}
|
|
text = fp.read_text()
|
|
# Parse the P-rule section
|
|
pattern = rf"## ({principle_id}\s*—\s*.+?)\n(.+?)(?=\n## |\Z)"
|
|
match = re.search(pattern, text, re.DOTALL)
|
|
if not match:
|
|
return {"error": f"principle '{principle_id}' not found in domain '{domain}'"}
|
|
title = match.group(1).strip()
|
|
body = match.group(2).strip()
|
|
# Find core C-rule from matrix
|
|
matrix_entry = next(
|
|
(e for e in _MATRIX.get(domain, []) if e["p"] == principle_id),
|
|
None,
|
|
)
|
|
core = matrix_entry["core"] if matrix_entry else "unknown"
|
|
return {
|
|
"domain": domain,
|
|
"principle_id": principle_id,
|
|
"title": title,
|
|
"body": body,
|
|
"core_c_rule": core,
|
|
}
|
|
|
|
@mcp.tool()
|
|
def atelier_list_domains() -> list[dict[str, Any]]:
|
|
"""List the 19 Atelier domains with P-rule counts + Nova-relevance."""
|
|
return DOMAINS
|
|
|
|
@mcp.tool()
|
|
def atelier_matrix_lookup(domain: str) -> dict[str, Any]:
|
|
"""Look up the domain→core principle mapping for a given domain."""
|
|
if domain not in _MATRIX:
|
|
return {"domain": domain, "mapping": [], "note": "full matrix not vendored for this domain; see Atelier live repo"}
|
|
return {"domain": domain, "mapping": _MATRIX[domain]} |