Skip to content
DocsLocal CLI execute

Concepts

Local CLI execute

Official Kody doc

When Node ≥22 and @kodycodes/cli are available, prefer npx @kodycodes/cli execute --local for one-off modules, smoke tests, and composition. Local execute is orthogonal to Open API: the CLI runs your module in a local workerd and forwards each kody:runtime call through the CapabilityProxy. Do not use hosted MCP execute for agent work when a local or Open API path exists.

Auth: --token / KODY_API_TOKEN (kody_at_… with org:execute; add package:execute when the module imports kody:@…), or when no API token is set, the access token from kody login (MCP OAuth) as Bearer on CapabilityProxy and package-graph routes.

Setup

Agents already on MCP: call cliCredentialBootstrap with a required lifetime (single-task agents use short). It returns a one-shot kody_bc_… code and a cli_command that already includes lifetime flags (not a kody_at_…). Bootstrap tokens are person-owned (org_binding: user); pass ?org=<slug> on API calls to act in a team org (default: personal). Run the CLI command (no second OAuth, no tokenCreate):

{
	"operationId": "cliCredentialBootstrap",
	"params": { "lifetime": "short" }
}
npx @kodycodes/cli auth bootstrap --code 'kody_bc_…' --lifetime short

short is 1 hour idle / 24 hours max. long is 14 days idle / 3 months max. Or pass both --idle-ttl-seconds and --max-lifetime-seconds. Lifetime is required; missing it fails with the exact flag syntax.

Default scopes: bootstrap tokens include local-execute use scopes (org:execute, org:read, and package/integration/secret/email/job/app/memory use scopes), plus package:write (plain-repo / package pushes) and token:delete (self-revoke). They do not include search:read. When a task needs a different set, pass scopes when minting. The list replaces the defaults, so include every scope you need:

# Agents on MCP: cliCredentialBootstrap({ lifetime: "short", scopes: ["org:execute", "org:read", "package:execute", "package:read", "package:write"] })
npx @kodycodes/cli auth bootstrap --code 'kody_bc_…' --lifetime short

# After `kody login`, the CLI mints the code itself and stores the token:
npx @kodycodes/cli auth bootstrap --lifetime short \
  --scopes org:execute,org:read,package:execute,package:read,package:write

--scopes mints with --token when given, else the kody login session (any scope your org role grants), else KODY_API_TOKEN or the stored token (only scopes that token already holds). --code with --scopes is an error because a code's scopes are fixed when it is minted.

Interactive humans: kody login once per machine, then run --local without KODY_API_TOKEN.

npx @kodycodes/cli login   # once

CI / headless only: scoped KODY_API_TOKEN (kody_at_… with org:execute, plus package:execute for saved-package imports), usually from MCP api tokenCreate with a required lifetime. Put the value in the environment; never paste it into chat. Prefer the env var over --token. When set, KODY_API_TOKEN wins over kody login / bootstrap store. Minting details: Open API (guide:open_api).

{
	"operationId": "tokenCreate",
	"params": {
		"name": "kody-cli-local",
		"scopes": [
			"org:execute",
			"org:read",
			"package:execute",
			"package:read",
			"integration:read",
			"integration:use",
			"secret:use"
		],
		"lifetime": "short"
	}
}
export KODY_API_TOKEN='kody_at_…'

Usage

npx @kodycodes/cli execute --local --code 'import { kody } from "kody:runtime"; export default async function main() { return await kody.metaGetCurrentUser({}) }'

Use --file path.ts the same way when the module lives on disk. Keep --local; static kody:@owner/name imports still run locally (package-graph download + CapabilityProxy hops). Inline code on POST /v1/local-execute/package-graph may be up to just under the Open API JSON body limit (~5 MiB); larger modules (or multi-megabyte kody dev worker bundles) pass imports alone so origin synthesizes a tiny entry. If inline code still exceeds that ceiling, the API (and CLI) name the actual UTF-8 byte length and the maximum — not a bare Invalid parameters. Package-graph always returns the gateway-fetch shim, so ad-hoc scripts with no kody:@ imports still expand {{secret:…}} placeholders on origin (or fail closed naming the secret) instead of sending them raw. import { credential } from 'kody:runtime' for ad hoc and unpublished package source needs a CLI whose host kody:runtime exports credential / credentials per the CLI local credential runtime contract (CapabilityProxy opens a handle; gateway placement only — same as hosted). Older CLIs fail at import time; upgrade @kodycodes/cli.

A fetch that carries a placeholder runs on origin with the same 5-minute overall deadline as cloud fetches. The deadline starts when origin sends the request upstream, after it checks your quota and fills placeholders, and runs until the response body ends. The response is also aborted after 60s with no bytes. --local has no run cap, so a long streamed response, such as an AI completion, can finish.

Small buffered placeholder-bearing bodies use the JSON CapabilityProxy hop (kody.gatewayFetch): origin buffers the whole response before returning it, so your code receives nothing until the body ends, and a body over 4 MiB fails. Streamed bodies, multipart bodies, and bodies that would exceed 4 MiB use the streaming hop (POST /v1/capability-proxy/gateway-fetch via kody.gatewayFetchStream): there is no 4 MiB hop cap. That path still respects the Cloudflare Worker upload limit (~100 MiB on Free/Pro). A Content-Length over that limit returns JSON 413 payload_too_large. See the CLI streaming gateway-fetch contract.

From @kodycodes/cli 1.13.1, the CLI's CapabilityProxy client waits up to 7 minutes for origin's reply, so the gateway's 5-minute deadline is what ends a slow fetch, with its own error. Older CLIs stop waiting after Node's default 300 seconds (UND_ERR_HEADERS_TIMEOUT) and need a few seconds of margin. With NODE_USE_ENV_PROXY=1 or --use-env-proxy, these requests still go through HTTP_PROXY / HTTPS_PROXY. A fetch without placeholders, and a multipart upload that does not need fill, goes straight from local workerd and is not subject to this deadline.

Failure

  • Node is below 22, the CLI is missing, or the host cannot run workerd. Use Open API / MCP api, or fix the environment. Hosted MCP execute stays banned for that failure case.
  • Bootstrap or redeem rejects a missing lifetime. Pass --lifetime short|long (or both idle/max flags). Call cliCredentialBootstrap again with lifetime: "short" if the code expired.
  • insufficient_scope on a token means it lacks a scope. The error names the scope and prints both mint commands with the full list (the token's scopes plus the missing one); run one and retry. --token and KODY_API_TOKEN still win over the stored token, so replace or unset them before retrying. Open API errors carry the same list as details.mint_scopes. When your org role lacks the permission (denial: missing_permission), a new token cannot help; an org Owner has to grant it.
  • The module imports kody:@…. Keep --local.
  • Auth priority for --local: --token / KODY_API_TOKEN, then the stored bootstrap token, then kody login.

Working with an agent? This page is also plain markdown at /docs/local-execute.md, or load it over MCP with search({ entity: 'guide:local_execute' }).