prompt markdown

This commit is contained in:
John Lancaster
2026-08-07 20:30:54 -05:00
parent 5b6d5aaec4
commit 88ff4c2c71
30 changed files with 475 additions and 373 deletions
+32
View File
@@ -1,5 +1,8 @@
from pathlib import Path
import pytest
from personal_mcp.prompts.content import load_prompt_definition
from personal_mcp.prompts.content import render_prompt
pytestmark = pytest.mark.unit
@@ -23,3 +26,32 @@ class TestPromptContentRenderer:
def test_rejects_invalid_prompt_id(self) -> None:
with pytest.raises(ValueError, match="lowercase kebab-case"):
render_prompt("../outside", {})
def test_rejects_missing_prompt_metadata(self, tmp_path: Path) -> None:
document = tmp_path / "PROMPT.md"
document.write_text("---\nicon: lucide/messages-square\n---\n\n# Body\n", encoding="utf-8")
with pytest.raises(ValueError, match="missing the 'prompt' block"):
load_prompt_definition("demo", document)
def test_rejects_unknown_prompt_metadata(self, tmp_path: Path) -> None:
document = tmp_path / "PROMPT.md"
document.write_text(
"---\nprompt: {version: '1', description: Demo, tags: [demo], arguments: {}, unknown: true}\n"
"---\n\n# Body\n",
encoding="utf-8",
)
with pytest.raises(ValueError, match="Extra inputs are not permitted"):
load_prompt_definition("demo", document)
def test_rejects_declared_placeholder_drift(self, tmp_path: Path) -> None:
document = tmp_path / "PROMPT.md"
document.write_text(
"---\nprompt: {version: '1', description: Demo, tags: [demo], arguments: "
"{topic: {description: Topic, required: true}}}\n---\n\n# Body\n",
encoding="utf-8",
)
with pytest.raises(ValueError, match="placeholders do not match arguments"):
load_prompt_definition("demo", document)
+70 -1
View File
@@ -1,6 +1,9 @@
from pathlib import Path
import pytest
from fastmcp import Client
from fastmcp import FastMCP
from fastmcp.exceptions import PromptError
from personal_mcp.prompts import create_prompts_provider
@@ -17,7 +20,20 @@ EXPECTED_PROMPTS = {
}
class TestPromptFileSystemProvider:
def write_prompt(document: Path, *, description: str, heading: str = "Demo") -> None:
document.parent.mkdir(parents=True, exist_ok=True)
document.write_text(
"---\n"
f"prompt: {{version: '1.0.0', description: {description!r}, tags: [demo], arguments: "
"{kind: {description: 'Kind to render.', required: true, choices: [first, second]}, "
"note: {description: 'Optional note.', required: false}}}\n"
"---\n\n"
f"# {heading}\n\nKind: {{{{kind}}}}\n\nNote: {{{{note}}}}\n",
encoding="utf-8",
)
class TestMarkdownPromptsProvider:
@pytest.mark.asyncio
async def test_discovers_exact_authored_set(self) -> None:
mcp = FastMCP("prompts-test")
@@ -47,6 +63,59 @@ class TestPromptFileSystemProvider:
)
required = {argument.name for argument in authoring.arguments or [] if argument.required}
artifact_type = next(argument for argument in authoring.arguments or [] if argument.name == "artifact_type")
assert required == {"artifact_type", "artifact_id", "goal"}
assert artifact_type.description == "Artifact type to create.\n\nAccepted values: skill, prompt, shim."
assert result.messages
assert "`artifact_id`: demo-skill" in result.messages[0].content.text
@pytest.mark.asyncio
async def test_enforces_required_arguments_and_choices(self) -> None:
provider = create_prompts_provider()
prompt = await provider.get_prompt("authoring")
assert prompt is not None
with pytest.raises(PromptError, match="Missing required arguments"):
await prompt.render({"artifact_type": "skill"})
with pytest.raises(PromptError, match="must be one of"):
await prompt.render(
{
"artifact_type": "unsupported",
"artifact_id": "demo-skill",
"goal": "Demonstrate validation.",
}
)
@pytest.mark.asyncio
async def test_live_loads_edits_without_python_components(self, tmp_path: Path) -> None:
prompts_root = tmp_path / "prompts"
prompts_root.mkdir()
provider = create_prompts_provider(prompts_root)
mcp = FastMCP("prompts-test")
mcp.add_provider(provider)
async with Client(mcp) as client:
assert await client.list_prompts() == []
document = prompts_root / "dynamic-demo" / "PROMPT.md"
write_prompt(document, description="Initial description")
prompts = await client.list_prompts()
assert [prompt.name for prompt in prompts] == ["dynamic-demo"]
assert prompts[0].description == "Initial description"
result = await client.get_prompt("dynamic-demo", {"kind": "first"})
assert "# Demo" in result.messages[0].content.text
assert "Note: Not provided" in result.messages[0].content.text
write_prompt(document, description="Updated description", heading="Updated")
prompts = await client.list_prompts()
assert prompts[0].description == "Updated description"
result = await client.get_prompt("dynamic-demo", {"kind": "second", "note": "ready"})
assert "# Updated" in result.messages[0].content.text
assert "Note: ready" in result.messages[0].content.text
document.unlink()
document.parent.rmdir()
assert await client.list_prompts() == []