Skip to main content

Getting Started

The fastest path is the connect-agent skill. Install it in your coding agent (Claude Code, Cursor, or any agent with shell access), or copy the full prompt, and the agent performs the whole setup, from finding the function to running the first suite:
Connect my agent to LangWatch agent testing
Install via CLI
npx skills add langwatch/skills/connect-agent
Skill Usage
/connect-agent
Copy Full PromptRun skill without installing
Download SKILL.mdManual installation
The steps below are the same setup by hand, and what to check when the skill’s run needs a correction.

1. Install the SDK

The process needs LANGWATCH_API_KEY in its environment. It is the project API key from Settings > API Keys, the same one the CLI and the SDK use for tracing. Without a key the SDK logs one line and opens no connection, so a build that has no key runs unchanged.

2. Connect it where your service starts

Write a small function beside the code that starts your service. This is the adapter between the messages that come from a scenario test and your agent.
model is a run parameter, so a run can select it. messages and thread_id are turn fields that LangWatch sends. See What the agent receives and Run parameters below.A synchronous function works the same way. It runs in a worker thread, so the connection stays responsive.
Put this on the startup path: the file that starts your service, or a module that file imports at startup. The connection opens with the process, so the code has to run when the process runs. Then start your service the way you always start it. The start command does not change, and there is no new process to run. A web server holds the connection open by itself, and one process serves every agent it declares over a single connection.
No long-running service? When the agent is a library with no process of its own, put the connect function in a small script and keep the script alive.In Python, call langwatch.agent.serve() at the end of the script. It blocks until Ctrl-C.In TypeScript, keep the script alive yourself. An open WebSocket holds the event loop while it is connected, but the HTTP fallback and the reconnect wait use unreferenced timers, so a script with no server of its own can exit between polls.
Run the script with whatever runner the project uses, for example tsx agent.ts.

3. Confirm it is online

The agent appears on the Agents page with a green Online mark, its environment, and the number of connected instances. From the terminal:
Run a test suite against it by name and environment:
connected:<agent-id> is the same target. langwatch agent list --format json prints both forms.

What the agent receives

The platform sends the same fields on every call. They describe the turn, and they are never run parameters. In Python the SDK reads the signature once, at decoration, and passes exactly the names the function declares. A function that declares messages and thread_id receives those two. A function that declares **kwargs receives all of them. A first parameter annotated langwatch.AgentCall receives one object with every field and the parameter values:
In TypeScript the handler receives one object, { messages, newMessages, threadId, session, params, traceId }. The function returns a string, one message, a list of messages, or a reply with a session value: langwatch.AgentReply(output, session=...) in Python, { output, session } in TypeScript.

Run parameters

A run parameter is a value a run supplies, so one scenario covers several conditions: a model, a plan, a tenant, a fixture. The agent declares the parameters it accepts, and the platform offers them in the run dialog, in --param and in the API.
Every parameter with a default that is not a turn field is a run parameter:
An annotation the SDK does not map falls back to the type’s JSON schema and is offered as text. parameters={...} on the decorator replaces what the signature declares.
The platform normalizes the declaration into the same shape a scenario declares, up to 20 parameters per agent. A closed list becomes a set of choices in the run dialog, and the platform refuses a value outside it before the run starts. A type the platform cannot map is offered as text, and the SDK logs which parameter that happened to. A parameter the run does not supply takes its default. When a parameter has no default and the run does not supply it, the SDK refuses the call before it reaches the function, and the message includes the parameter name. Scenario-declared parameters and agent-declared parameters share one list. See Run parameters for how a run supplies values, for secret parameters, and for what happens when two targets accept different names.

Session, when your agent keeps its own conversation id

thread_id is the platform’s id for the conversation. Many agents create their own conversation on their side and cannot accept an id from outside. session covers that: it is an opaque JSON value the agent owns, empty on the first turn, and whatever the function returns comes back on the next turn of the same conversation.
The platform holds the value for the run, so the next turn reaches any instance with the value. The process stores no state, and a deployment with many pods needs no routing rule. To keep the state in your own process instead, use an in-memory map. Key it by thread_id and set sticky=True (sticky: true), which keeps every turn of one conversation on the instance that took the first one:
With sticky, a conversation whose instance goes away fails with agent_instance_lost instead of restarting somewhere else. A session value is limited to 64 kilobytes. An agent that reads the whole messages list on every turn needs neither of these.

Environments and personal agents

The agent registers under an environment. Each environment is its own row and its own target. support-agent in production and support-agent in development are two targets, so one comparison run puts your machine next to production. The SDK resolves the environment in this order:
  1. The environment argument on the decorator.
  2. LANGWATCH_AGENT_ENVIRONMENT.
  3. APP_ENV, then ENVIRONMENT, then NODE_ENV.
  4. development.
