Skip to main content
Glama
mshegolev

harbor-registry-mcp

harbor_delete_artifact

Destructive

Permanently delete an artifact from a Harbor registry by specifying its tag or digest. The manifest is removed immediately and blobs are reclaimed by garbage collection.

Instructions

Delete a single artifact by tag or digest.

DESTRUCTIVE & IRREVERSIBLE — Harbor immediately removes the manifest from its catalogue; the underlying blobs are reclaimed by the next GC sweep. There is no soft-delete or undo.

Returns the freed space and tag list for confirmation.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
project_nameYesHarbor project name.
repository_nameYesRepository name within the project.
referenceYesTag (e.g. 'v1.0') or digest (e.g. 'sha256:...').

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
successYes
projectYes
repositoryYes
referenceYes
deleted_tagsYes
freed_sizeYes
freed_bytesYes
errorYes

Implementation Reference

  • The harbor_delete_artifact function is the actual tool handler. It resolves the artifact by digest or tag via _list_artifacts, then calls client.delete() to remove it from Harbor. Returns a DeleteArtifactOutput with freed size and tags.
    def harbor_delete_artifact(
        project_name: Annotated[str, Field(min_length=1, max_length=255, description="Harbor project name.")],
        repository_name: Annotated[
            str, Field(min_length=1, max_length=255, description="Repository name within the project.")
        ],
        reference: Annotated[
            str, Field(min_length=1, max_length=255, description="Tag (e.g. 'v1.0') or digest (e.g. 'sha256:...').")
        ],
    ) -> DeleteArtifactOutput:
        """Delete a single artifact by tag or digest.
    
        **DESTRUCTIVE & IRREVERSIBLE** — Harbor immediately removes the manifest
        from its catalogue; the underlying blobs are reclaimed by the next GC
        sweep. There is no soft-delete or undo.
    
        Returns the freed space and tag list for confirmation.
        """
        try:
            client = get_client()
            # Resolve the artifact first so we can return helpful metadata.
            artifacts = _list_artifacts(client, project_name, repository_name)
            target = None
            for art in artifacts:
                tags = [t["name"] for t in (art.get("tags") or [])]
                if art.get("digest") == reference or reference in tags:
                    target = art
                    break
    
            if target is None:
                empty: DeleteArtifactOutput = {
                    "success": False,
                    "project": project_name,
                    "repository": repository_name,
                    "reference": reference,
                    "deleted_tags": [],
                    "freed_size": "0 B",
                    "freed_bytes": 0,
                    "error": f"Artifact {reference!r} not found in {project_name}/{repository_name}",
                }
                md = f"❌ Artifact {reference!r} not found in {project_name}/{repository_name}"
                return output.ok(empty, md)  # type: ignore[return-value]
    
            client.delete(
                f"/projects/{project_name}/repositories/{encode_repo(repository_name)}/artifacts/{encode_repo(reference)}"
            )
    
            size = int(target.get("size", 0) or 0)
            tags = [t["name"] for t in (target.get("tags") or [])]
            result: DeleteArtifactOutput = {
                "success": True,
                "project": project_name,
                "repository": repository_name,
                "reference": reference,
                "deleted_tags": tags,
                "freed_size": size_human(size),
                "freed_bytes": size,
                "error": None,
            }
            md = (
                f"✅ Deleted {project_name}/{repository_name}@{reference} "
                f"(tags: {','.join(tags) or '(untagged)'}, freed {size_human(size)})"
            )
            return output.ok(result, md)  # type: ignore[return-value]
        except Exception as exc:
            output.fail(exc, f"deleting artifact {reference} in {project_name}/{repository_name}")
  • The @mcp.tool decorator registers the function as a FastMCP tool named 'harbor_delete_artifact' with annotations marking it as destructive, non-idempotent, etc.
    @mcp.tool(
        name="harbor_delete_artifact",
        annotations={
            "title": "Delete Single Artifact",
            "readOnlyHint": False,
            "destructiveHint": True,
            "idempotentHint": False,
            "openWorldHint": True,
        },
        structured_output=True,
  • DeleteArtifactOutput TypedDict defines the return schema: success, project, repository, reference, deleted_tags, freed_size, freed_bytes, error.
    class DeleteArtifactOutput(TypedDict):
        """Result of :func:`harbor_delete_artifact`.
    
        ``success`` is ``False`` when the artifact was not found before delete;
        ``error`` carries an explanation in that case, else is ``None``.
        """
    
        success: bool
        project: str
        repository: str
        reference: str
        deleted_tags: list[str]
        freed_size: str
        freed_bytes: int
        error: str | None
  • _list_artifacts helper fetches all artifact pages for a given repo, used by the handler to find the artifact by digest or tag.
    def _list_artifacts(client: Any, project_name: str, repository_name: str) -> list[dict[str, Any]]:
        """Fetch every artifact for a repository across all pages."""
        return client.get_all_pages(
            f"/projects/{project_name}/repositories/{encode_repo(repository_name)}/artifacts",
            page_size=100,
            extra_params={"with_tag": True, "with_scan_overview": True},
        )
  • encode_repo URL-encodes repository names (replacing / with %2F) for the Harbor REST API call in the delete path.
    def encode_repo(repository_name: str) -> str:
        """URL-encode a Harbor repository name.
    
        Harbor repos can contain slashes (e.g. ``nginx-proxy/nginx``); its REST
        API expects them percent-encoded as ``%2F`` inside the path.
        """
        return repository_name.replace("/", "%2F")
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Description goes beyond annotations by emphasizing 'DESTRUCTIVE & IRREVERSIBLE', explaining immediate manifest removal and blob reclamation during GC, with no soft-delete or undo. Also states the return value of freed space and tag list for confirmation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Description is relatively concise with front-loaded purpose. The warning section is slightly redundant but adds value. Could be more streamlined without losing clarity.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Description covers purpose, behavioral impact, parameter clarification, and return value. For a destructive action with 3 required parameters, it adequately informs the agent. Output schema existence is noted but not detailed, which is acceptable given description covers return.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Input schema has 100% coverage with clear descriptions for each parameter. Description adds minimal value beyond schema, only clarifying that reference can be tag or digest. Baseline 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description clearly states 'Delete a single artifact by tag or digest', using a specific verb and resource. It distinguishes from sibling tools like harbor_delete_old_artifacts (batch deletion) and harbor_delete_untagged (deletes untagged artifacts).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Description implies usage for deleting a specific artifact by tag or digest. It does not explicitly mention when not to use or alternative tools, but the context of siblings provides some guidance. Lacks exclusions for bulk or untagged deletion scenarios.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/mshegolev/harbor-registry-mcp'

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