# SkillPatch skill: agent-platform-skill-registry

This skill enables agents to interact with the Gemini Enterprise Agent Platform Skill Registry. It supports skill discovery (search, list, inspect), lifecycle management (upload, update, delete), long-running operation monitoring, and automated scaffolding of new agent skills locally.

You (the agent) were given this URL and asked to use this skill. This is a **public** skill — no SkillPatch account, API key, or prior setup is required. Two ways to use it:

**1. Use it right now** — the skill's complete file tree (SKILL.md + all reference files) is inlined below; read `SKILL.md` first, then follow it, consulting the other files as it directs.

**2. Install the exact package onto disk** (recommended if you can run a shell — this reproduces the skill byte-for-byte, including any binary assets that can't be inlined):

```bash
mkdir -p .claude/skills/agent-platform-skill-registry
curl -sSL https://skillpatch.dev/install_skill/agent-platform-skill-registry | tar -xz -C .claude/skills/
```

(`.claude/skills/` is Claude Code's convention; use whatever directory your agent loads skills from.)


---

## Skill files (8)

- `SKILL.md`
- `references/generate-skill.md`
- `references/manage-skills.md`
- `references/monitor-operations.md`
- `references/query-skills.md`
- `scripts/requirements.txt`
- `scripts/skill_registry_ops.py`
- `scripts/validate_env.py`


### `SKILL.md`

````markdown
---
name: agent-platform-skill-registry
metadata:
  category: AiAndMachineLearning
description: >
  Interact with the Gemini Enterprise Agent Platform Skill Registry to create
  and search for available skills. Use this skill to enable agents to register
  functionality or discover new capabilities.
---

# Skill Registry

This skill provides instructions for interacting with the **Skill Registry** on
the Gemini Enterprise Agent Platform.

## Core Capabilities

-   **Skill Discovery** - Query the registry to easily search, list, get
    specific skills, and inspect revision histories.
-   **Skill Lifecycle Management** - Upload, update, or permanently delete
    skills.
-   **Operation Monitoring** - Utility to check the completion status of
    long-running state changes (LROs).
-   **Generate Skill** - Automate the initial scaffolding of new agent skills
    locally.

## Core Directives

-   **Mandatory Validation**: ALWAYS execute the environment validation check
    before performing any operations.

    Before any operation, you **must** validate the core environment.

    ```bash
    # Execute the validation script
    python3 scripts/validate_env.py
    ```

## Prerequisites & Authentication

### Library & Authentication

Ensure you have the latest Google Cloud credentials and libraries installed.

```bash
# Install required libraries
pip install google-auth requests

# Authenticate with Google Cloud
gcloud auth application-default login
```

### Environment Variables

The following variables are required for operations:

-   `GCP_PROJECT_ID`: Your Google Cloud Project ID.
-   `GCP_LOCATION`: The region (e.g., `us-central1`).

--------------------------------------------------------------------------------

## Quickstart

Quickly search for available skills in the registry:

```bash
python3 scripts/skill_registry_ops.py search \
  --query "test skill" \
  --top-k 5
```

--------------------------------------------------------------------------------

## Operations

-   **Skill Discovery**: [query-skills.md](references/query-skills.md)
-   **Skill Lifecycle**: [manage-skills.md](references/manage-skills.md)
-   **Monitor Operations**:
    [monitor-operations.md](references/monitor-operations.md)
-   **Generate Skill**: [generate-skill.md](references/generate-skill.md)

````


### `references/generate-skill.md`

```markdown
# Generate Skill

## Description

GenerateSkill automates the *initial* scaffolding of new agent skills by generating standardized documentation (`SKILL.md`) and directory structures based on user requirements. Note: This tool serves strictly as a starting point. It generates a foundational draft and requires human intervention to refine the logic, review the architecture, and ensure the final skill meets production-level quality standards.

**Use when:**

* Scaffolding a new agent skill from scratch.
* Establishing a standardized directory structure for a new tool.
* Drafting a properly formatted `SKILL.md` for a specific use case.

**Don't use when:**

* Executing an existing skill or performing a general query.
* Writing general code outside of a skill directory.

---

## Directory Structure

When generating a new skill, the following standardized architecture should be established:

* **`SKILL.md`**: The core documentation and instruction set for the skill *(Required)*.
* **`references/`**: Directory for storing heavy external documentation, API specs, or knowledge bases *(Optional)*.
* **`scripts/`**: Directory for executable scripts, helper functions, or setup files *(Optional)*. Offload complex code snippets, deterministic helper functions, or repetitive setup tasks into this directory to keep `SKILL.md` lean and focused entirely on high-level instructions and usage patterns.
* **`assets/`**: Directory for static files, templates, or media used by the skill *(Optional)*.

---

## Execution Workflow

To successfully generate and deliver a new skill draft, follow these sequential steps:

1. **Requirement Gathering:** Analyze the user's prompt to understand the purpose, inputs, outputs, and constraints of the desired skill.
2. **Drafting:** Generate the `SKILL.md` content based on the gathered requirements. Ensure the description is concise (under 300 words) and explicitly defines "Use when" and "Don't use when" conditions. Identify and map out necessary optional directories (`references/`, `scripts/`, `assets/`) if applicable, ensuring that any complex or repetitive code logic is offloaded into the `scripts/` directory.
3. **Validation:** Automatically parse and validate the drafted `SKILL.md` to ensure strictly valid Markdown formatting (e.g., correct header nesting, closed tags, proper list syntax). Fix any errors before proceeding.
4. **Review Request:** Present the generated `SKILL.md` and directory structure to the user. Explicitly request their review and manual revision, reiterating that human evaluation is required to finalize the draft for production.

```


### `references/manage-skills.md`

````markdown
# Skill Lifecycle Management

This document covers state-changing actions (uploading, updating, deleting)
for a skill.

## Upload Skill
Upload a new skill into the Skill Registry using either a zipped package or a
folder.

### Supported Flags

*   `--skill-id` (Required): The unique identifier for the skill.
*   `--display-name` (Required): The human-readable name of the skill.
*   `--description` (Required): A description of what the skill does.
*   `--zip-file` (Required, mutually exclusive with `--folder`): Path to a local
    `.zip` file containing the skill.
*   `--folder` (Required, mutually exclusive with `--zip-file`): Path to a local
    folder containing the skill.

```bash
# Option 1: Upload a skill from a folder (recommended)
python3 scripts/skill_registry_ops.py upload \
  --skill-id "my-sample-skill" \
  --display-name "My Sample Skill" \
  --description "A test skill uploaded via script." \
  --folder "/path/to/skill/folder"

# Option 2: Upload a skill using a .zip file
python3 scripts/skill_registry_ops.py upload \
  --skill-id "my-sample-skill" \
  --display-name "My Sample Skill" \
  --description "A test skill uploaded via script." \
  --zip-file "/path/to/skill.zip"
```
*Note: This returns a long-running operation ID. See `monitor-operations.md`.*

## Update Skill
Update an existing skill's metadata or files. At least one update parameter
must be provided (`--display-name`, `--description`, `--zip-file`, or
`--folder`).

### Supported Flags

*   `--skill-id` (Required): The unique identifier for the skill.
*   `--display-name` (Optional): A new display name for the skill.
*   `--description` (Optional): A new description for the skill.
*   `--zip-file` (Optional, Mutually exclusive with `--folder`): Path to a new
    `.zip` file payload.
*   `--folder` (Optional, Mutually exclusive with `--zip-file`): Path to a new
    folder payload.

```bash
python3 scripts/skill_registry_ops.py update \
  --skill-id "my-sample-skill" \
  --display-name "Updated Name" \
  --description "Updated description." \
  --folder "/path/to/updated/skill/folder"
```
*Note: This returns a long-running operation ID. See `monitor-operations.md`.*

## Delete Skill
Remove a specific skill from the registry forever.

### Supported Flags

*   `--skill-id` (Required): The unique identifier for the skill to delete.

```bash
python3 scripts/skill_registry_ops.py delete --skill-id "my-skill"
```
*Note: This returns a long-running operation ID. See `monitor-operations.md`.*

````


### `references/monitor-operations.md`

````markdown
# Monitor Operations

This document covers how to monitor the status of Long-Running Operations (LRO)
returned by lifecycle management actions (uploading, updating, or deleting a
skill).

## Check Operation Status

Check the status of a long-running operation using its `OPERATION_ID` (or full
resource name).

### Supported Flags

*   `--operation-id` (Required): The unique identifier or full resource name of
    the long-running operation returned from previous commands.

```bash
python3 scripts/skill_registry_ops.py monitor \
  --operation-id "projects/my-project/locations/us-central1/operations/123456789"
```

````


### `references/query-skills.md`

````markdown
# Skill Discovery

This document covers safe, read-only operations for finding and inspecting
skills as well as their revision histories in the Skill Registry.

## Search Skills
Find skills matching a semantic search term.

### Supported Flags

*   `--query` (Required): The semantic query to find matching skills.
*   `--top-k` (Optional): The maximum number of skills to return. Defaults to 5.

```bash
python3 scripts/skill_registry_ops.py search \
  --query "test skill" \
  --top-k 5
```

## List Skills
List all skills in the registry for the configured project and location.

### Supported Flags

*(None)*

```bash
python3 scripts/skill_registry_ops.py list
```

## Get Skill
Retrieve details for a specific skill by its ID.

### Supported Flags

*   `--skill-id` (Required): The unique identifier for the skill.

```bash
python3 scripts/skill_registry_ops.py get --skill-id "my-skill"
```

## List Revisions
Inspect the history of changes / versions for a specific skill. (Read-only
metadata about lifecycle).

### Supported Flags

*   `--skill-id` (Required): The unique identifier for the skill.

```bash
python3 scripts/skill_registry_ops.py list-revision --skill-id "my-skill"
```

## Get Revision
Fetch details of a specific revision.

### Supported Flags

*   `--skill-id` (Required): The unique identifier for the skill.
*   `--revision-id` (Required): The specific revision ID to fetch (e.g., from
    list-revision).

```bash
python3 scripts/skill_registry_ops.py get-revision \
  --skill-id "my-skill" \
  --revision-id "test-revision-123"
```

````


### `scripts/requirements.txt`

```
# Required Python packages for Skill Registry Python scripts.
google-auth
requests

```


### `scripts/skill_registry_ops.py`

```
"""A script to perform operations on the Skill Registry."""

import argparse
import base64
import io
import json
import os
import sys
import urllib.parse
import zipfile
import google.auth
from google.auth.transport.requests import Request
import requests


def get_access_token():
  credentials, _ = google.auth.default()
  credentials.refresh(Request())
  return credentials.token


def get_endpoint(region):
  return f"{region}-aiplatform.googleapis.com"


def upload(args):
  """Uploads a new skill in the Skill Registry.

  Args:
    args: The command line arguments.
  """
  token = get_access_token()
  endpoint = get_endpoint(args.location)
  url = f"https://{endpoint}/v1beta1/projects/{args.project}/locations/{args.location}/skills?skillId={args.skill_id}"

  headers = {
      "Authorization": f"Bearer {token}",
      "Content-Type": "application/json",
  }

  if args.zip_file:
    with open(args.zip_file, "rb") as f:
      zip_bytes = f.read()
  elif args.folder:
    zip_buffer = io.BytesIO()
    with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
      for root, _, files in os.walk(args.folder):
        for file in files:
          file_path = os.path.join(root, file)
          arcname = os.path.relpath(file_path, args.folder)
          zip_file.write(file_path, arcname)
    zip_bytes = zip_buffer.getvalue()
  else:
    raise ValueError("Must provide either --zip-file or --folder")

  zipped_filesystem = base64.b64encode(zip_bytes).decode("utf-8")

  payload = {
      "displayName": args.display_name,
      "description": args.description,
      "zippedFilesystem": zipped_filesystem,
  }

  print(f"Uploading skill {args.skill_id} at {endpoint}...")
  response = requests.post(url, headers=headers, json=payload)

  if response.status_code >= 400:
    print(f"Error: {response.status_code} - {response.text}")
    sys.exit(1)

  print("Response:")
  print(json.dumps(response.json(), indent=2))


def search(args):
  """Searches for skills in the Skill Registry."""
  token = get_access_token()
  endpoint = get_endpoint(args.location)
  query_encoded = urllib.parse.quote(args.query)
  url = f"https://{endpoint}/v1beta1/projects/{args.project}/locations/{args.location}/skills:retrieve?query={query_encoded}&topK={args.top_k}"

  headers = {
      "Authorization": f"Bearer {token}",
      "Content-Type": "application/json",
  }

  print(f"Searching skills at {endpoint} with query '{args.query}'...")
  response = requests.get(url, headers=headers)

  if response.status_code >= 400:
    print(f"Error: {response.status_code} - {response.text}")
    sys.exit(1)

  print("Response:")
  print(json.dumps(response.json(), indent=2))


def get_skill(args):
  """Gets a skill from the Skill Registry."""
  token = get_access_token()
  endpoint = get_endpoint(args.location)
  url = f"https://{endpoint}/v1beta1/projects/{args.project}/locations/{args.location}/skills/{args.skill_id}"

  headers = {
      "Authorization": f"Bearer {token}",
  }

  print(f"Getting skill {args.skill_id} at {endpoint}...")
  response = requests.get(url, headers=headers)

  if response.status_code >= 400:
    print(f"Error: {response.status_code} - {response.text}")
    sys.exit(1)

  print("Response:")
  print(json.dumps(response.json(), indent=2))


def list_skills(args):
  """Lists skills in the Skill Registry."""
  token = get_access_token()
  endpoint = get_endpoint(args.location)
  url = f"https://{endpoint}/v1beta1/projects/{args.project}/locations/{args.location}/skills"

  headers = {
      "Authorization": f"Bearer {token}",
  }

  print(f"Listing skills at {endpoint}...")
  response = requests.get(url, headers=headers)

  if response.status_code >= 400:
    print(f"Error: {response.status_code} - {response.text}")
    sys.exit(1)

  print("Response:")
  print(json.dumps(response.json(), indent=2))


def delete_skill(args):
  """Deletes a skill from the Skill Registry."""
  token = get_access_token()
  endpoint = get_endpoint(args.location)
  url = f"https://{endpoint}/v1beta1/projects/{args.project}/locations/{args.location}/skills/{args.skill_id}"

  headers = {
      "Authorization": f"Bearer {token}",
  }

  print(f"Deleting skill {args.skill_id} at {endpoint}...")
  response = requests.delete(url, headers=headers)

  if response.status_code >= 400:
    print(f"Error: {response.status_code} - {response.text}")
    sys.exit(1)

  print("Response:")
  print(json.dumps(response.json(), indent=2))


def update_skill(args):
  """Updates an existing skill in the Skill Registry."""
  token = get_access_token()
  endpoint = get_endpoint(args.location)

  update_mask_parts = []
  payload = {}

  if args.display_name:
    update_mask_parts.append("displayName")
    payload["displayName"] = args.display_name

  if args.description:
    update_mask_parts.append("description")
    payload["description"] = args.description

  if args.zip_file or args.folder:
    if args.zip_file:
      with open(args.zip_file, "rb") as f:
        zip_bytes = f.read()
    elif args.folder:
      zip_buffer = io.BytesIO()
      with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
        for root, _, files in os.walk(args.folder):
          for file in files:
            file_path = os.path.join(root, file)
            arcname = os.path.relpath(file_path, args.folder)
            zip_file.write(file_path, arcname)
      zip_bytes = zip_buffer.getvalue()

    zipped_filesystem = base64.b64encode(zip_bytes).decode("utf-8")

    update_mask_parts.append("zippedFilesystem")
    payload["zippedFilesystem"] = zipped_filesystem

  if not update_mask_parts:
    print(
        "Error: must provide at least one field to update (--display-name,"
        " --description, --zip-file, --folder)"
    )
    sys.exit(1)

  update_mask = ",".join(update_mask_parts)
  url = f"https://{endpoint}/v1beta1/projects/{args.project}/locations/{args.location}/skills/{args.skill_id}?updateMask={update_mask}"

  headers = {
      "Authorization": f"Bearer {token}",
      "Content-Type": "application/json",
  }

  print(f"Updating skill {args.skill_id} at {endpoint}...")
  response = requests.patch(url, headers=headers, json=payload)

  if response.status_code >= 400:
    print(f"Error: {response.status_code} - {response.text}")
    sys.exit(1)

  print("Response:")
  print(json.dumps(response.json(), indent=2))


def list_skill_revision(args):
  """Lists revisions of a skill in the Skill Registry."""
  token = get_access_token()
  endpoint = get_endpoint(args.location)
  url = f"https://{endpoint}/v1beta1/projects/{args.project}/locations/{args.location}/skills/{args.skill_id}/revisions"

  headers = {
      "Authorization": f"Bearer {token}",
  }

  print(f"Listing revisions for skill {args.skill_id} at {endpoint}...")
  response = requests.get(url, headers=headers)

  if response.status_code >= 400:
    print(f"Error: {response.status_code} - {response.text}")
    sys.exit(1)

  print("Response:")
  print(json.dumps(response.json(), indent=2))


def get_skill_revision(args):
  """Gets a specific revision of a skill from the Skill Registry."""
  token = get_access_token()
  endpoint = get_endpoint(args.location)
  url = f"https://{endpoint}/v1beta1/projects/{args.project}/locations/{args.location}/skills/{args.skill_id}/revisions/{args.revision_id}"

  headers = {
      "Authorization": f"Bearer {token}",
  }

  print(
      f"Getting skill {args.skill_id} revision {args.revision_id} at"
      f" {endpoint}..."
  )
  response = requests.get(url, headers=headers)

  if response.status_code >= 400:
    print(f"Error: {response.status_code} - {response.text}")
    sys.exit(1)

  print("Response:")
  print(json.dumps(response.json(), indent=2))


def monitor(args):
  """Monitors the status of a long-running operation."""
  token = get_access_token()
  endpoint = get_endpoint(args.location)
  op_id = args.operation_id.lstrip("/")
  url = f"https://{endpoint}/v1beta1/{op_id}"

  headers = {
      "Authorization": f"Bearer {token}",
  }

  print(f"Monitoring operation {args.operation_id} at {endpoint}...")
  response = requests.get(url, headers=headers)

  if response.status_code >= 400:
    print(f"Error: {response.status_code} - {response.text}")
    sys.exit(1)

  print("Response:")
  print(json.dumps(response.json(), indent=2))


def main():
  parser = argparse.ArgumentParser(
      description="Skill Registry Operations Utility"
  )
  parser.add_argument("--project", default=os.environ.get("GCP_PROJECT_ID"))
  parser.add_argument("--location", default=os.environ.get("GCP_LOCATION"))

  subparsers = parser.add_subparsers(dest="action", required=True)

  upload_parser = subparsers.add_parser("upload")
  upload_parser.add_argument("--skill-id", required=True)
  upload_parser.add_argument("--display-name", required=True)
  upload_parser.add_argument("--description", required=True)

  group = upload_parser.add_mutually_exclusive_group(required=True)
  group.add_argument("--zip-file")
  group.add_argument("--folder")

  search_parser = subparsers.add_parser("search")
  search_parser.add_argument("--query", required=True)
  search_parser.add_argument("--top-k", type=int, default=5)

  get_parser = subparsers.add_parser("get")
  get_parser.add_argument("--skill-id", required=True)

  subparsers.add_parser("list")

  delete_parser = subparsers.add_parser("delete")
  delete_parser.add_argument("--skill-id", required=True)

  update_parser = subparsers.add_parser("update")
  update_parser.add_argument("--skill-id", required=True)
  update_parser.add_argument("--display-name", required=False)
  update_parser.add_argument("--description", required=False)
  update_group = update_parser.add_mutually_exclusive_group(required=False)
  update_group.add_argument("--zip-file")
  update_group.add_argument("--folder")

  list_rev_parser = subparsers.add_parser("list-revision")
  list_rev_parser.add_argument("--skill-id", required=True)

  get_rev_parser = subparsers.add_parser("get-revision")
  get_rev_parser.add_argument("--skill-id", required=True)
  get_rev_parser.add_argument("--revision-id", required=True)

  monitor_parser = subparsers.add_parser("monitor")
  monitor_parser.add_argument("--operation-id", required=True)

  args = parser.parse_args()

  missing = []
  if not args.project:
    missing.append("GCP_PROJECT_ID")
  if not args.location:
    missing.append("GCP_LOCATION")

  if missing:
    print(
        f"ERROR: Missing required environment variables: {', '.join(missing)}"
    )
    sys.exit(1)

  if args.action == "upload":
    upload(args)
  elif args.action == "search":
    search(args)
  elif args.action == "get":
    get_skill(args)
  elif args.action == "list":
    list_skills(args)
  elif args.action == "delete":
    delete_skill(args)
  elif args.action == "update":
    update_skill(args)
  elif args.action == "list-revision":
    list_skill_revision(args)
  elif args.action == "get-revision":
    get_skill_revision(args)
  elif args.action == "monitor":
    monitor(args)


if __name__ == "__main__":
  main()

```


### `scripts/validate_env.py`

```
"""Validates that required environment variables are set."""

import os
import sys


def validate_env():
  print("Validating core environment variables...")
  required = ["GCP_PROJECT_ID", "GCP_LOCATION"]
  missing = [v for v in required if not os.environ.get(v)]
  if missing:
    print(f"ERROR: Missing core variables: {', '.join(missing)}")
    sys.exit(1)
  print("SUCCESS: Core environment validated.")


if __name__ == "__main__":
  validate_env()

```
