Create Agent Catalog Source
Scan a repository for agent templates and metadata, then generate a custom agent catalog source compatible with the agents catalog service.
Usage:
/create-agent-catalog-source— scan the current working directory/create-agent-catalog-source /path/to/repo— scan a local repository/create-agent-catalog-source https://github.com/org/repo— clone and scan a remote repository
Step 1: Resolve Input
Parse the argument passed by the user to determine the target repository path.
If the argument looks like a GitHub URL (starts with https://github.com/ or git@github.com:):
- Generate a temporary directory path:
TMPDIR=$(mktemp -d /tmp/agent-catalog-XXXXXX) - Clone the repository (shallow):
git clone --depth 1 <url> "$TMPDIR" - If the clone fails, report the error and suggest the user clone manually and pass the local path instead. Stop.
- Use
$TMPDIRas the scan target. Remember to clean up at the end.
If the argument is a local path:
- Verify the path exists:
test -d <path> - If it does not exist, report the error and stop.
- Use the provided path as the scan target.
If no argument is provided:
- Use the current working directory as the scan target.
After resolving the target path, detect git metadata for repositoryUrl generation:
cd <target-path>
git remote get-url origin 2>/dev/null
git branch --show-current 2>/dev/null
If the directory is not a git repo, warn the user that repositoryUrl fields will be empty but continue.
Convert the git remote URL to an HTTPS base URL:
git@github.com:org/repo.git→https://github.com/org/repohttps://github.com/org/repo.git→https://github.com/org/repo- Store the remote URL and branch name for later use when building
repositoryUrlper agent.
Check the output directory:
test -d <original-cwd>/.agent-catalog
If .agent-catalog/ already exists, ask the user whether to overwrite or choose a different output
path using AskUserQuestion. Store the chosen path as <output-dir> (default: <original-cwd>/.agent-catalog).
All subsequent steps use <output-dir> when writing output files.
Step 2: Discover Agents — Convention Scan
Search the target repository for directories containing an agent.yaml file. This is the
preferred agent metadata format. Inform the user:
"Scanning for agent.yaml files (preferred convention)..."
Find all agent.yaml files:
find <target-path> -name "agent.yaml" \
-not -path "*/.git/*" \
-not -path "*/.github/*" \
-not -path "*/node_modules/*" \
-not -path "*/vendor/*" \
-not -path "*/build/*" \
-not -path "*/dist/*" \
-not -path "*/__pycache__/*"
For each agent.yaml found:
- Read the file content.
- Extract known fields:
name(string, required)displayName(string, optional)framework(string, required)description(string, required)labels(string array, optional, default[])logo(string, optional, default"")env(object withrequiredandoptionalstring arrays, optional)
- Note any additional top-level fields — these will become
customProperties. - Check for a companion README.md in the same directory:
test -f <agent-dir>/README.md - If README.md exists, read its full content for the
readmefield. - Compute the agent's relative path from the repo root (for
repositoryUrl).
Collect all discovered agents into a list. Track for each:
- Relative path within the repo
- Parsed
agent.yamlfields - Whether README.md was found
- Any extra fields for customProperties
- Discovery source:
"convention"
Validation per agent:
- If
nameis missing, derive it from the directory name (kebab-case). - If
frameworkis missing, flag it as needing user input. - If
descriptionis missing, try to extract the first paragraph from README.md if available.
Always proceed to Step 3 (Heuristic Scan) regardless of how many agents the convention
scan found. The heuristic scan may discover additional agents in non-standard formats
(e.g., YAML files not named agent.yaml, or agents documented only in Markdown).
Step 3: Discover Agents — Heuristic Scan
This step always runs after the convention scan to find additional agents that may use non-standard file names or only have Markdown documentation.
Inform the user:
"Scanning for additional agents via YAML heuristic and Markdown analysis..."
3a: YAML Heuristic Scan
Search for YAML files that look like they describe an agent:
find <target-path> -type f \( -name "*.yaml" -o -name "*.yml" \) \
-not -path "*/.git/*" \
-not -path "*/node_modules/*" \
-not -path "*/vendor/*" \
-not -name "docker-compose*" \
-not -name "*lock*" \
-not -name ".pre-commit*"
For each YAML file, read it and check if it is "agent-like":
- It must contain a
namefield (string) - It must contain at least one of:
framework,description, orenv - Exclude files that look like CI configs, Dockerfiles, Kubernetes manifests (check for
apiVersion,kind,services,jobs,stepstop-level keys — these are not agents)
For qualifying files:
- Extract the same known fields as the convention scan
- Set discovery source to
"yaml-heuristic" - Note the file path
3b: Markdown Scan
Search for Markdown files that describe agents:
find <target-path> -type f -name "*.md" \
-not -path "*/.git/*" \
-not -path "*/node_modules/*" \
-not -name "CHANGELOG*" \
-not -name "LICENSE*" \
-not -name "CONTRIBUTING*"
For each Markdown file, scan for agent-relevant signals:
Framework mentions — search (case-insensitive) for:
langgraph,crewai,autogen,llamaindex,llama-index,haystack,semantic-kernel,langflow,langchain,openai-agents,google-adk,a2aAgent keywords — search for:
agent,tool-calling,ReAct,chain-of-thought,orchestrator,agentic,function calling,tool useEnvironment variable documentation — look for sections with headers like "Environment Variables", "Configuration", "Setup", ".env" that list variable names
Description extraction — extract the first paragraph after the top-level heading as a candidate description
A Markdown file is a candidate if it has at least one framework mention AND at least one agent keyword.
For qualifying Markdown files, extract as many of the 7 required catalog fields as possible:
name— infer from the filename or first heading (kebab-cased)displayName— infer from the first heading (original casing)framework— from the framework mention founddescription— from the first paragraph after the top-level headinglabels— default[](user can add during confirmation)logo— default""(user can provide during confirmation)env— extract from environment variable documentation: look for bullet lists or tables listing variable names. Parserequired/optionalindicators from the text (e.g., "required", "optional", "Yes", "No"). If a var is listed without an indicator, default torequired: true- Set
readmeto the full file content - Set discovery source to
"markdown-inference" - Flag all fields as draft — the user must confirm/edit
Merge Results
Combine convention-scan, YAML-heuristic, and markdown-inference results into a single list:
- Deduplicate: If a heuristic/markdown candidate is from the same directory as a convention-scan agent, skip the heuristic candidate (convention already captured it).
- Merge within heuristic: If a YAML-heuristic candidate and a markdown-inference candidate are from the same directory, merge them — prefer YAML fields, supplement with Markdown-extracted description/readme.
Proceed to Step 3c (Generate agent.yaml) with the merged list.
If zero agents total after all scans, tell the user:
"No agents found in this repository. To use this skill, organize your agents with an
agent.yamlfile per agent directory. Here is the expected format:"
Then print an example agent.yaml:
name: my-agent
displayName: My Agent
framework: langgraph
description: A custom agent that does X
labels:
- tool-calling
- rag
env:
required:
- API_KEY
- MODEL_ENDPOINT
optional:
- DEBUG
And stop.
3c: Generate agent.yaml for Heuristic-Discovered Agents
Every agent in the catalog must have an agent.yaml file. Convention-scanned agents already
have one. For each agent discovered via yaml-heuristic or markdown-inference, generate
an agent.yaml matching the format expected by the agents catalog.
Important: The generated agent.yaml is NOT written to the agent's source directory —
it is assembled in memory and included only in the catalog output (as the templates entry
in <output-dir>/catalog.yaml). The agent's source directory remains unchanged.
For each heuristic/markdown-discovered agent:
Assemble the agent.yaml content from the extracted metadata using this exact format:
name: <kebab-case-name> displayName: "<Human Readable Display Name>" framework: <framework> description: "<description>" labels: [<labels-as-inline-array-or-empty>] logo: "<logo-string-or-empty>" env: required: - VAR_NAME_1 - VAR_NAME_2 optional: - VAR_NAME_3Field mapping:
name: from extracted metadata, kebab-caseddisplayName: from extracted metadata, or title-cased from name if not availableframework: from extracted metadatadescription: from extracted metadatalabels: from extracted metadata, default[]logo: from extracted metadata, default""env.required/env.optional: from extracted environment variable documentation. If no env vars were found, userequired: []andoptional: []
Display the generated agent.yaml to the user:
"Generated
agent.yamlfor agent<name>(discovered via<source>):"<full assembled content>Use
AskUserQuestionto ask the user to confirm:- "Accept (Recommended)" — the generated agent.yaml is correct
- "Deny" — reject this agent; it will be excluded entirely from the catalog
If the user chooses Accept:
- Ask: "Would you like to amend any fields before finalizing? (y/N)"
- If yes: let the user specify which fields to change and their new values. Update the assembled content. Display the updated version for final confirmation.
- Store the confirmed agent.yaml content for use in Step 5 (templates field).
If the user chooses Deny:
- Remove this agent from the discovered agents list.
- Inform the user:
"Agent
<name>has been excluded from the catalog."
After processing all heuristic/markdown agents, report the final count:
"N agents confirmed with agent.yaml files. Proceeding to catalog source configuration."
If all heuristic/markdown agents were denied and no convention agents exist either, stop with the same "No agents found" message as above.
Proceed to Step 4 with only the confirmed agents.
Step 4: User Confirmation
Present all discovered agents to the user in a summary table. Format:
Discovered N agents:
# Name Framework Source Path
1. langgraph-react-agent langgraph convention agents/react_agent/
2. crewai-rag-agent crewai convention agents/rag/
3. my-custom-agent autogen yaml-heuristic tools/agent.yaml
4. search-bot langgraph markdown bots/search/README.md
For markdown-inference sourced agents, also show the draft metadata:
Agent #4 (search-bot) — inferred from Markdown, please review:
name: search-bot
framework: langgraph (inferred from README mention)
description: "A search bot that uses LangGraph to orchestrate web search tools"
[Edit any field? y/N]
Use AskUserQuestion to ask:
"Include all discovered agents, or select which to include?"
- Options: "Include all (Recommended)", "Let me select", "Add agents manually"
- If "Let me select": ask which numbers to include (comma-separated)
- If "Add agents manually": ask for the path to an agent directory, read its files, and add it to the list. Repeat until the user is done.
Required fields check. Every agent in the catalog MUST have all 7 of these fields (even if some use default/empty values):
name(string, non-empty)displayName(string, non-empty)description(string, non-empty)framework(string, non-empty)labels(string array, may be[])logo(string, may be"")env(array of{name, required}, may be[])
For any agent missing a non-empty required field (
name,displayName,description, orframework), ask the user to provide the value. Forlabels,logo, andenv, use defaults ([],"",[]) if not available, but show the user what defaults were applied so they can override.For
markdown-inferenceagents, present each one's draft metadata for ALL 7 required fields and ask if the user wants to edit any. Let them type corrections inline."What should the catalog source be named?"
- Default: the repository name (derived from git remote or directory name)
- The user can accept or type a custom name
"Would you like to add labels to this catalog source? (e.g., 'Custom', 'Internal')"
- Default: no labels (empty array)
- If yes, ask for comma-separated label strings
Store the confirmed agents list, source name, and source labels for output generation.
Step 5: Generate Catalog YAML
Create the output directory:
mkdir -p <output-dir>
Build the catalog YAML content. The output must match this exact schema (compatible with
the model-registry yamlAgentCatalog consumer):
source: <confirmed-source-name>
agents:
- name: <kebab-case-name>
displayName: <display-name>
description: <description>
readme: |
<full README.md content, or empty string>
repositoryUrl: <git-remote-https-url>/tree/<branch>/<agent-relative-path>
framework: <framework>
labels:
- <label1>
- <label2>
logo: "<logo-string-or-empty>"
env:
- name: <var-name>
required: true
- name: <var-name>
required: false
templates:
- name: agent.yaml
content: '<full-agent-yaml-as-json-string>'
customProperties:
<extra-field-name>:
metadataType: MetadataStringValue
string_value: "<value>"
Field assembly per agent:
| Field | How to build |
|---|---|
name |
From confirmed metadata. Must be kebab-case and unique within the catalog. |
displayName |
From confirmed metadata. If empty, title-case the name (replace hyphens with spaces, capitalize each word). |
description |
From confirmed metadata. |
readme |
Full content of the agent's README.md. Use YAML literal block scalar (` |
repositoryUrl |
<git-remote-url>/tree/<branch>/<agent-relative-path>. Empty string if no git remote was detected. |
framework |
From confirmed metadata. |
labels |
From agent.yaml labels field. Default []. Always include this field. |
logo |
From agent.yaml logo field. Default "". Always include this field. |
env |
Transform from {required: [...], optional: [...]} to flat list: each required var gets {name: X, required: true}, each optional var gets {name: X, required: false}. For markdown-inferred agents, use env vars extracted from the README. Default []. Always include this field. |
templates |
The templates content must always reflect the final confirmed metadata — including any edits the user made in Step 4. Convert the final metadata to a compact JSON string using ./bin/yq. The result becomes a single template entry: {name: "agent.yaml", content: "<json>"}. For all agents, assemble a YAML representation of the final confirmed fields, write it to a temporary file (TMPFILE=$(mktemp /tmp/agent-yaml-XXXXXX.yaml)), run ./bin/yq "$TMPFILE" -o json -I 0, capture stdout, then clean up (rm "$TMPFILE"). For convention-discovered agents where the user edited fields in Step 4: the on-disk agent.yaml is now out of sync — ask the user whether they want to update the original agent.yaml file in the source repository to match. Always include this field. |
customProperties |
For each top-level field in agent.yaml NOT in the known set (name, displayName, framework, description, labels, logo, env), create an entry: {metadataType: "MetadataStringValue", string_value: "<value-as-string>"}. Omit the field entirely if there are no extra fields. |
IMPORTANT: The 7 required fields (name, displayName, description, framework,
labels, logo, env) must be present on EVERY agent entry in the catalog, regardless
of discovery source. Use empty defaults ([], "") where no value was found.
Write the catalog file to <output-dir>/catalog.yaml.
Ensure the file ends with a newline character.
Validate: Read back the written file and confirm:
- It is valid YAML
- It has a
source:key - It has an
agents:array with the expected number of entries - Each agent has ALL 7 required fields present:
name,displayName,description,framework,labels,logo,env
Report to the user:
"Catalog written to
<output-dir>/catalog.yamlwith N agents."
Step 6: Generate Deployment Artifacts
6a: Sources Config Snippet
Derive the source ID from the confirmed source name:
- Lowercase the name
- Replace spaces with underscores
- Remove any characters that are not alphanumeric or underscores
Write <output-dir>/sources-snippet.yaml with the following content:
- name: "<confirmed-source-name>"
id: <derived-source-id>
type: yaml
enabled: true
properties:
yamlCatalogPath: /data/custom-agents/catalog.yaml
labels:
- <label1>
The yamlCatalogPath uses a placeholder — the user will update this to match the actual
mount path in their deployment.
If the user chose no labels, use: labels: []
6b: Deploy Script
Write <output-dir>/deploy.sh with the following content:
#!/bin/bash
# Deploy custom agent catalog source to Kubernetes
# Generated by /create-agent-catalog-source
#
# Prerequisites:
# - kubectl (or oc) CLI logged into the target cluster
# - Appropriate permissions to create ConfigMaps in the target namespace
#
# Usage:
# cd <project-root>
# NAMESPACE=my-namespace <output-dir>/deploy.sh
set -euo pipefail
NAMESPACE="${NAMESPACE:-kubeflow}"
CONFIGMAP_NAME="<derived-source-id>-catalog"
echo "Creating ConfigMap '$CONFIGMAP_NAME' in namespace '$NAMESPACE'..."
kubectl create configmap "$CONFIGMAP_NAME" \
--from-file=catalog.yaml=<output-dir>/catalog.yaml \
-n "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
echo ""
echo "ConfigMap created successfully."
echo ""
echo "Next steps:"
echo " 1. Mount the ConfigMap into the model-registry catalog deployment."
echo " 2. Edit the existing catalog sources ConfigMap to add this agent source:"
echo " kubectl edit configmap <sources-configmap-name> -n $NAMESPACE"
echo ""
echo " Add the following under the 'agent_catalogs' section:"
echo ""
cat <output-dir>/sources-snippet.yaml
echo ""
echo " 3. Update the yamlCatalogPath to match the mount path in your deployment."
echo " 4. The catalog service will hot-reload the new source automatically."
Make the deploy script executable:
chmod +x <output-dir>/deploy.sh
Replace <derived-source-id> in the script with the actual derived source ID value
before writing.
6c: Cleanup
If a temporary clone was created in Step 1:
rm -rf "$TMPDIR"
6d: Final Summary
Print to the user:
Agent catalog source generated successfully!
Output directory: <output-dir>/
catalog.yaml — Agent catalog data (N agents)
sources-snippet.yaml — Sources config snippet (paste into existing sources ConfigMap)
deploy.sh — Deployment script for Kubernetes
To deploy:
1. Run: <output-dir>/deploy.sh
2. Edit the existing sources ConfigMap to add the snippet from sources-snippet.yaml
3. The catalog service will hot-reload automatically — no restart needed.
Source name: <confirmed-source-name>
Source ID: <derived-source-id>
Note: Source IDs must be unique across all catalog types (models, MCP servers, agents)
in your model-registry deployment. If you have an existing source with ID
'<derived-source-id>', choose a different name.