Create Railcode Agent
Update First
Before answering a Railcode agent or CLI question or running a
command, update
the installed Railcode skills and CLI, then confirm npm's published version:
bash
npx skills add Railcode-HQ/railcode-skills
npm install -g railcode@latest
railcode --version
npm view railcode version
If the skill changes, re-read this file from the top. If npm is unreachable, state that the
latest version could not be verified and do not claim this guidance is current. This version
was checked against published
Railcode CLI 0.1.28. Agent input is free-form; the
prompt defines its contract. The backend validates manifest tools and limits, so save-time
validation is authoritative over this snapshot.
Since 0.1.28 the CLI self-updates within its major version — but only on an
interactive
terminal, and agent-driven sessions are non-interactive, so keep running the explicit
npm install -g railcode@latest
above rather than assuming you're on the latest.
Map The Request To Railcode
Use this table before authoring the manifest. If the request names an external product or
data source, always check data connections/saved queries, service connectors, and personal
connectors before deciding what is available; the detailed discovery commands are in the
scoping step.
Hard file boundary: whenever AI must
read, understand, extract, summarize, transform, or
generate a file, use a
managed agent with
and its sandbox. The companion
app may upload, store, list, download, and display files, but its in-page
/
must never consume file contents, file URLs, file-derived payloads, or generate
file artifacts as a substitute. Publish durable results back through
.
| What the user asks for | Use this Railcode feature |
|---|
| "Analyze company metrics/orders/customers from our database" | (default); use only when direct SQL is explicitly requested |
| "Use our team's shared Stripe, CRM, or other SaaS account" | plus ; the org owns the service-connector credential |
| "Use my Gmail, Slack, or another account I personally connected" | tools.personal_connectors
on a personal agent; calls run as the agent owner |
| "Connect my account to a product Railcode does not bundle" | A custom MCP personal connector works for an app, not an agent; offer a companion app, service connector, or data import path |
| "Read records people manage in a Railcode app" | , usually with a companion app |
| "Read, extract, summarize, transform, or generate a file with AI" | Managed agent with + sandbox; never the companion app's in-page LLM |
| "Edit this Word document / DOCX and preserve it as a file" | Managed agent + companion app: the app stores/manages source and output files; declare to load the DOCX and to publish the edited document |
| "Create or revise a PowerPoint / PPTX deck" | Managed agent + companion app: the app manages templates, inputs, and generated decks; use the sandbox to create/edit the PPTX and to return it |
| "Create a PDF report, form, or document" | Managed agent + companion app: the app manages inputs and downloadable outputs; generate and verify the PDF in the sandbox, then publish it through |
| "Analyze this Excel / XLSX workbook" | Managed agent + companion app: the app stores the workbook and results; load it through , parse/analyze it in the sandbox, and publish durable results through |
| "Write results or publish an artifact back to an app" | ( / ) |
| "Remember state between runs" | ; use app storage instead when humans need to view or edit it |
| "Parse files, produce documents, or run code" | Managed-agent sandbox plus ; publish durable outputs back to an app with |
| "Email a report from the system" | ; use a Gmail personal connector when it must send from the owner's own mailbox |
| "Run every morning, from Slack, or after a browser closes" | Managed agent plus a cron schedule or the built-in Slack invocation path |
| "Give people a UI to upload inputs, trigger runs, or review results" | A companion Railcode app using / |
| "Call an arbitrary website/API" | First look for service or bundled personal connectors; otherwise offer connector setup/import—a managed agent cannot fetch the open web or declare custom MCP toolkits |
Sandbox Capabilities
Managed agents run in a per-run code sandbox when sandboxing is configured for the deployment;
there is no manifest key or per-agent switch to request it. The sandbox provides shell and file
tools so the agent can write and run code, inspect and transform files, and use appropriate
libraries for tasks such as parsing Excel/CSV data, extracting or assembling PDFs, editing
documents, unpacking archives, and producing generated artifacts.
The sandbox is ephemeral and carries no credentials or standing org access. Bring tenant files
in with
, reach other systems only through explicit manifest authorities, and
publish durable files or records back through
. Anything left only in the
sandbox disappears when the run ends.
When To Use A Managed Agent vs The In-Page LLM
A
managed agent (this skill) runs server-side under its own ratified manifest, with a
code sandbox and durable, auditable runs. The
in-page LLM (
/
with
, via
) runs in the app viewer's tab with the app's SDK
authority and dies with the tab. Pick the first matching row:
| The AI feature… | Use |
|---|
| Summarizes / classifies / analyzes data the app already reads — user watching, done in seconds | In-page LLM |
| Reads, understands, extracts, summarizes, transforms, or generates any file | Managed agent ( + sandbox); never the in-page LLM |
| Writes and runs code | Managed agent (sandbox) |
| Is triggered outside the app (Slack, cron, API) | Managed agent |
| Runs unattended, must survive tab close, or needs retries | Managed agent |
| Has effects that must not depend on who's viewing (shared writes, send as the system) | Managed agent |
| Needs a run history someone will audit or debug | Managed agent |
The planes compose: the app keeps its chat shell in the page and delegates heavy steps by
calling
/
from an LLM tool's
(the app manifest declares
; this agent declares
to reach uploaded files).
Start From An Example
Railcode ships worked, deployable examples at
https://github.com/Railcode-HQ/railcode-examples. Read them to learn a pattern; copy one
when it covers much of what the user is asking for. Each
example is a
companion app
plus its agent manifests, which is the shape most agent work takes.
| Example | What it is | Showcases |
|---|
| An app for uploading company materials, paired with an agent that writes a polished pitch-deck PDF from them. | App-paired managed agents: / access, code execution, publishing runs back as tracked versions. |
| An app that imports Granola client meetings, paired with an agent that drafts editable proposals from a meeting plus stored materials. | Personal connectors (Granola), cron-triggered agent runs, connector calls made directly from the app without an agent. |
They pair an app with a managed agent because agents can't own files or storage directly — they
work through an app they have data access to. The repo's
directory holds plain-app
examples (kanban, data chat, CRM); reach for those through
.
Ask, don't assume. When the request substantially overlaps an example, put the choice in the
step 1 scoping batch, naming the example in the user's own terms:
"Railcode provides an example that already generates client proposals from meeting notes.
Should I use that as a starting point, or build from scratch?"
Ask once, alongside the other scoping questions. Never copy an example unprompted, and don't
raise the question when nothing matches.
Copying an example
Copy
only the one directory, as plain files — never
the repo into the user's
project, add it as a submodule, or leave a
behind:
bash
mkdir -p my-proposals
curl -fsSL https://github.com/Railcode-HQ/railcode-examples/archive/refs/heads/main.tar.gz \
| tar -xz --strip-components=3 -C my-proposals railcode-examples-main/agents/proposals
drops
railcode-examples-main/agents/<example>/
, so the example's files
land directly in
. Swap the trailing path for the other row above. To study one
manifest without copying anything, fetch it raw from
https://raw.githubusercontent.com/Railcode-HQ/railcode-examples/main/agents/proposals/agents/proposal-writer/agent.yaml
.
The agent manifests live at
agents/<agent-name>/agent.yaml
; the app around them is a normal
Railcode app (
+
). Make the copy the user's own
before
authoring behavior:
- Rename each directory and the manifest's , so the copy doesn't collide
with an agent that already exists in the org.
- Point / / at the renamed companion app slug, and
keep right for the tools declared ( needs ).
- Cut every tool the new agent doesn't need — a copied manifest carries the example's authority,
not the narrowest set for this job — and re-size for the new workload.
- Rewrite the prompt and for the new contract; the example's prompt
encodes its own step-by-step procedure and input shape.
- In the app: set in , rename 's , run ,
update the list in and every / call,
and replace the example's if it ships one.
Then test the draft (
railcode agent test --file …
) before creating anything, exactly as in the
build workflow below. If the download fails, say so and build from scratch — don't reconstruct
an example from memory.
Build Workflow
1. Scope the agent
Ask the scoping questions first, all in one batch — this is the moment the user is
still present; questions dribbled out mid-build risk landing after they've stepped away.
Phrase them for a non-technical user who knows nothing of Railcode internals: ask
about intent and let the answers pick the primitives without naming them. "Should the
whole team be able to run this, or just you?" — not "org or personal visibility?".
"Should it also run by itself every morning?" — not "do you want a cron schedule?". The
bullets below are what you need to learn from the answers, not the words to use.
If the request needs something agents can't do (scraping the open web, reacting to data
changes, running continuously — see
Hard Limits), say so up front and
propose the nearest supported shape. Clarify only choices that materially change the
definition:
External source discovery is mandatory. Whenever the user asks for an agent that reads,
writes, syncs, searches, or acts on data from a named product or system ("X"), do not jump
straight to a manifest or conclude that X is unsupported. Before choosing tools, inspect every
Railcode integration plane available to the signed-in user:
bash
railcode db list # database/data-source connections
railcode query list # admin-published saved queries over those sources
railcode connector list # org service connectors
railcode personal-connectors list # per-user bundled and custom toolkits + connection status
Inspect any plausible match before authoring (
railcode connector docs <name>
and/or
railcode personal-connectors tools <toolkit>
), and copy exact connector names, endpoints,
tool slugs, and schemas rather than guessing. Prefer saved queries over
. A bundled
personal toolkit requires
and runs as the agent owner; an org agent
cannot declare personal connectors. If you cannot authenticate or reach the instance, ask what
is configured and give the user these discovery commands rather than treating the failed check
as evidence that no integration exists.
If no suitable source exists, explain the gap and offer concrete paths: have an admin connect a
database and publish a saved query; enable or create an org service connector for a shared
credential/API; or connect a bundled personal toolkit and make the agent personal. Also mention
a remote custom MCP personal connector when X provides one, but state the boundary clearly:
connectors are callable by Railcode
apps only, not managed-agent manifests.
For an agent workflow, offer a companion app that calls the custom MCP as its viewer, an
admin-configured service connector, or importing the needed data into a connected database/app
store the agent can read. If X exposes none of those supported surfaces, say Railcode cannot
connect to it directly and ask which alternative source the user wants to use.
- the job it owns and the output expected;
- the input it accepts — free-form JSON or text; the prompt is the input contract;
- the model and tools it needs;
- whether it runs on demand, from an app, on a schedule, or by Slack mention;
- whether it needs a companion app (see Companion Apps);
- whether to start from an example — when one in Start From An Example
covers much of the request, ask: "Railcode provides an example that already generates client
proposals from meeting notes. Should I use that as a starting point, or build from scratch?";
- what real systems, data, spend, or side effects a test may touch;
- its visibility — (the default: shared, invokable by anyone with an invoke
grant, managed by its creator or any admin) or (owned and invoked by its
creator alone, admins included — no grant makes it shared, and it cannot later become
). Pick only when the agent needs
tools.personal_connectors
(its
owner's own Gmail/Slack/etc.) or should otherwise be usable by exactly one person.
Use the narrowest useful tool set and explicit instructions — but size
the other
way: match the token budget to the task and
leave headroom. Document-editing and
file-analysis runs use far more tokens than the defaults; a run that hits a cap dies as
, while an unused ceiling costs nothing (see
manifest tools reference, "Sizing
").
Do not invent tool identifiers,
provider names, or manifest fields; managed-agent manifest fields are server-defined (author
the file in JSON or YAML — see step 2). See
manifest tools reference for the current
vocabulary
and
— a snapshot of the server schema, not a contract; a save-time error always wins
over this file.
2. Authenticate and inspect
Run
if the CLI has no usable saved token. Agent commands use the selected
organization context and work from any directory; agents can have
or
visibility and do not use
.
Manifest file format. (on
/
/
) reads the manifest as
JSON
or YAML; the CLI picks the parser by extension (
/
→ YAML, anything else →
JSON), and both parse to the same object the API stores. There is no local manifest-schema
validator — treat server validation and ratification warnings as authoritative.
For an existing agent, pull its exact stored manifest before editing:
bash
railcode agent show <agent>
railcode agent pull <agent> --output agent.json
and
emit JSON only — there is no YAML output flag. If the user
asks to see or store an agent's definition as YAML, convert that pulled JSON to YAML yourself
and write
; it round-trips back through
unchanged.
For a new agent, author the manifest in the current server-supported shape — or, if the user
chose an example in step 1, copy that example's
and adapt it (see
Start From An Example). Write the
prompt as described in
Writing The System Prompt.
Ask the user
whether to save the definition in the current working directory. If yes, write it there as
YAML (
) and feed that file to
/
; if no, keep it in a scratch
location outside their project.
3. Test the draft
Test an unsaved manifest before creating or replacing an agent:
bash
railcode agent test --file agent.json --input '{"key":"value"}' --trace
Use
for larger or sensitive test payloads. Testing invokes real configured
models and tools, so it may incur spend, read real data, or cause tool side effects. Get any
needed authorization before running a side-effecting test.
An agent that reads a companion app through
/
proves nothing against an
empty app. Seed it first with
railcode app kv set <collection> <key> '<json>'
and
railcode app files upload <path>
(CLI 0.1.28+, app owner or org admin), using the shape the
app itself writes, then clean up anything throwaway.
Do not rely only on the process exit code: a request that reached the runtime can exit 0 even
when the run's printed status is failed. Check
or inspect
.
A draft test does not persist writes.
tools run against a per-run in-memory
overlay, while
tools are omitted because no saved agent exists yet. An untouched
store after
is expected; use
+
to verify persistence. See
manifest tools reference.
4. Publish or update
bash
railcode agent create --file agent.yaml # or agent.json — format is picked by extension
railcode agent create --file agent.yaml --visibility personal
railcode agent update <agent> --file agent.yaml
replaces the stored manifest. Preserve fields intentionally by starting from
, and read all ratification warnings before considering the change done.
While you're in the pulled manifest, audit the
prompt for debris left by earlier test
rounds — see
Writing The System Prompt.
--visibility <org|personal>
on
/
/
sets or changes who the agent
belongs to. Omit it on
/
for the default
; omit it on
to leave
the existing visibility alone (never pass it just to be explicit — an omitted flag and an
explicit
are different requests server-side). Creating/transitioning to
needs the
capability;
needs
— holding one does not
imply the other.
is rejected outright;
is allowed but
does not retroactively change past shared runs/writes.
5. Verify the saved agent
bash
railcode agent run <agent> --input '{"key":"value"}' --trace
railcode agent show <agent> --manifest
Confirm the saved manifest, run status, output, and relevant trace steps. For organization
observability logs, use
; its
workflow is an
admin capability rather than part of agent authoring.
When the agent writes back through
, a clean run status is not proof the
data landed. Check the companion app's stores directly (CLI 0.1.28+, app owner or an org
admin):
bash
railcode app kv collections --app <app> # collections + record counts
railcode app kv list <collection> --app <app> # what app_kv_set actually wrote
railcode app files list --app <app> # what publish_artifact_to_app produced
railcode app files download <name> --app <app> # open the generated .docx/.pdf yourself
Add
--scope user --user <member-uuid>
or
--scope role --role <role-uuid>
to inspect a
non-shared namespace;
lists across every scope with owner attribution. This is
also the fastest way to catch a
personal agent writing into its owner's private scope when
the team expected shared records.
6. Schedule only when requested
Each managed agent currently has at most one cron schedule. Inspect it first, then use
to upsert or a stricter create/update alias when that distinction matters.
bash
railcode agent schedule show <agent>
railcode agent schedule set <agent> --cron "0 9 * * *" --timezone UTC
Use an IANA timezone and a five-field cron expression. Verify the stored schedule after every
mutation.
executes synchronously against real services.
A scheduled run passes
null input — there is no per-schedule payload. Write the
prompt so a run with no input knows exactly what to do. See
example agents (
).
Writing The System Prompt
The
prompt is the agent's whole contract: what job it owns, how to read its input,
and what to return. Two habits keep it working as it evolves.
Prefer positive instruction. Say what the agent should do rather than what it shouldn't.
"Quote figures only from the uploaded materials, and write a bracketed placeholder where one
is missing" gives the model something to aim at; "don't invent figures" forbids one path and
leaves the rest to guesswork. This is an encouragement, not a rule — a real boundary ("never
email anyone outside the attendee list") is worth stating outright, and hard limits should
stay hard. But when a "don't" is standing in for a "do", write the "do".
Audit the whole prompt on every update. Each run reads the prompt cold. The agent has no
memory of previous versions, earlier runs, or the bug being chased last week — so prompts
accumulate debris that reads fine to us and misleads the agent:
- Corrections phrased as history — "We no longer do X, do Y instead." This agent never
did X; the sentence introduces X and asks it to carry both. State only Y.
- Debug leftovers — a temporary "for now, only process the first three rows", or a
workaround for a bug that has since been fixed.
- Orphaned steps — instructions naming a tool, app, connector, or field the manifest no
longer declares.
- The same rule three times in slightly different words, each added during a different
test round. Restatements compete; keep the clearest one.
So before
, read the stored
end to end from
— not from memory of what you last wrote — and rewrite it as the
procedure someone encountering it cold would follow. A system prompt should read as a
specification, never as a changelog.
Slack (On By Default)
Once an org admin has connected the org's Slack workspace, every active agent is
reachable from Slack with no per-agent setup. Members run one by mentioning the bot in a
channel it has been invited to:
@Railcode $<agent-name> summarize this thread
The agent name takes a leading
and must be the
first token after the mention (a
bare name gets a usage hint instead of silently running something). What this means for
agent design:
- Authority is unchanged. The Slack caller is resolved by verified email to a live org
member and must hold the normal invoke grant — no match, no run. A agent is
therefore reachable on Slack only by its owner.
- Input arrives as . Agent input is free-form, so any agent can be
mentioned; its prompt must explain how to interpret that input.
- The platform posts the final reply into the mentioning thread, on success and on
failure. Whatever the agent returns IS the Slack reply (a Slack-triggered run is told so
in its system prompt and to write Slack mrkdwn); it does not need the connector
to answer — that connector, when granted, is for interim progress updates only.
So any agent a team will use conversationally should handle free-text input and produce a
final answer that reads well as a Slack message.
Companion Apps
An agent often needs a
companion app — a small static app (
)
deployed alongside it. Reach for this pattern whenever the agent relies on files or
records someone must manage, or people need a place to trigger it and see its output:
- Storage the agent relies on — the app is the UI for uploading and managing the files
and records the agent reads: / in the app;
/ in the agent's manifest.
- A surface for results — the agent writes back via
(, ) and the app renders run outputs.
- An easy way to test and trigger — a button wired to
agents.invoke(name, input)
(app manifest: ) exercises the agent end-to-end far faster than
hand-crafting CLI runs, and doubles as the interactive production trigger.
Name the app after the agent (e.g. agent
, app
), declare the narrowest slugs on both sides, and build the app
with
. The
rows in
Start From An Example
are working versions of exactly this pairing — read or copy one instead of assembling it
from scratch.
Hard Limits
What a managed agent
cannot do, regardless of manifest (the full platform-wide list is
in
→ "Limitations"):
- Reach the open web. Sandbox egress is an allowlist (PyPI, npm, the presigned
download host with ); the tool reaches only ratified endpoints.
No scraping, no arbitrary APIs.
- Run long or continuously. Runs are bounded — at most 300 steps / 1200 s / the token
caps in . No daemons, no monitors; recurring work is a cron schedule.
- React to events. Triggers are app/API call, cron, and Slack mention only — no
data-change or inbound-webhook triggers.
- Invoke other agents. There is no agent→agent tool; compose pipelines through an app
or an external caller instead.
- Use custom MCP personal connectors. A user-added by-URL MCP connector
() works for its owner and for apps, but is not declarable in an agent
manifest — ratification checks the static registry. Bundled toolkits only.
- Keep sandbox state. The sandbox filesystem is per-run; anything worth keeping must
be published () or written to KV before the run ends.
Permissions and Boundaries
- , , and only ever return org agents plus the caller's own
personal agents — someone else's personal agent is invisible (a 404, never a 403, to
avoid confirming it exists), admins included.
- For an org agent: needs an invoke grant for that agent; ///
schedule mutations are allowed for the agent's own creator, or any org owner/admin — not
every member. Creating (or transitioning an existing agent to) additionally needs the
capability.
- For a personal agent: invoke and manage are both owner-only, with no admin
override — there is no break-glass, so even an org owner/admin can't reach someone else's.
Creating one needs the broadly-grantable capability, not owner/admin.
- archives the agent while keeping run history and requires outside a TTY.
- Use when building a static app that invokes an agent through
agents.invoke(name, input)
. A privileged app manifest declares .
- An app can also run its own agentic loop in the page with /
— no managed agent involved. See When To Use A Managed Agent
vs The In-Page LLM at the top of this skill for the split.
- Use for members, roles/grants, apps/access, connections, service
connectors, analytics, and organization logs.
Reference
Read
CLI reference for the exact agent commands, aliases,
schedule behavior, inputs, outputs, and failure semantics. Read
manifest tools reference for the
vocabulary,
what each grants, its permission gate, and
. Read
example agents for worked, runnable manifests covering a minimal
agent and a scheduled query-to-email workflow. For complete agent-plus-companion-app projects
(pitch decks, client proposals), read or copy from
— see
Start From An Example.