# Openchoreo Developer

> Application-level OpenChoreo work via the control-plane MCP server — deploying services, configuring workloads, consuming managed-infrastructure Resources, promoting releases, managing secret references, inspecting runtime. Use when the user says 'deploy my service', 'add a component', 'rebuild from source', 'use a database', 'promote to staging', 'rollback', or 'why is my pod crashing'.

- Skill: `openchoreo/openchoreo-developer` (Agent Skill, multi-file: 13 files)
- Install (CLI): `npx skillmds@latest add openchoreo/openchoreo-developer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/openchoreo/openchoreo-developer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: openchoreo (https://skillmd.com/u/openchoreo)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/openchoreo/openchoreo-developer

---


# OpenChoreo Developer Guide

Help an application developer ship and operate a service on OpenChoreo through the control-plane MCP server. Discover the live platform shape via MCP; read detailed references only when the task needs them.

## Step 0 — Confirm MCP connectivity

Run `list_namespaces`. If the tool isn't reachable, tell the user the `openchoreo-cp` MCP server needs configuring per <https://openchoreo.dev/docs/ai/mcp-servers/> and stop.

## Step 1 — Load concepts (MANDATORY)

**Read [`./references/concepts.md`](./references/concepts.md) in full before anything else.** Not optional, even if the task looks simple — you'll get these wrong from memory. Load once per session; if you catch yourself acting without it, stop and load now.

Then load the matching reference for the task:

- **First-time deploy** (no Project yet, or first time touching OpenChoreo) → [`./references/getting-started.md`](./references/getting-started.md)
- **Working with existing Components** → straight to the matching recipe. To pick between BYO and source-build for an existing Component, `get_component` and check `spec.workflow`: present → [`build-from-source.md`](./references/recipes/build-from-source.md), absent → [`deploy-prebuilt-image.md`](./references/recipes/deploy-prebuilt-image.md).

## Step 2 — Load the PE skill for platform tasks

If the request touches `ProjectType` / `ComponentType` / `ResourceType` / `Trait` / `Workflow` (or cluster variants) or anything under *What this skill cannot do*, also load [`../openchoreo-platform-engineer/SKILL.md`](../openchoreo-platform-engineer/SKILL.md). If the PE skill isn't installed, escalate.

## What this skill can do

- **Projects** — create, update. `create_project` takes an optional `type_name` / `type_kind` / `parameters` referencing a PE-authored `(Cluster)ProjectType`; omitted, it defaults to the `default` ProjectType. A project's **cell** must be deployed per environment before its components can be → see *Deploy the project cell* in [`deploy-and-promote.md`](./references/recipes/deploy-and-promote.md).
- **Components** — create, update, `patch_component` for `auto_deploy` / `parameters` / `traits` / `workflow` / metadata.
  - BYO image → [`deploy-prebuilt-image.md`](./references/recipes/deploy-prebuilt-image.md)
  - Source-build → [`build-from-source.md`](./references/recipes/build-from-source.md)
- **Workloads** — image, ports, endpoints, env vars, files → [`configure-workload.md`](./references/recipes/configure-workload.md)
- **Attach Traits** — pick from the platform's catalog → [`configure-workload.md`](./references/recipes/configure-workload.md)
- **Connect components** — endpoint dependencies; platform injects env vars → [`connect-components.md`](./references/recipes/connect-components.md)
- **Consume Resources** — managed-infrastructure dependencies (databases, queues, caches); platform injects outputs as env vars and file mounts via `dependencies.resources[]` → [`use-a-resource.md`](./references/recipes/use-a-resource.md)
- **SecretReferences** — CRUD + `secretKeyRef` consumption → [`manage-secrets.md`](./references/recipes/manage-secrets.md). The `ClusterSecretStore` is PE-owned.
- **Deploy and promote** — deploy the project cell per env (`create_project_release_binding`, pin seeded by the controller), then bind a `ComponentRelease` to an Environment and promote both along the DeploymentPipeline → [`deploy-and-promote.md`](./references/recipes/deploy-and-promote.md)
- **Per-environment overrides** — replicas, resources, env vars, trait config on the ReleaseBinding → [`override-per-environment.md`](./references/recipes/override-per-environment.md)
- **Soft-undeploy / rollback** — `update_release_binding release_state: Undeploy`, or rebind to a prior `ComponentRelease`.
- **Hard-delete developer resources** — `delete_component`, `delete_workload`, `delete_release_binding`, `delete_project`, `delete_component_release`. Destructive; confirm first. **No `delete_namespace`** — PE-side.
- **Inspect runtime** — Component / ReleaseBinding `status.conditions[]` and `status.endpoints[]`; `get_resource_tree` to map a binding to its rendered K8s resources; `get_resource_events` / `get_resource_logs` for pod-level evidence; WorkflowRun logs and events → [`inspect-and-debug.md`](./references/recipes/inspect-and-debug.md)
- **Discover platform resources** (read-only) — ProjectTypes, ComponentTypes, ResourceTypes, Traits, Workflows, Environments, DeploymentPipelines, planes.

## What this skill cannot do

Platform-side work: authoring ProjectTypes / ComponentTypes / ResourceTypes / Traits / Workflows, Environments, DeploymentPipelines, planes, authorization, gateway / secret-store / IdP config, observability setup, longer-horizon log / metric / trace queries (pod-level events and current logs *are* covered via `get_resource_events` / `get_resource_logs`).

Load `../openchoreo-platform-engineer/SKILL.md` if it's installed; otherwise tell the user to escalate to a platform engineer. PE scope catalog: <https://openchoreo.dev/docs/platform-engineer-guide/>.

## Tool surface

One MCP server: `openchoreo-cp`. Throughout this skill, tools are referenced by bare name (e.g. `get_component`); your agent wraps with its prefix (Claude Code uses `mcp__openchoreo-cp__<tool>`).

ComponentType / ResourceType / Trait / Workflow and the plane tools are **scope-collapsed**: one tool with a `scope` arg — `"namespace"` (default) or `"cluster"` for the platform-wide `Cluster*` resource. This skill always uses the canonical name + `scope`. The old `*_cluster_*` names still exist as deprecated aliases (banner in v1.1, hidden in v1.2, removed in v1.3) and can be used alternatively against a v1.1 server — but prefer the canonical form.

The `resource` toolset (dev-facing, enabled by default) covers `Resource` CRUD plus scope-collapsed reads on `(Cluster)ResourceType`. `ResourceReleaseBinding` and `ProjectReleaseBinding` CRUD both sit in the `deployment` toolset alongside `ReleaseBinding`. The `resource_release_binding` create/update calls are how a Resource gets deployed into an env (or promoted); `project_release_binding` create/update are the same for a project's cell. `(Cluster)ProjectType` is read-only here (`list_project_types` / `get_project_type` / `get_project_type_schema`, `scope`-aware) — authoring it is PE-side.

## Working style

- **Live cluster output beats memory.** Discover via MCP first; don't guess available ComponentTypes / Traits / Workflows / Environments / field names.
- **Schema-first authoring.** `get_workload_schema`, or `get_component_type_schema` / `get_trait_schema` with `scope: "cluster"` for platform-wide standards, before writing a spec from scratch.

## Stable guardrails

- All work goes through the control-plane MCP server. If a task can't be done via MCP, it's platform-side — hand off.
- **Third-party / public apps: default to BYO image.** Source builds commonly fail on third-party Dockerfiles using `ARG BUILDPLATFORM` (exit 125). Switch to BYO immediately if you see it.
- **Before deploying any third-party app:** fetch the official manifests and extract every required env var — dependencies inject service addresses but not `PORT`, feature flags, or vendor SDK disable flags.
- **A handed-over migration plan is the spec.** When the user supplies a migration/onboarding plan, take namespace, env var placement (static Workload env vs per-env `workloadOverrides`), and wiring decisions from it. Deviate only out loud — state what you're changing and why *before* acting, never silently substitute.
- **Author types to fit the app — never re-shape the app to fit shipped types.** When the plan (or the app) needs a `ComponentType` / `ResourceType` / `Trait` that isn't installed *or that ships but doesn't meet the requirement*, the fix is to **author it** — load `../openchoreo-platform-engineer/SKILL.md` and create a fit-for-purpose type. Do **not** work around the gap by: re-modeling a managed dependency as a plain Component because the shipped `ResourceType` doesn't fit (it's still a Resource — author a `ResourceType` that reproduces what the dependency needs); dropping a capability the app needs because no shipped Trait provides it (author the Trait); or forcing the workload onto a shipped `ComponentType` whose shape or defaults don't fit (author the `ComponentType`). Reuse a shipped type **only** when it genuinely meets the contract. The needs drive the types, not the reverse.
- **Set resources from the source, not the ComponentType's defaults.** Use the workload's source-declared requests/limits (CPU *and* memory). If the source declares none, pick a plausible default for the workload class — never inherit a generic CT's defaults, which are typically demo-grade and under-provision. When you author the CT, bake sensible defaults into its `environmentConfigs`; otherwise set them via the ReleaseBinding's per-env config.
- **Injected connection values are literal — there is no `$(VAR)` interpolation.** Dependency `envBindings` inject each connection value (`address` / `host` / `port` / `basePath`) as the literal env var you name; the platform does **not** expand shell/Kubernetes `$(VAR)` references inside other env values, so `value: "http://$(HOST):$(PORT)"` reaches the container verbatim. Bind the exact env var the app reads. If the app needs a composed string the injected vars don't directly provide (a full URI, a bare `host:port` with no scheme, a scheme prefix), set that composed value as a literal env var — typically a per-env `workloadOverride` using the resolved address — rather than referencing `$(VAR)`.
- **A missing tool means version skew, not absence.** When a documented MCP tool isn't found, check the server version against the cluster before concluding the surface doesn't exist — report the mismatch to the user, then fall back or escalate.

## Anti-patterns

- **Skipping the recipe.** Before any new operation (new CRD kind this turn, lifecycle action, runtime inspection) — re-scan the recipe index above, load the matching recipe (one Read call), THEN call MCP / kubectl. Skipping is how kubectl falls multiply and existing MCP tools get missed. Concept references aren't enough — recipes name the right tool calls in sequence.
- Running every discovery call before checking the resource already implicated.
- Writing specs from memory when `get_*_schema` / `get_*` can reveal the current shape.
- Guessing deployed URLs instead of reading `ReleaseBinding.status.endpoints[]`.
- Treating a platform-side failure as an app-only problem after the evidence points elsewhere.
- Creating source-build components for third-party apps that have pre-built images.
- Setting `visibility: external` on a service-to-service dependency in the same project — `project` is the default.
- **Treating `Ready=True` as "working".** Ready means reconciled, not functional. A crash-looping container can flap Ready, and a stable container can be Ready while misconfigured (env vars bound to wrong names, deps resolving to nowhere). Confirm with `get_resource_tree` → `get_resource_events` / `get_resource_logs` and an actual endpoint hit.