development makes the agent personal. With a personal API key it belongs to the key’s owner, and only that person can run it. With a project API key it belongs to the machine, and the team can see and run it. Every other environment name is shared by the whole project. For a development box the team runs against, give it a name of its own:
See Environments and personal agents for the full rules, the local loop, and what a teammate sees.

Production with many instances

One deployment with several pods is one row and several instances. The platform dispatches each call to one live instance and reports the instance that served each run. The platform delivers each call at most once. The SDK confirms a call when the function starts, and the platform never sends a confirmed call to a second instance, so a turn with side effects runs one time. During a rolling deploy the platform closes the connections after the request drain and the SDK reconnects at once, so a new pod picks the agent up.

Tracing

The platform passes the turn’s trace context in the call, and the SDK adopts it before it calls the function. The spans your agent produces land in the turn’s trace, and the judge reads them before its verdict: tool calls, database writes, retrievals. A criterion such as “the agent looked up the order before answering” then passes on evidence instead of on the reply’s wording. There is no middleware to add, and the SDK does this for a synchronous function and an asynchronous one alike. Report the traces to the same project that runs the scenarios. Traces sent to another project, or to another observability backend only, are invisible to the judge. See Linking your traces for what the judge reads and how long it waits.

Security

The connection is outbound only. The process opens one TLS connection to LangWatch and keeps it. It opens no listening port, needs no public URL and no tunnel, and needs no inbound firewall rule. A network that already allows outbound HTTPS to your LangWatch endpoint needs no change. No credentials are shared. The only credential the SDK reads is the LangWatch API key. Model provider keys, database connections and internal API tokens stay inside your process and are never sent to LangWatch. What LangWatch sends in a call: the conversation, the values of the parameters you declared, the session value your function returned on the previous turn, and the turn’s trace context. What it cannot do: it can only call the function you decorated, with the parameters you declared. An instance receives calls for the agents that instance registered, and for no other agent. What the key needs. Connecting needs scenarios:manage, and starting a run needs scenarios:create. An ingestion key cannot connect. A personal API key never exceeds the permissions its owner has, so a person who cannot run suites cannot connect an agent that runs them. A personal development agent can be run by its owner only. The SDK sends the API key in a request header, never in the URL, so proxy logs and browser history do not record it. On a self-hosted deployment behind a private certificate authority, point the runtime at the certificate bundle: SSL_CERT_FILE for Python, NODE_EXTRA_CA_CERTS for Node.

Behind a proxy that blocks WebSockets

The connection is a WebSocket by default. Some corporate proxies refuse the upgrade and answer it with an HTTP status such as 403 or 426. The SDK then switches to HTTP long polling on its own, prints one warning line with the status, and the agent connects the same way: one POST registers, one request waits for the next turn, one POST sends each answer. No WebSocket egress is needed for this path, only outbound HTTPS. To use HTTP from the start, pass transport="http" in Python or transport: "http" in TypeScript, or set LANGWATCH_AGENT_TRANSPORT=http in the process environment. A poll waits up to 25 seconds on the platform side; a proxy with a shorter read timeout closes it early, and the SDK polls again. Declare parameters with their types and their options, and do not pass a raw parameter string into a privileged operation. LangWatch only calls the function. What the function does is your code, and its safety is yours.

Troubleshooting

The decorator never stops the application from starting. Every problem is one warning line on stdout with the fix, and the process runs on.

Options reference

@langwatch.connect_agent(...)langwatch.agent.serve() blocks the current thread while the connection runs. langwatch.Param(description=..., options=[...], type=...) annotates one parameter. langwatch.AgentReply(output, session=...) returns a reply with a session value.
Environment variables, read by both SDKs:

Read the first run

Open the run in Agent Testing > Results. A connected setup shows the conversation transcript, a trace link on each turn, and judge reasoning that cites what the agent did.
The run detail with the conversation transcript and a View trace link on the turn
The run results with the verdict and the judge reasoning citing a tool call span from the trace
The trace link on a turn opens the full trace: the simulation’s own steps first, then your agent’s spans for that turn. This is the evidence the judge reads:
The trace waterfall with the simulation spans and the agent's own spans in one trace
The run settings also record which instance served each run, so a result from a deployment with several pods shows which instance served it.

Common failures

Next steps

Environments and personal agents

Your machine, staging and production as separate targets

Run parameters

Declare values a run supplies, and offer them in the run dialog

Other ways to connect

HTTP agents, the local tunnel, and code agents

Linking your traces

What the judge reads from your agent’s own spans
Last modified on August 31, 2026