Skip to main content
Glama

update_repo_issue

Modify existing repository issues by updating titles, descriptions, or labels through the Tangled MCP Server's git collaboration platform.

Instructions

update an existing issue on a repository

Args: repo: repository identifier in 'owner/repo' format issue_id: issue number to update title: optional new title (if None, keeps existing) body: optional new body (if None, keeps existing) labels: optional list of label names to SET (replaces existing)

Returns: UpdateIssueResult with url (clickable link) and issue_id

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
repoYesrepository identifier in 'owner/repo' format (e.g., 'zzstoatzz/tangled-mcp')
issue_idYesissue number (e.g., 1, 2, 3...)
titleNo
bodyNo
labelsNo

Implementation Reference

  • Core handler function for the 'update_repo_issue' tool, decorated with @tangled_mcp.tool for registration. Includes input schema via Annotated Fields and Pydantic return type. Executes repo resolution and delegates to _tangled.update_issue helper.
    @tangled_mcp.tool
    def update_repo_issue(
        repo: Annotated[
            str,
            Field(
                description="repository identifier in 'owner/repo' format (e.g., 'zzstoatzz/tangled-mcp')"
            ),
        ],
        issue_id: Annotated[int, Field(description="issue number (e.g., 1, 2, 3...)")],
        title: Annotated[str | None, Field(description="new issue title")] = None,
        body: Annotated[str | None, Field(description="new issue body/description")] = None,
        labels: Annotated[
            list[str] | None,
            Field(
                description="list of label names to SET (replaces all existing labels). "
                "use empty list [] to remove all labels"
            ),
        ] = None,
    ) -> UpdateIssueResult:
        """update an existing issue on a repository
    
        Args:
            repo: repository identifier in 'owner/repo' format
            issue_id: issue number to update
            title: optional new title (if None, keeps existing)
            body: optional new body (if None, keeps existing)
            labels: optional list of label names to SET (replaces existing)
    
        Returns:
            UpdateIssueResult with url (clickable link) and issue_id
        """
        # resolve owner/repo to (knot, did/repo)
        knot, repo_id = _tangled.resolve_repo_identifier(repo)
        # update_issue doesn't need knot (uses atproto putRecord, not XRPC)
        _tangled.update_issue(repo_id, issue_id, title, body, labels)
    
        return UpdateIssueResult(repo=repo, issue_id=issue_id)
  • Pydantic model defining the output schema for the update_repo_issue tool, including computed URL field.
    class UpdateIssueResult(BaseModel):
        """result of updating an issue"""
    
        repo: RepoIdentifier
        issue_id: int
    
        @computed_field
        @property
        def url(self) -> str:
            """construct clickable tangled.org URL"""
            return _tangled_issue_url(self.repo, self.issue_id)
  • Helper function implementing the core update logic: locates repo and issue records via atproto list_records, constructs updated record preserving unchanged fields, performs put_record with swap for atomic update, and handles label changes via label op records.
    def update_issue(
        repo_id: str,
        issue_id: int,
        title: str | None = None,
        body: str | None = None,
        labels: list[str] | None = None,
    ) -> dict[str, Any]:
        """update an existing issue on a repository
    
        Args:
            repo_id: repository identifier in "did/repo" format (e.g., 'did:plc:.../tangled-mcp')
            issue_id: the sequential issue number (e.g., 1, 2, 3...)
            title: optional new issue title (if None, keeps existing)
            body: optional new issue body (if None, keeps existing)
            labels: optional list of label names to SET (replaces all existing labels)
                    use empty list [] to remove all labels
    
        Returns:
            dict with uri and cid of updated issue record
        """
        client = _get_authenticated_client()
    
        if not client.me:
            raise RuntimeError("client not authenticated")
    
        # parse repo_id to get owner_did and repo_name
        if "/" not in repo_id:
            raise ValueError(f"invalid repo_id format: {repo_id}")
    
        owner_did, repo_name = repo_id.split("/", 1)
    
        # get the repo AT-URI and label definitions
        records = client.com.atproto.repo.list_records(
            models.ComAtprotoRepoListRecords.Params(
                repo=owner_did,
                collection="sh.tangled.repo",
                limit=100,
            )
        )
    
        repo_at_uri = None
        repo_labels: list[str] = []
        for record in records.records:
            if (name := getattr(record.value, "name", None)) and name == repo_name:
                repo_at_uri = record.uri
                if (subscribed_labels := getattr(record.value, "labels", None)) is not None:
                    repo_labels = subscribed_labels
                break
    
        if not repo_at_uri:
            raise ValueError(f"repo not found: {repo_id}")
    
        # find the issue record with matching issueId
        existing_issues = client.com.atproto.repo.list_records(
            models.ComAtprotoRepoListRecords.Params(
                repo=client.me.did,
                collection="sh.tangled.repo.issue",
                limit=100,
            )
        )
    
        issue_record = None
        issue_rkey = None
        for record in existing_issues.records:
            if (
                (repo := getattr(record.value, "repo", None)) is not None
                and repo == repo_at_uri
                and (_issue_id := getattr(record.value, "issueId", None)) is not None
                and _issue_id == issue_id
            ):
                issue_record = record
                issue_rkey = record.uri.split("/")[-1]  # extract rkey from AT-URI
                break
    
        if not issue_record:
            raise ValueError(f"issue #{issue_id} not found in repo {repo_id}")
    
        # update the issue fields (keep existing if not specified)
        updated_record = {
            "$type": "sh.tangled.repo.issue",
            "repo": repo_at_uri,
            "issueId": issue_id,
            "owner": (
                (owner := getattr(issue_record.value, "owner", None)) is not None and owner
                if hasattr(issue_record.value, "owner")
                else client.me.did
            ),
            "title": title
            if title is not None
            else getattr(issue_record.value, "title", None),
            "body": body if body is not None else getattr(issue_record.value, "body", None),
            "createdAt": getattr(issue_record.value, "createdAt", None),
        }
    
        # get current CID for swap
        current_cid = issue_record.cid
    
        # update the issue record
    
        if issue_rkey is None:
            raise ValueError(
                f"issue rkey not found for issue #{issue_id} in repo {repo_id}"
            )
    
        response = client.com.atproto.repo.put_record(
            models.ComAtprotoRepoPutRecord.Data(
                repo=client.me.did,
                collection="sh.tangled.repo.issue",
                rkey=issue_rkey,
                record=updated_record,
                swap_record=current_cid,  # ensure we're updating the right version
            )
        )
    
        result = {"uri": response.uri, "cid": response.cid}
    
        # if labels were specified, create a label op to set them
        if labels is not None:
            issue_uri = response.uri
    
            # get current label state for this issue
            current_labels = _get_current_labels(client, issue_uri)
    
            # apply the new label state
            _apply_labels(client, issue_uri, labels, repo_labels, current_labels)
    
        return result
  • FastMCP server instance creation where all tools are registered via decorators. The update_repo_issue tool is registered via @tangled_mcp.tool decorator on its handler.
    tangled_mcp = FastMCP("tangled MCP server")

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zzstoatzz/tangled-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server