Track 3 — Agent Orchestration¶
The voyage is planned. The crew is confirmed. The cargo is scheduled. And now operations walks in with the final ask: "I need one document. Voyage, ship, crew, cargo — all of it, resolved, in one payload. Before the ship leaves."
Three separate calls and client-side stitching would work. But we already
have the patterns (Track 1 Step 7) and the abstraction (Track 2 Step 9). This
track scales them up: four call steps and three lookup steps, defined
once as an aggregate function, referenced from MCP, Skill, and REST
simultaneously — and split across multiple files for maintainability.
⚓ Prerequisites. Track 1 and Track 2 completed. Files live in
ikanos-docs/tutorial/in the public Ikanos repository.
Step 11 — The Fleet Manifest¶
Files:
Shared consumes¶
The Fleet Manifest needs the Maritime Registry, the Crew API, the Cargo API,
and a Legacy Vessels XML feed. Rather than redeclaring consumes in every
capability, factor them into shared files imported via from: / import:.
# step-11-shipyard-fleet-manifest.yml
ikanos: "1.0.0-alpha3"
capability:
consumes:
- from: "shared/step11-registry-consumes.yml"
import: registry
- from: "shared/legacy-consumes.yaml"
import: legacy
This is the same trick that keeps a multi-capability fleet maintainable — one team owns the consumes declaration for an API, every capability reuses it.
The seven-step aggregate¶
capability:
aggregates: # alpha3 — keyed map by namespace
manifest:
display: "Voyage Manifest"
flows: # alpha3 — keyed map by flow name (was `functions`)
build-fleet-manifest:
description: "Resolve voyage + ship + crew + cargo into a single manifest."
semantics: { safe: true, idempotent: true, cacheable: true }
inputParameters:
voyageId: { type: string, required: true }
steps: # alpha3 — keyed map by step name
get-voyage:
type: call
call: registry.get-voyage
with: { voyage_id: manifest.voyageId }
get-ship:
type: call
call: registry.get-ship
with: { imo_number: "$.get-voyage.imo_number" }
list-crew:
type: call
call: registry.list-crew
get-cargo:
type: call
call: registry.get-cargo
with: { voyage_id: manifest.voyageId }
resolve-captain:
type: lookup
index: list-crew
match: crewId
lookupValue: "$.get-ship.captainId"
outputParameters: ["fullName", "certifications"]
resolve-crew:
type: lookup
index: list-crew
match: crewId
lookupValue: "$.get-ship.assignedCrew"
outputParameters: ["fullName", "role"]
resolve-legacy:
type: lookup
index: legacy.list-vessels
match: imoNumber
lookupValue: "$.get-ship.imo_number"
outputParameters: ["lastSurvey"]
mappings:
- { target: voyage, value: "$.get-voyage" }
- { target: ship, value: "$.get-ship" }
- { target: captain, value: "$.resolve-captain" }
- { target: crew, value: "$.resolve-crew" }
- { target: cargo, value: "$.get-cargo" }
- { target: lastSurvey, value: "$.resolve-legacy.lastSurvey" }
Seven steps, three data sources (registry, crew, legacy), all stitched server-side. The agent gets a single JSON document.
One aggregate, three doors¶
The same manifest.build-fleet-manifest aggregate appears on all three expose
adapters:
exposes:
- type: mcp
namespace: shipyard-tools
tools: # alpha3 — keyed map
build-fleet-manifest:
ref: manifest.build-fleet-manifest
inputParameters:
voyageId: { type: string, required: true }
with: { voyageId: shipyard-tools.voyageId }
- type: skill
namespace: shipyard-skills
skills: # alpha3 — keyed map
ops-handover:
description: "End-to-end voyage handover documents"
tools: # alpha3 — keyed map
build-fleet-manifest:
from: { sourceNamespace: shipyard-tools, action: build-fleet-manifest }
- type: rest
namespace: shipyard-api
resources: # alpha3 — keyed map
voyage-manifest:
path: /voyages/{voyageId}/manifest
operations: # alpha3 — keyed map
get-manifest:
method: GET
ref: manifest.build-fleet-manifest
inputParameters:
voyageId: { in: path, required: true }
with: { voyageId: shipyard-api.voyageId }
🧭 What you learned: unified
from/importfor shared consumes, multi-step orchestration at scale, mixingcallandlookupsteps, the same aggregate exposed via MCP + Skill + REST.
Patterns to take away¶
| Pattern | When to reach for it |
|---|---|
from: / import: shared consumes |
More than one capability uses the same API |
aggregates + ref |
Same logic on more than one expose adapter |
type: lookup steps |
Joining two API responses in-memory |
semantics: |
Anything read-only, idempotent, or cacheable |
| Multi-adapter expose | Mixed audience (AI agents + web app + partner) |
Next steps¶
- Track 4 — Platform Operations — what the engine looks like in production: linting, observability, environments.
- Components → Ikanos → Schema — the full reference for every keyword used in this track.