Skip to main content

langctl

Scaffold, run, and deploy production LangChain agents — frontend and agent in one command.

langctl new my-agent      # backend + chat UI, wired together
cd my-agent
langctl dev               # one command, one URL, already talking to each other

langctl dev starts the LangGraph Agent Server and your Next.js app, waits for the agent's health endpoint before booting the UI, proxies the agent behind the frontend's own origin, and tears both down cleanly on Ctrl-C. Like next dev, but the backend is an agent.

Why the proxy

The browser only ever talks to /api/agent/... on the frontend's origin:

localhost:3000                      127.0.0.1:2024
┌────────────────────────┐          ┌──────────────────┐
│ Next.js                │          │ langgraph dev    │
│  /            chat UI  │          │  /threads /runs  │
│  /api/agent/* ─proxy───┼─────────▶│  /assistants /ok │
└────────────────────────┘          └──────────────────┘
        same origin ⇒ no CORS, ever

Three things fall out of this:

  • CORS never applies. There is no cross-origin request to preflight.
  • The API key stays on the server. The proxy runs in a route handler and attaches x-api-key there. Nothing secret reaches the browser.
  • Dev and production differ by one variable. AGENT_PROXY_TARGET unset means the local agent; langctl deploy sets it to the deployed Agent Server URL. The frontend source does not change.

Chat UI

langctl new scaffolds one of three UIs. All share the same proxy, streaming contract, and langctl dev behaviour — only web/app/components/ differs.

--ui What you get CSS
assistant-ui (default) assistant-ui runtime (threads, branching, composer) via npm; we ship a ~130-line converter you own Tailwind utilities only
minimal one hand-written Chat.tsx, zero UI dependencies Tailwind utilities only
ai-elements (experimental) shadcn-registry components copied into web/components/ Tailwind @theme tokens

No template contains hand-written CSS. globals.css is @import "tailwindcss"; and nothing else, except ai-elements, which adds a @theme token block because its components reference semantic classes. A test enforces this.

ai-elements is experimental and currently fails npm run build. Its generated components pull streamdown, which resolves two incompatible copies of shiki; npm overrides do not dedupe them. Our own files typecheck clean — the failure is entirely in the vendored components. It is excluded from the interactive wizard and reachable only via an explicit --ui ai-elements.

Commands

Command What it does
langctl new [name] Scaffold backend (+ frontend), agent.yaml, langgraph.json, git init, install deps.
langctl dev Run agent + frontend as one app. --backend-only, --frontend-only, --port, --backend-port, --docker, --tunnel, --no-open, --strict-port.
langctl sync Regenerate langgraph.json from agent.yaml, preserving hand-written keys. --check for CI.
langctl doctor Verify toolchain, ports, keys, and config before something fails mid-command.

Configuration

agent.yaml is the single source of truth; langgraph.json is generated from it. sync merges rather than overwrites, so hand-added keys (auth, checkpointer, dockerfile_lines, …) survive, and drift in owned keys is reported instead of silently clobbered.

Install

uv tool install -e .        # editable: source edits take effect immediately
langctl --version

Install editable. A non-editable uv tool install . is cached by version, so after changing the source uv tool install --force . reports Audited … and keeps running the old code — the version string has to change for the cache to miss. If you already hit that: uv tool install --force --reinstall -e .

When debugging which copy is running, call it by absolute path (~/.local/bin/langctl): an activated project venv shadows the global binary.

Development

uv venv && . .venv/bin/activate
uv pip install -e ".[dev]"
pytest                    # unit + process-level integration
ruff check src tests

The suite spawns real child processes rather than mocking Popen: the failures that matter here — orphaned grandchildren, ports left held, signals that never arrive — do not exist at the mock level. See tests/test_supervisor.py and tests/test_signals.py.

Two heavier checks need a scaffolded project with dependencies installed:

export LANGCTL_E2E_PROJECT=/path/to/scaffolded/project
tests/e2e/dev_runtime.sh     # health gate, proxy, thread creation, clean teardown
tests/e2e/sse_streaming.sh   # asserts SSE arrives incrementally, not buffered

sse_streaming.sh measures arrival times, not just the final body — a buffering proxy passes every status-code assertion and still ruins the product.

Status

Phase 1 (scaffold + unified dev runtime) works end to end. Deploy providers (langsmith_cloud, vercel) are next — see plan/.

License

Apache-2.0. Generated projects carry no license obligation to this tool.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

langctl-0.3.0.tar.gz (95.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

langctl-0.3.0-py3-none-any.whl (75.9 kB view details)

Uploaded Python 3

File details

Details for the file langctl-0.3.0.tar.gz.

File metadata

  • Download URL: langctl-0.3.0.tar.gz
  • Upload date:
  • Size: 95.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for langctl-0.3.0.tar.gz
Algorithm Hash digest
SHA256 fcf641c523c9d73a9936581ec1efcebb9f058abb3e252c184e400f5b94693a37
MD5 7094e6c83dcaf1bbcd3e3de1e3a5d52b
BLAKE2b-256 bff05ca3002ab483b8a64dbb270766b98eed374a337714e27b0ac3411635571b

See more details on using hashes here.

Provenance

The following attestation bundles were made for langctl-0.3.0.tar.gz:

Publisher: release.yml on Sami606713/agent_cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file langctl-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: langctl-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 75.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for langctl-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7e6b4fcb731e2ab80d95779cb9a3e79435644525ce106e84feed68693665c9b4
MD5 10639137735c2ee34ecb2acfd3ec5c77
BLAKE2b-256 d8fe59a311c073aaa09d93e51019132ab7e4d160b711225f8dcab3bd5020983f

See more details on using hashes here.

Provenance

The following attestation bundles were made for langctl-0.3.0-py3-none-any.whl:

Publisher: release.yml on Sami606713/agent_cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page