"""Repo -> project binding MCP tools. Let the operator map the working git repository to a Scribe project so the SessionStart hook auto-loads that project's context — without pinning a project id in plugin config (which would force a single project for every repo). Run `bind_repo` once per repo; the binding is keyed on the normalized git remote, so it survives re-clones and ssh/https URL differences. """ from __future__ import annotations from scribe.mcp._context import current_user_id from scribe.services import projects as projects_svc from scribe.services import repo_bindings as repo_bindings_svc async def bind_repo(repo_url: str, project_id: int) -> dict: """Bind a git repository to a Scribe project for session-start context. After this, any session started in that repo auto-loads the project's context (the SessionStart hook sends the repo's remote; the server resolves it here). Idempotent — re-binding the same repo updates the target project. Args: repo_url: the repo's git remote (e.g. the output of `git remote get-url origin` — ssh or https form, both work). project_id: the Scribe project this repo represents. """ uid = current_user_id() project = await projects_svc.get_project(uid, project_id) if project is None: raise ValueError(f"project {project_id} not found") binding = await repo_bindings_svc.set_binding(uid, repo_url, project_id) return { "repo_key": binding.repo_key, "project_id": binding.project_id, "project_title": project.title, "message": f"Bound `{binding.repo_key}` -> {project.title} (id {project.id}).", } async def list_repo_bindings() -> dict: """List every repo -> project binding for the current user.""" uid = current_user_id() bindings = await repo_bindings_svc.list_bindings(uid) return {"bindings": [b.to_dict() for b in bindings]} async def unbind_repo(repo_url: str) -> dict: """Remove a repo's binding. Sessions there fall back to standing rules only. Args: repo_url: the repo's git remote (same form used to bind it). """ uid = current_user_id() removed = await repo_bindings_svc.delete_binding(uid, repo_url) key = repo_bindings_svc.normalize_repo_key(repo_url) return { "removed": removed, "repo_key": key, "message": ( f"Unbound `{key}`." if removed else f"No binding found for `{key}`." ), } def register(mcp) -> None: for fn in (bind_repo, list_repo_bindings, unbind_repo): mcp.tool(name=fn.__name__)(fn)