@vantageos/vantage-registry-mcp
v1.19.0
Published
MCP server exposing VantageRegistry Convex functions as Claude Code tools
Downloads
429
Readme
@vantageos/vantage-registry-mcp
MCP server exposing VantageRegistry Convex functions as Claude Code tools via stdio transport.
Backend: Convex (vibrant-ibex-858) Transport: stdio (Claude Code MCP protocol)
Installation
Add to your Claude Code MCP config (.claude/mcp.json or equivalent):
{
"mcpServers": {
"vantage-registry": {
"command": "bunx",
"args": ["@vantageos/vantage-registry-mcp"],
"env": {
"CONVEX_URL": "https://vibrant-ibex-858.convex.cloud",
"VR_ADMIN_WRITE_SECRET": "<your-write-secret>"
}
}
}
}bunx fetches the latest published version by default; leave the package unpinned in args. If a stale local cache serves an older build, force a fresh fetch (clear the bunx/npm cache) — never pin a version number, which reintroduces exactly the stale install this warns against.
Or set CONVEX_URL in .env.local at the repo root.
What's new in 1.17.0
Lite projection by default on the catalogue's list_* tools (task k17fc3rp).
| Feature | Details |
|---------|---------|
| Compact projection by default | The list_* catalogue tools that accept a fields param now return a compact lite projection by default — name/id/description/version/metadata — dropping heavy body fields (content, vrContent, testContent, body, evalCorpus). This keeps large catalogue scans well under the MCP token cap. |
| Opt in to the full form | Pass fields="full" to get the complete document, including body fields. Legacy summary=false (on the handful of tools that predate the fields param) maps to the same full response. |
| Not every list_* tool takes fields | A few list tools (list_skills_by_team, list_skills_by_category) only accept the legacy summary boolean, and a few junction/read-lookup tools (list_consumers_for_template, list_templates_for_consumer, list_process_components) always return the lite projection with no full-document option. Check each tool's row below before assuming fields="full" is accepted. |
Relation to 1.9.0 testContent: the lite projection strips testContent from the default list_* response along with the other body fields; fields="full" (or the dedicated get_*_content tools) still returns it.
What's new in 1.9.0
| Feature | Details |
|---------|---------|
| testContent field on hooks, skills, and agents | The 9 content tools (upsert_hook_content, upsert_skill_content, upsert_agent_content, get_hook_content, get_skill_content, get_agent_content, detect_hook_drift, detect_skill_drift, detect_agent_drift) now carry a cohabiting testContent field alongside the primary content |
| testContent upsert param | All three upsert_*_content tools accept an optional testContent string — pass the test file body to store it atomically with the production content in the same Convex document |
| testContent read fields | All three get_*_content tools return three new fields: testContent (raw bytes), testContentHash (SHA-256 of stored test bytes), and testContentVersion (backend patch counter, increments on any byte change) |
| testHash in drift detection | detect_hook_drift, detect_skill_drift, and detect_agent_drift return a testHash field per entry — compare against sha256(disk test bytes) to detect drift in test files independently of production content |
Relation to eval-corpus: testContent is complementary to, not a replacement for, upsert_skill_eval_corpus. Eval-corpus stores structured prompt/response pairs for automated quality gates. testContent stores the raw test file (e.g. a pytest or vitest file) that lives alongside the hook/skill/agent in the fleet. Both can coexist on the same artifact.
Idempotency: testContentVersion is incremented only when the byte content changes. A re-upsert with identical bytes leaves testContentVersion unchanged and does not trigger a false-positive drift signal.
What's new in 1.8.0
| Feature | Details |
|---------|---------|
| doc-binary template type | upsert_template accepts template_type="doc-binary" plus 4 new optional fields: renderer_kind, input_schema, assets, output_formats |
| 4 template_consumers tools | link_template_consumer, unlink_template_consumer, list_consumers_for_template, list_templates_for_consumer — junction table linking templates to the components/BUs that consume them |
| 3 Bible fields on components | consumers, clientReadyStage, and lastUsageSignal now returned by list_components / get_component |
Tools
Teams
| Tool | Description |
|------|-------------|
| upsert_team | Create or update a team (upsert by name) |
| list_teams | List all teams — optional status filter |
| get_team | Get a team by Convex document ID |
Agents
| Tool | Description |
|------|-------------|
| upsert_agent | Create or update an agent (upsert by name) |
| list_agents | List agents — optional status / team filters. fields="lite" (compact, default) or fields="full" (complete document) |
| list_agents_by_team | List agents for a specific team. fields="lite" (compact, default) or fields="full" (complete document) |
| get_agent | Get an agent by Convex document ID |
Skills
| Tool | Description |
|------|-------------|
| upsert_skill | Create or update a skill (upsert by name). New in 1.6.0: optional vrBody writes the full SKILL.md body (vrContent/hash/version) in the same call |
| list_skills | List skills — optional status / team / category filters. fields="lite" (compact, default) or fields="full" (complete document) |
| list_skills_by_team | List skills for a specific team. No fields param — pass summary=false for full documents (default summary=true, lite) |
| list_skills_by_category | List skills for a specific category. No fields param — pass summary=false for full documents (default summary=true, lite) |
| list_skills_by_domain | List skills for a domainCategory, optionally narrowed to a scopeOrchestrator — cursor-paginated, see "Domain category axis" below. fields="lite" (compact, default) or fields="full" (complete document) |
| get_skill | Get a skill by Convex document ID |
Plugins
| Tool | Description |
|------|-------------|
| upsert_plugin | Create or update a plugin (upsert by name) |
| list_plugins | List plugins — optional status filter. fields="lite" (compact, default) or fields="full" (complete document) |
| get_plugin | Get a plugin by Convex document ID |
Hooks
| Tool | Description |
|------|-------------|
| upsert_hook | Create or update a hook (upsert by name). New in 1.6.0: optional tests array of hook test file paths |
| list_hooks | List hooks — optional status / event / scope filters. fields="lite" (compact, default) or fields="full" (complete document) |
| get_hook | Get a hook by Convex document ID |
Rules
| Tool | Description |
|------|-------------|
| upsert_rule | Create or update a rule (upsert by name) |
| upsert_rule_content | Set/update the canonical rule body (vrContent/hash/version) |
| list_rules | List rules — optional status filter. fields="lite" (compact, default) or fields="full" (complete document). limit is backend-clamped (max 200 lite / 30 full) — NOT cursor-paginated (legacy .take() path) |
| list_rules_by_domain | List rules for a domainCategory, optionally narrowed to a scopeOrchestrator — cursor-paginated, see "Domain category axis" below. fields="lite" (compact, default) or fields="full" (complete document) |
| get_rule | Get a rule by name — metadata only, no content |
| get_rule_content | Get a rule's full content + hash + paths |
| detect_rule_drift | Return per-rule vrHash for drift comparison against local disk bytes |
Prompts
| Tool | Description |
|------|-------------|
| upsert_prompt | Create or update a prompt (upsert by name) |
| list_prompts | List prompts — optional status / team filters. fields="lite" (compact, default) or fields="full" (complete document) |
| get_prompt | Get a prompt by Convex document ID |
Templates
| Tool | Description |
|------|-------------|
| upsert_template | Create or update a template (upsert by name). New in 1.8.0: template_type="doc-binary" + 4 doc-binary fields |
| list_templates | List templates — optional team and template_type filters. fields="lite" (compact, default) or fields="full" (complete document) |
| get_template | Get a template by Convex document ID |
| detect_template_drift | Return per-template contentHash + provenance for drift comparison |
| list_templates_by_category | List templates for a specific category (uses byCategory index). fields="lite" (compact, default) or fields="full" (complete document) |
template_type enum (all values):
"standard" | "mission" | "brief" | "runbook" | "document" | "checklist" | "doc-binary"doc-binary template type (new in 1.8.0)
Use template_type="doc-binary" for Living Bible binary document templates. Four additional optional fields become meaningful:
| Field | Type | Description |
|-------|------|-------------|
| renderer_kind | string | Renderer identifier — e.g. "pdf", "docx", "html" |
| input_schema | string | JSON-encoded schema string describing template inputs |
| assets | object | Asset URLs: { icon?, banner?, thumbnail? } |
| output_formats | string[] | Supported output formats — e.g. ["pdf", "docx"] |
Example: upsert a doc-binary template
upsert_template(
name="employee-contract-v2",
team="hephaistos",
purpose="Generates employee contract PDFs from structured input",
content="# Employee Contract\n\n{{name}}, {{start_date}}...",
version="1.0.0",
template_type="doc-binary",
category="hr/contracts",
renderer_kind="pdf",
input_schema="{\"name\":{\"type\":\"string\"},\"start_date\":{\"type\":\"string\"}}",
assets={"icon": "https://cdn.example.com/icons/contract.svg"},
output_formats=["pdf", "docx"]
)Template Consumers (new in 1.8.0)
The template_consumers junction table links templates to the components/BUs that consume them. This enables the Living Bible pattern — tracking which consumers depend on each doc-binary template.
Link type semantics (linkType):
"uses"— consumer actively consumes/renders this template"produces"— consumer generates output documents from this template"references"— informational cross-reference
| Tool | Type | Description |
|------|------|-------------|
| link_template_consumer | write | Upsert a link by (templateId, consumerName). Returns {linkId, created: true} on insert, {linkId, created: false} on update |
| unlink_template_consumer | write | Hard-delete the link by (templateId, consumerName). Returns {unlinked: true} if found, {unlinked: false} if not |
| list_consumers_for_template | read | List all consumers for a template — returns link objects with consumerName, consumerBU, consumerComponentId, linkType, createdAt. Optional linkType filter. No fields param — always returns the lite projection, no full-document option |
| list_templates_for_consumer | read | Reverse lookup — enriched [{link, template}] for a consumerName. Optional linkType filter. Orphaned links skipped. No fields param — always returns the lite projection, no full-document option |
link_template_consumer params:
| Param | Type | Required | Description |
|-------|------|----------|-------------|
| templateId | string | yes | Convex document ID of the template |
| consumerName | string | yes | Stable slug identifying the consumer — e.g. "hephaistos-doc-forge" |
| consumerBU | string | no | Business unit slug — e.g. "hephaistos", "sigma", "omega" |
| consumerComponentId | string | no | Convex document ID of the component row (omit for BU-level consumers without a registered components row) |
| linkType | string | no | "uses", "produces", or "references" — omit to leave untyped |
| createdBy | string | no | Orchestrator or script creating this link — e.g. "omega" |
unlink_template_consumer params:
| Param | Type | Required | Description |
|-------|------|----------|-------------|
| templateId | string | yes | Convex document ID of the template |
| consumerName | string | yes | Slug identifying the consumer |
list_consumers_for_template params:
| Param | Type | Required | Description |
|-------|------|----------|-------------|
| templateId | string | yes | Convex document ID of the template |
| linkType | string | no | Filter by link type — omit to return all |
list_templates_for_consumer params:
| Param | Type | Required | Description |
|-------|------|----------|-------------|
| consumerName | string | yes | Slug identifying the consumer |
| linkType | string | no | Filter by link type — omit to return all |
Example invocations:
# Link a consumer to a template
link_template_consumer(
templateId="j57abc123...",
consumerName="hephaistos-doc-forge",
consumerBU="hephaistos",
linkType="uses",
createdBy="omega"
)
# Remove a consumer link
unlink_template_consumer(templateId="j57abc123...", consumerName="hephaistos-doc-forge")
# Who consumes this template?
list_consumers_for_template(templateId="j57abc123...")
# What templates does this consumer use?
list_templates_for_consumer(consumerName="hephaistos-doc-forge", linkType="uses")Runbooks
Added in 1.1.0. Runbooks are structured, versioned operational playbooks stored in VantageRegistry.
| Tool | Description |
|------|-------------|
| upsert_runbook | Create or update a runbook (upsert by name) |
| get_runbook | Get a runbook by slug — optionally guard by version |
| list_runbooks | List runbooks — optional status, category, team, applicability filters. fields="lite" (compact, default) or fields="full" (complete document) |
| list_runbooks_by_category | List runbooks for a specific category (uses byCategory index). fields="lite" (compact, default) or fields="full" (complete document) |
| list_runbooks_by_team | List runbooks for a specific team (uses byTeam index). fields="lite" (compact, default) or fields="full" (complete document) |
| delete_runbook | Soft-delete a runbook — sets status to deprecated |
| detect_runbook_drift | Return per-runbook vrHash (sha256 of content) + category + version for drift comparison (added in 1.6.0) |
Runbook status values: draft | published | deprecated
Example invocations:
# Get a specific runbook
get_runbook(name="deploy-production", version="1.0.0")
# List all published deployment runbooks
list_runbooks(status="published", category="deployment")
# List all runbooks for the core team
list_runbooks_by_team(team="core")
# List all published runbooks in the quality category
list_runbooks_by_category(category="quality", status="published")
# Soft-delete a runbook
delete_runbook(name="old-process-v1")Upsert a runbook (minimal example):
upsert_runbook(
name="onboard-agent",
description="Standard protocol to onboard a new agent to the fleet",
version="1.0.0",
status="draft",
category="onboarding",
tags=["agent", "onboarding", "fleet"],
content="# Onboard Agent\n\n...",
phases=[{ name="prep", description="Preparation phase", steps=[], gate=null }],
inputs=[],
outputs=[],
applicability={ orchestrators=["omega"], business_units=[], use_cases=[] },
author="omega",
team="core",
related_skills=[],
related_agents=[],
linked_templates=[]
)Components Catalog
| Tool | Description |
|------|-------------|
| register_component | Create or update a component (upsert by name+kind) |
| list_components | List components — optional kind, ownerTeam, status, tags filters. New in 1.8.0: response includes consumers, clientReadyStage, lastUsageSignal. fields="lite" (compact, default) or fields="full" (complete document) |
| get_component | Get a component by Convex document ID. New in 1.8.0: response includes consumers, clientReadyStage, lastUsageSignal |
| search_components | Search components by name prefix |
| update_component | Patch individual fields on an existing component |
| delete_component | Soft-delete a component — sets status to deprecated |
Component Bible fields (new in 1.8.0)
Three fields are now surfaced in list_components and get_component responses:
| Field | Type | Description |
|-------|------|-------------|
| consumers | string[] | Downstream orchestrators / agents that consume this component — e.g. ["omega", "sigma"] |
| clientReadyStage | string | Readiness stage for client-facing exposure: "internal-only", "dogfood", "private-beta", or "public-cloud-paid" |
| lastUsageSignal | object | Latest usage signal: { source, value, capturedAt, raw? } — auto-updated on each get_component / list_components call when VR_ADMIN_WRITE_SECRET is configured |
These fields can be set via register_component or update_component:
register_component(
name="check-messages",
kind="skill",
status="active",
ownerTeam="core",
consumers=["omega", "pi", "sigma"],
clientReadyStage="internal-only"
)Process Components
| Tool | Description |
|------|-------------|
| list_process_components | List process components — reusable orchestrator process templates (mission template + runbook + skills/rules/hooks layers + scripts + deliverable schema + example deliverables + eval), each with a complete flag over its declared layers. Reuses the existing processComponents:listProcessComponents catalogue query — no new catalogue logic. No fields param — always returns the lite projection, no full-document option |
Args:
| Field | Type | Description |
|-------|------|-------------|
| limit | number (optional) | Max items to return, 1-500. Default 100. |
Return shape — array of:
{
"name": "string",
"stage": "prototype | beta | ga",
"consumers": ["omega", "sigma"],
"layersPresent": {
"missionTemplate": true,
"runbook": true,
"skills": 2,
"rules": 1,
"hooks": 0,
"hooksApplicable": false,
"scripts": 1,
"deliverableSchema": true,
"exampleDeliverables": 1,
"eval": true
},
"complete": true,
"freshness": 1755000000000,
"updatedAt": 1755000000000
}complete is computed server-side from layersPresent: missionTemplate, runbook, >=1 skills, >=1 rules, >=1 scripts, eval, deliverableSchema, and >=1 exampleDeliverables are all required; hooks is only required (>=1) when hooksApplicable is true.
Process components are stored as public-fleet components rows (kind: "template", tagged process-component), scoped read-only — no admin secret required.
Example
list_process_components(limit=50)Cohabitant tests (testContent)
The testContent field lets you store a test file body atomically alongside the production content of any hook, skill, or agent — a single Convex document holds both. This is the "cohabitant test" pattern: the test lives in the registry next to the artifact it covers, versioned independently.
Tools that carry testContent:
| Tool | Direction | testContent behaviour |
|------|-----------|----------------------|
| upsert_hook_content | write | Optional testContent param — stored atomically with content |
| upsert_skill_content | write | Optional testContent param — stored atomically with content |
| upsert_agent_content | write | Optional testContent param — stored atomically with content |
| get_hook_content | read | Returns testContent, testContentHash, testContentVersion |
| get_skill_content | read | Returns testContent, testContentHash, testContentVersion |
| get_agent_content | read | Returns testContent, testContentHash, testContentVersion |
| detect_hook_drift | read | Returns testHash per entry alongside vrHash |
| detect_skill_drift | read | Returns testHash per entry alongside vrHash |
| detect_agent_drift | read | Returns testHash per entry alongside vrHash |
Fields returned by get_*_content (new in 1.9.0):
| Field | Type | Description |
|-------|------|-------------|
| testContent | string | null | Raw bytes of the test file; null if not set |
| testContentHash | string | null | SHA-256 of the stored test bytes |
| testContentVersion | number | Backend patch counter; increments only on byte change |
detect_*_drift testHash field:
detect_hook_drift, detect_skill_drift, and detect_agent_drift return a testHash per entry. Compare it against sha256(fs.readFileSync(testFilePath)) client-side to detect drift in the test file independently of the production content SHA.
Example: upsert a skill with its test file
upsert_skill_content(
name="check-messages",
content="# Check Messages skill\n\n...",
testContent="import { describe, it, expect } from 'vitest'\n\ndescribe('check-messages', () => {\n it('returns messages', () => { ... })\n})"
)Example: read back production + test content
get_skill_content(name="check-messages")
# returns: { content, vrContentHash, vrContentVersion, testContent, testContentHash, testContentVersion }Relation to eval-corpus: testContent is complementary to, not a replacement for, upsert_skill_eval_corpus. Eval-corpus stores structured prompt/response pairs for automated quality gates. testContent stores the raw test file (a pytest, vitest, or bun test file) that lives alongside the artifact. Both can coexist on the same document.
Idempotency: testContentVersion increments only when the stored bytes change. A re-upsert with identical testContent leaves the version and hash unchanged — no false-positive drift signal is produced.
Stats
| Tool | Description |
|------|-------------|
| get_stats | Registry overview — UNCLAMPED counts per table (including rules) plus runbooks breakdown |
Fixed in this release (O3, catalogue-categories-et-portee): get_stats used to recompose its counts from api.agents.list / api.skills.list / api.hooks.list called with no paginationOpts/limit — each one silently fell back to the backend's default clamp (50 rows) and rules was never counted at all. get_stats now calls the convex-side unclamped source of truth directly (stats:getCatalogStats + stats:getRunbookStats, ctx.db.query(table).collect().length — no .take() clamp anywhere in the path), so every count — including any table past 50 rows — is exact.
get_stats response shape:
{
"teams": 18,
"agents": 148,
"skills": 328,
"plugins": 16,
"hooks": 22,
"prompts": 14,
"templates": 9,
"rules": 42,
"runbooks_count": {
"total": 12,
"by_status": {
"draft": 3,
"published": 8,
"deprecated": 1
},
"by_category": {
"deployment": 4,
"quality": 4,
"onboarding": 4
}
},
"total": 570
}Domain category axis (new — O3, catalogue-categories-et-portee)
Rules and skills carry an optional domainCategory field — a CLOSED 8-literal enum, distinct from a skill's category FORM field (capability/composite/playbook/root/external):
deployment-and-delivery | quality-and-tests | fleet-coordination |
review-and-merge | agents-and-plugins | technical-foundation |
onboarding | secrets-and-productionBoth rules and skills also carry an optional scopeOrchestrators: string[] — the list of orchestrators the row concerns (e.g. ["marketing", "omega"]).
| Tool | Description | Page size | Continuation |
|------|-------------|-----------|--------------|
| list_rules_by_domain | Rules for a domainCategory, optionally narrowed to a scopeOrchestrator | default 20, hard cap 200 | pass nextCursor back as cursor for the next page — absent nextCursor is the ONLY signal a listing is complete; a domain bucket bigger than one page NEVER silently truncates |
| list_skills_by_domain | Skills for a domainCategory, optionally narrowed to a scopeOrchestrator | default 20, hard cap 200 | same cursor contract as above |
Backend: both tools are cursor-paginated via native Convex .paginate() (rules.listByDomainCategory / skills.listByDomainCategory, indexed on by_domainCategory) — never a bare .take() cap. A page MAY contain fewer rows than requested when scopeOrchestrator narrows the result (the filter runs AFTER pagination, same semantics as every other cursor-paginated VR list tool) — keep paging via nextCursor until it is absent, not until the page looks "full".
Example: pull every rule + skill a marketing orchestrator needs to onboard, in one command each:
list_rules_by_domain(domainCategory="onboarding", scopeOrchestrator="marketing")
list_skills_by_domain(domainCategory="onboarding", scopeOrchestrator="marketing")
# If either response carries a nextCursor, there is more — page until it's absent:
list_rules_by_domain(domainCategory="onboarding", scopeOrchestrator="marketing", cursor="<nextCursor>")Schema
template_consumers table (new in 1.8.0)
| Field | Type | Notes |
|-------|------|-------|
| templateId | Id<"templates"> | Foreign key — template being consumed |
| consumerName | string | Stable slug identifying the consumer |
| consumerBU | string? | Business unit slug — e.g. "hephaistos" |
| consumerComponentId | Id<"components">? | Optional FK to components row |
| linkType | "uses" \| "produces" \| "references"? | Semantic relationship type |
| createdAt | number | Unix timestamp (ms) |
| createdBy | string? | Orchestrator or script that created this link |
Indexes: byTemplate, byConsumer, byTemplateAndConsumer (composite — enforces at-most-one link per pair)
runbooks table (new in 1.1.0)
| Field | Type | Notes |
|-------|------|-------|
| name | string | Unique slug — upsert key |
| description | string | Purpose summary |
| version | string | Semantic version |
| status | draft \| published \| deprecated | Lifecycle state |
| category | string | e.g. deployment, quality, onboarding |
| tags | string[] | Searchable tags |
| content | string | Full markdown content |
| phases | Phase[] | Ordered phases with steps and gates |
| inputs | RunbookInput[] | Required input parameters |
| outputs | RunbookOutput[] | Produced output artifacts |
| applicability | object | orchestrators, business_units, use_cases slugs |
| author | string | Author slug |
| team | string | Owning team slug |
| related_skills | string[] | Referenced skill slugs |
| related_agents | string[] | Referenced agent slugs |
| linked_templates | LinkedTemplate[] | Templates used within this runbook |
Indexes: byName, byStatus, byCategory, byTeam
templates table
template_type discriminator field — all values as of 1.8.0:
| Value | Description |
|-------|-------------|
| standard | Standard template |
| mission | Mission brief template |
| brief | Agent/task brief |
| runbook | Runbook template |
| document | Free-form document |
| checklist | Checklist template |
| doc-binary | Living Bible binary document template (new in 1.8.0) — use with renderer_kind, input_schema, assets, output_formats |
components table — Bible fields (new in 1.8.0)
| Field | Type | Notes |
|-------|------|-------|
| consumers | string[]? | Downstream orchestrators / agents that consume this component |
| clientReadyStage | string? | "internal-only" | "dogfood" | "private-beta" | "public-cloud-paid" |
| lastUsageSignal | object? | { source, value, capturedAt, raw? } — auto-updated on every read |
Requirements
- Bun runtime (server is authored in TypeScript for Bun)
CONVEX_URLenvironment variable pointing to the VantageRegistry Convex deployment (use.convex.cloudURL, not.convex.site)
ADMIN mode vs read-only mode (new in 1.7.0)
The MCP server operates in one of two modes depending on token configuration:
ADMIN mode (write operations enabled)
Requires both environment variables:
MCP_BEARER_TOKEN=<admin-token> # HTTP transport only — identifies admin callers
VR_ADMIN_WRITE_SECRET=<secret> # backend write guard secret (from PR #93)Write tools (upsert_*, delete_*, register_component, update_component,
link_runbook_template, unlink_runbook_template, link_template_consumer,
unlink_template_consumer) inject adminSecret into every Convex mutation call server-side.
adminSecret is never exposed in tool input schemas — clients do not provide it.
Read-only mode (write operations refused)
When the server is configured with MCP_BEARER_TOKEN_READONLY only (no admin token), all
write tool calls are refused:
- HTTP transport: returns
403 Forbiddenbefore the handler executes. - stdio transport:
adminWriteArgs()throws immediately — zero mutations fired.
A read-only instance cannot be made to write even if VR_ADMIN_WRITE_SECRET is present in
its environment. The ADMIN mode gate is checked before the secret is injected.
Write tool list (all require ADMIN mode + VR_ADMIN_WRITE_SECRET)
upsert_team, upsert_agent, upsert_skill, upsert_skill_content,
upsert_agent_content, upsert_plugin_content, upsert_hook_content,
upsert_command_content, upsert_plugin, upsert_hook, upsert_prompt,
upsert_template, upsert_test_run, upsert_skill_eval_corpus,
upsert_runbook, delete_runbook, link_runbook_template,
unlink_runbook_template, link_template_consumer, unlink_template_consumer,
register_component, update_component, delete_component
Read tool list (work in both modes, no secret required)
All list_*, get_*, detect_*, search_components, get_stats.
Changelog
See CHANGELOG.md in this directory.
Orchestrator: Omega — VantageOS Team | 2026-06-18
