This commit is contained in:
John Lancaster
2026-06-21 17:35:01 -05:00
parent c189677717
commit 36347ff4a5
6 changed files with 297 additions and 151 deletions
+23 -83
View File
@@ -4,129 +4,67 @@ import importlib
from collections import defaultdict
from pathlib import Path
from pathlib import PurePosixPath
from typing import Any
import yaml
from personal_mcp.registry.ingest.document import MarkdownDocument
from personal_mcp.registry.ingest.prompt import PromptFilesBundle
from personal_mcp.registry.ingest.skill import SkillFilesBundle
from personal_mcp.registry.models.common import ReferenceEntry
from personal_mcp.registry.ingest.skill import build_stored_skill
from personal_mcp.registry.models.common import _normalize_docs_path
from personal_mcp.registry.models.prompt import StoredPrompt
from personal_mcp.registry.models.registry import DocsRegistry
from personal_mcp.registry.models.registry import PromptRecord
from personal_mcp.registry.models.registry import PromptSummaryRecord
from personal_mcp.registry.models.registry import ReferenceRecord
from personal_mcp.registry.models.registry import SkillRecord
from personal_mcp.registry.models.registry import SkillSummaryRecord
from personal_mcp.skills.document_loader import _reference_id_from_filename
from personal_mcp.skills.document_loader import _title_from_reference_filename
from personal_mcp.skills.document_loader import _validate_prompt_frontmatter
from personal_mcp.skills.document_loader import _validate_skill_frontmatter
def _parse_frontmatter(raw_frontmatter: str | None, *, path: PurePosixPath) -> dict[str, Any]:
if raw_frontmatter is None:
raise ValueError(f"missing YAML frontmatter: {path.as_posix()}")
parsed = yaml.safe_load(raw_frontmatter)
if not isinstance(parsed, dict):
raise TypeError(f"frontmatter must parse to an object: {path.as_posix()}")
return parsed
def _discover_reference_entries(bundle: SkillFilesBundle) -> dict[str, ReferenceEntry]:
discovered: dict[str, ReferenceEntry] = {}
for reference_doc in bundle.references:
ref_id = _reference_id_from_filename(reference_doc.relpath.name)
if ref_id is None:
continue
discovered[ref_id] = ReferenceEntry(
path=PurePosixPath("references").joinpath(reference_doc.relpath.name).as_posix(),
title=_title_from_reference_filename(reference_doc.relpath.name),
)
return discovered
def _build_skill_record(
*, bundle: SkillFilesBundle, docs_by_relpath: dict[PurePosixPath, MarkdownDocument]
) -> SkillRecord:
frontmatter_raw = _parse_frontmatter(bundle.skill.frontmatter, path=bundle.skill.relpath)
frontmatter = _validate_skill_frontmatter(frontmatter_raw, skill_dir_name=bundle.slug)
metadata = frontmatter.x_personal_mcp
merged_entries = _discover_reference_entries(bundle)
merged_entries.update(dict(metadata.references))
stored = build_stored_skill(bundle=bundle, docs_by_relpath=docs_by_relpath)
metadata = stored.frontmatter.x_personal_mcp
references: dict[str, ReferenceRecord] = {}
for ref_id, entry in sorted(merged_entries.items()):
ref_relpath = PurePosixPath("skills").joinpath(bundle.slug).joinpath(entry.path)
if ref_relpath not in docs_by_relpath:
raise KeyError(f"reference document not found for '{metadata.id}:{ref_id}' at {ref_relpath.as_posix()}")
ref_doc = docs_by_relpath[ref_relpath]
for ref_id, ref in sorted(stored.references.items()):
references[ref_id] = ReferenceRecord(
ref_id=ref_id,
uri=f"resource://skills/{metadata.id}/references/{ref_id}",
relpath=ref_relpath.as_posix(),
mime_type=entry.mime_type,
title=entry.title,
content=ref_doc.content,
relpath=ref.relpath.as_posix(),
mime_type=ref.entry.mime_type,
title=ref.entry.title,
content=ref.content,
)
return SkillRecord(
skill_id=metadata.id,
name=frontmatter.name,
description=frontmatter.description,
name=stored.frontmatter.name,
description=stored.frontmatter.description,
version=metadata.version,
tags=tuple(metadata.tags),
capabilities=tuple(metadata.capabilities),
depends_on=tuple(metadata.depends_on),
document_uri=f"resource://skills/{metadata.id}/document",
document_relpath=bundle.skill.relpath.as_posix(),
document_content=bundle.skill.content,
document_relpath=stored.relpath.as_posix(),
document_content=stored.content,
references=references,
)
def _build_prompt_record(*, bundle: PromptFilesBundle) -> PromptRecord:
frontmatter_raw = _parse_frontmatter(bundle.prompt.frontmatter, path=bundle.prompt.relpath)
frontmatter = _validate_prompt_frontmatter(frontmatter_raw, prompt_dir_name=bundle.slug)
metadata = frontmatter.x_personal_mcp
stored = StoredPrompt.from_bundle(bundle)
metadata = stored.frontmatter.x_personal_mcp
return PromptRecord(
prompt_id=metadata.id,
name=frontmatter.name,
description=frontmatter.description,
name=stored.frontmatter.name,
description=stored.frontmatter.description,
version=metadata.version,
tags=tuple(metadata.tags),
capabilities=tuple(metadata.capabilities),
arguments=dict(metadata.arguments),
document_uri=f"resource://prompts/{metadata.id}/document",
document_relpath=bundle.prompt.relpath.as_posix(),
document_content=bundle.prompt.content,
)
def _to_summary(skill: SkillRecord) -> SkillSummaryRecord:
return SkillSummaryRecord(
skill_id=skill.skill_id,
name=skill.name,
description=skill.description,
tags=skill.tags,
capabilities=skill.capabilities,
document_uri=skill.document_uri,
version=skill.version,
)
def _to_prompt_summary(prompt: PromptRecord) -> PromptSummaryRecord:
return PromptSummaryRecord(
prompt_id=prompt.prompt_id,
name=prompt.name,
description=prompt.description,
tags=prompt.tags,
capabilities=prompt.capabilities,
document_uri=prompt.document_uri,
version=prompt.version,
document_relpath=stored.relpath.as_posix(),
document_content=stored.content,
)
@@ -215,7 +153,9 @@ def load_docs_registry(*, package_anchor: str, docs_root: str = "docs") -> DocsR
return DocsRegistry(
skills_by_id=skills_by_id,
skills_in_load_order=skills_in_order_tuple,
skills_summary_in_load_order=tuple(_to_summary(skills_by_id[skill_id]) for skill_id in skills_in_order_tuple),
skills_summary_in_load_order=tuple(
SkillSummaryRecord.from_record(skills_by_id[skill_id]) for skill_id in skills_in_order_tuple
),
docs_markdown_by_path=docs_markdown_by_path,
docs_markdown_path_index=tuple(sorted(docs_markdown_by_path)),
tag_to_skill_ids=_build_tag_index_skills(skills_in_order_tuple, skills_by_id),
@@ -223,7 +163,7 @@ def load_docs_registry(*, package_anchor: str, docs_root: str = "docs") -> DocsR
prompts_by_id=prompts_by_id,
prompts_in_load_order=prompts_in_order_tuple,
prompts_summary_in_load_order=tuple(
_to_prompt_summary(prompts_by_id[prompt_id]) for prompt_id in prompts_in_order_tuple
PromptSummaryRecord.from_record(prompts_by_id[prompt_id]) for prompt_id in prompts_in_order_tuple
),
tag_to_prompt_ids=_build_tag_index_prompts(prompts_in_order_tuple, prompts_by_id),
)