rollback_skill
Revert a skill to a previous state by specifying its ID and optionally selecting a specific backup file to restore.
Instructions
Restore the latest or selected backup of a skill.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| skill_id | Yes | ||
| backup_file | No |
Implementation Reference
- src/friday_mcp_server/tools/skills.py:68-71 (registration)The MCP tool 'rollback_skill' is registered as a @mcp.tool() decorator. The function delegates to skill_store.rollback_skill(skill_id, backup_file or None).
@mcp.tool() def rollback_skill(skill_id: str, backup_file: str = "") -> dict: """Restore the latest or selected backup of a skill.""" return skill_store.rollback_skill(skill_id, backup_file or None) - The core logic of rollback_skill in SkillStore. Finds backup candidates, selects the latest or specific backup, reads its content, and re-installs it via install_skill_from_markdown with source_type='rollback'.
def rollback_skill(self, skill_id: str, backup_file: str | None = None) -> dict[str, Any]: candidates = sorted(self.backups_dir.glob(f"{skill_id}-*.md")) if backup_file is not None: candidate = (self.backups_dir / backup_file).resolve() if candidate not in [path.resolve() for path in candidates]: raise SkillError(f"Unknown backup '{backup_file}' for skill '{skill_id}'.") else: if not candidates: raise SkillError(f"No backups available for skill '{skill_id}'.") candidate = candidates[-1].resolve() markdown = candidate.read_text(encoding="utf-8") record = self.install_skill_from_markdown( markdown, source=str(candidate), source_type="rollback", activate=True, ) record["restored_from"] = str(candidate) return record - SkillDocument dataclass defines the schema/structure for skill metadata used in rollback operations.
class SkillDocument: skill_id: str name: str version: str description: str instructions: str capabilities: list[str] min_server_version: str - SkillStore.__init__ sets up backups_dir used by rollback_skill for finding backup candidates.
def __init__(self, root: Path) -> None: self.root = root self.installed_dir = self.root / "installed" self.backups_dir = self.root / "backups" self.registry_path = self.root / "registry.json" - The register function that wires up all skill tools including rollback_skill via the skills.register call path.
def register(mcp, *, skill_store) -> None: @mcp.tool() def list_skills(active_only: bool = False) -> list[dict]: """List installed skills and their activation state.""" return skill_store.list_skills(active_only=active_only) @mcp.tool() def get_skill(skill_id: str) -> dict: """Get the full installed content and metadata for a skill.""" return skill_store.get_skill(skill_id) @mcp.tool() def validate_skill_markdown(markdown: str) -> dict: """Validate a candidate skill document before installation.""" return skill_store.validate_skill_markdown(markdown) @mcp.tool() def install_skill_from_markdown( markdown: str, source: str = "generated", activate: bool = True, ) -> dict: """Install a skill from markdown front matter and instructions.""" return skill_store.install_skill_from_markdown( markdown, source=source, source_type="generated", activate=activate, ) @mcp.tool() async def install_skill_from_url(url: str, activate: bool = True) -> dict: """Download, validate, install, and optionally activate a skill from a URL.""" async with httpx.AsyncClient(follow_redirects=True, timeout=15.0) as client: response = await client.get(url) response.raise_for_status() return skill_store.install_skill_from_markdown( response.text, source=url, source_type="url", activate=activate, )