> ## Documentation Index
> Fetch the complete documentation index at: https://runelite.zip/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent Server

> Localhost-only HTTP API for runtime inspection, shared action dispatch, and allowlisted plugin operations.

Agent Server operates as a disabled-by-default n3 plugin. It exposes a localhost-only HTTP API for agent and client development on a Java 11 `HttpServer` bound to `127.0.0.1`. The default `RANDOM_TCP` mode chooses and persists separate ports in the 20000-60000 range; `FIXED_TCP` uses the configured ports. The default `ON_DEMAND` bind mode opens listeners while Break Handler reports an active automation plugin, an observable workflow runs or suspends, or a user-opened connection window remains active. Requests extend the window. After the configured idle interval with no active owner, both listeners close and invalidate sessions, leases, and in-flight operations. `ALWAYS` provides explicit compatibility. Authentication defaults to `GENERATED`, which creates a session-only 256-bit bearer token before binding; `CONFIGURED` requires the secret RuneLite config value to be nonblank, while `DISABLED` acts as an explicit insecure opt-out.

<Info>
  **Verification boundary:** This page describes the committed source. It does not certify revision-sensitive RuneLite UI, packet, or in-game outcomes; treat those as live-client verification pending unless the page records direct evidence.
</Info>

## Security and mutation protocol

Except for `GET /api/v1/health`, all routes, direct MCP requests, and WebSocket handshakes require `Authorization: Bearer <token>` when you enable authentication. `/ready` requires authentication. You can reveal, copy, and rotate generated tokens from the panel. Rotation invalidates sessions, leases, and stream connections. WebSocket query tokens support clients that cannot set headers, but client tooling may record URLs, so prefer bearer headers.

Raw write clients must create a session with `POST /api/v1/sessions`, explicitly acquire the lease with `POST /api/v1/lease/acquire`, renew through `POST /api/v1/lease/renew`, release through `POST /api/v1/lease/release`, and supply `sessionId` on every operational write. The server accepts `sessionId` flexibly from HTTP headers (`X-N3-Session-Id`, `Session-Id`, `SessionID`, `X-Session-Id`) or request body properties (`sessionId`, `SessionID`, `session_id`). An `Idempotency-Key` can be supplied via header (`Idempotency-Key`, `X-Idempotency-Key`) or body (`idempotencyKey`, `IdempotencyKey`, `idempotency_key`), and defaults to a random UUID if omitted. The lease defaults to 30 seconds and requires renewal every 10 seconds. Session creation skips acquiring it. Use `GET /api/v1/lease/status` and `GET /api/v1/operations/{id}` for sanitized status. The stdio bridge performs this protocol automatically and accepts `N3_AGENT_SERVER_TOKEN`.

## Request execution flow

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant Client as External Agent / Stdio Bridge
    participant HTTP as Agent Server HttpServer (127.0.0.1)
    participant Auth as Session & Lease Manager
    participant Service as AgentActionService
    participant ClientThread as RuneLite Client Thread
    participant SDK as Api.actions & SdkQuery

    Client->>HTTP: POST /api/v1/actions/npc (Bearer Token, X-N3-Session-Id, Idempotency-Key)
    HTTP->>Auth: Validate Token, Active Session & Lease Lock
    alt Auth or Lease Failure
        Auth-->>Client: 401 Unauthorized / 403 Lease Busy
    else Lease Validated
        Auth->>Service: Dispatch Request with Idempotency Key
        Service->>ClientThread: Queue Task via ClientThread.invoke
        ClientThread->>SDK: Query NPC & Execute Action (e.g. NPCActions.interact)
        SDK-->>ClientThread: Return InteractionResult (SUCCESS / PACED / ACTION_NOT_FOUND)
        ClientThread-->>Service: Capture Game State & Execution Snapshot
        Service-->>HTTP: Wrap in JSON Response Envelope { ok: true, data: ... }
        HTTP-->>Client: 200 OK JSON Payload
    end
```

## Runtime model

* Config group: `n3agentserver`.
* Default endpoint: persisted-random loopback TCP, shown in the Agent Server panel. Fixed mode defaults to `127.0.0.1:17631` (HTTP) and `127.0.0.1:17632` (dashboard stream).
* Direct MCP endpoint: `POST /mcp` using Streamable HTTP without SSE. `GET /mcp` returns 405.
* The sidebar panel shows bind address, port, running state, request count, last error, and plugin-operation availability.
* Read endpoints snapshot live client state on the RuneLite client thread.
* `GET /api/v1/debug/context` and `n3_get_debug_context` return one bounded,
  atomic player/modal/inventory/scene/navigation/trace/error snapshot. Use
  focused reads when one domain is sufficient.
* Gameplay write endpoints route through `Api.actions.*`. Idempotent operation records retain their `InteractionResult` and skip treating dispatch alone as confirmed success. Plugins needing account settings must declare and enforce their own requirements.
* Clients must avoid tight retry loops. Treat `PACED` and failure responses as pacing signals and retry with randomized or backoff behavior.
* Plugin operations cover all loaded RuneLite plugins. `PacketUtilsPlugin` lists as readable but ignores disable commands because it owns shared suite runtime state.
* `/mcp` allows missing `Origin` for non-browser clients and restricts browser origins to localhost.

## JSON envelopes

Workflow telemetry sits available from authenticated `GET /api/v1/workflow/status` and direct MCP tool `n3_get_workflow_status`. The response contains immutable snapshots and bounded transition histories for active and retained-terminal registered workflows.

Read success:

```json theme={null}
{ "ok": true, "status": "ok", "data": {} }
```

Write success or failure:

```json theme={null}
{
  "ok": true,
  "status": "SUCCESS",
  "message": "Queued dialog continue",
  "result": { "status": "SUCCESS" }
}
```

Errors:

```json theme={null}
{
  "ok": false,
  "status": "PACED",
  "message": "Action pacing is active"
}
```

## Endpoints

See the [OpenAPI JSON](agent-server-openapi.json) contract. The generated contract checks against the operation catalog and requires every documented operation to exist in the router. Import it into Swagger Editor, ReDoc, or an OpenAPI client generator; use the effective URL shown in the Agent Server panel. The checked-in document uses fixed-mode port `17631` as an illustrative default.

For example, after assigning the panel URL to `$agentUrl` and copying the generated token:

```powershell theme={null}
$agentUrl = "http://127.0.0.1:<effective-port>"
$token = "<generated-token>"
curl.exe "$agentUrl/api/v1/state" -H "Authorization: Bearer $token"
curl.exe -X POST "$agentUrl/api/v1/sessions" -H "Authorization: Bearer $token" -H "Content-Type: application/json" -d '{"clientName":"raw-example"}'
# Response returns {"ok": true, "data": {"sessionId": "<session-id>", ...}}
curl.exe -X POST "$agentUrl/api/v1/lease/acquire" `
  -H "Authorization: Bearer $token" `
  -H "Content-Type: application/json" `
  -H "X-N3-Session-Id: <session-id>"
curl.exe -X POST "$agentUrl/api/v1/walk" `
  -H "Authorization: Bearer $token" `
  -H "Content-Type: application/json" `
  -H "X-N3-Session-Id: <session-id>" `
  -H "Idempotency-Key: <uuid>" `
  -d '{"x":3222,"y":3222,"plane":0}'
```

### Read endpoints

* `GET /api/v1/health` provides unauthenticated liveness; `GET /api/v1/ready` provides authenticated readiness.
* `GET /api/v1/state`
* `GET /api/v1/diagnostics`
* `GET /api/v1/stream/status`
* `GET /api/v1/navigation/status`
* `GET /api/v1/workflow/status` for active and retained-terminal workflow telemetry.
* `GET /api/v1/login/modes`
* `GET /api/v1/login/status`
* `GET /api/v1/widgets/list`
* `GET /api/v1/widgets/search?text=...&action=...&limit=...`
* `GET /api/v1/widgets/describe?widgetId=...&index=...&action=...` returns sanitized listener presence, parent/runtime-index metadata, the selected named-action dispatch mode, and (when `action` exists) the resolved visible action owner without exposing listener arguments or live widget objects.
* `GET /api/v1/inventory`
* `GET /api/v1/players`
* `GET /api/v1/npcs`
* `GET /api/v1/objects` (player-nearest objects first, then the requested limit)
* `GET /api/v1/ground-items`
* `GET /api/v1/skills`
* `GET /api/v1/developer/metrics` for a structured developer snapshot including world, player position, skills, inventory, bank state, session/account context, and optional screenshot capture.
* `GET /api/v1/bank/status` for bank-open status.
* `GET /api/v1/bank` for the bank item snapshot.
* `GET /api/v1/plugins`
* `GET /api/v1/plugins/status`
* `GET /api/v1/plugins/config?className=...`
* `GET /api/v1/plugins/configs`
* `GET /api/v1/plugins/logs?className=...&limit=...`
* `GET /api/v1/menu-entries`
* `GET /api/v1/last-interactions`
* `GET /api/v1/varbit?id=...`
* `GET /api/v1/varplayer?id=...`
* `GET /api/v1/varbit-changes`
* `GET /api/v1/suite/capabilities` for the current coverage catalog across REST, direct MCP, SDK docs, typed read/write domains, and read-only SDK helpers.
* `GET /api/v1/sdk/read?method=...` for simple allowlisted read-only SDK helper calls. Use `POST /api/v1/sdk/read` when the helper needs structured arguments.
* `GET /api/v1/world`
* `GET /api/v1/world-map`
* `GET /api/v1/camera`
* `GET /api/v1/line-of-sight?fromX=...&fromY=...&fromPlane=...&toX=...&toY=...&toPlane=...`
* `GET /api/v1/item-metadata?itemId=...`
* `GET /api/v1/prices?itemId=...`
* `GET /api/v1/social`
* `GET /api/v1/progress?quest=...`
* `GET /api/v1/recent-events`
* `GET /api/v1/questhelper`
* `GET /api/v1/production`
* `GET /api/v1/minigames`
* `GET /api/v1/loadout/state`

### Write endpoints

Tab-dependent writes never prepend an implicit `/api/v1/tab/open` call. Inventory actions and equip require Inventory visible; unequip requires Equipment; prayer state changes require Prayer; spell writes require Magic; attack-style and auto-retaliate changes require Combat. If the prerequisite is closed, the endpoint returns `WIDGET_HIDDEN` targeting `tab/<TAB_NAME>` and dispatches exactly once. Call `/api/v1/tab/open` or `n3_tab_open`, observe the selected tab, and then submit the dependent request.

* `POST /api/v1/widgets/click` with `widgetId`, optional dynamic-child `index` from widget list/search results, optional `action`/`actions`, or raw `op`. Supply `index` when multiple live widgets share a packed ID. Prefer named actions; raw `op` provides a live-debug escape hatch for actionless widgets, not the default automation style.
* `POST /api/v1/inventory/interact` with `name`, `id`, or `index`, plus optional `action`/`actions`.
* `POST /api/v1/drop` with `name`, `id`, or `index`.
* `POST /api/v1/npcs/interact` with `name`, `id`, or `index`, plus optional `action`/`actions`.
* `POST /api/v1/players/interact` with exact `name`, plus optional `action`/`actions`.
* `POST /api/v1/objects/interact` with `name` or `id`, plus optional `action`/`actions`.
* `POST /api/v1/ground-items/pickup` with `name` or `id`, plus optional `action`/`actions`.
* `POST /api/v1/walk` with `x`, `y`, optional `plane`, optional `reachedDistance`, and optional `cancelOnNewTarget` (default `true`). Send `{"cancel":true}` to cancel the active walker path. The endpoint starts or reuses a walker path and stays non-blocking; poll `GET /api/v1/state` for progress.
* `POST /api/v1/navigation/preview` with `x`, `y`, and optional `plane` to inspect a path without starting movement.
* `POST /api/v1/login/profile/apply`, `POST /api/v1/login/start`, and `POST /api/v1/login/clear` manage memory-only dashboard login profiles. Status responses omit secrets, and login attempts cap out until the profile clears or reapplies.
* `POST /api/v1/bank/open`.
* `POST /api/v1/bank/close`.
* `POST /api/v1/bank/transact` with an `operations` array and optional `close` flag.
* `POST /api/v1/deposit`.
* `POST /api/v1/withdraw` with `name` or `id`, `amount`, and optional `noted`.
* `POST /api/v1/dialogue/continue`.
* `POST /api/v1/dialogue/select` with `index` or `text`.
* `POST /api/v1/dialogue/amount` with positive integer `amount`.
* `POST /api/v1/dialogue/text` with non-blank `text` for a visible chatbox text input.
* `POST /api/v1/developer/heartbeat` with optional `webhookUrl`, `secret`, and `includeScreenshot` to publish a developer metrics heartbeat payload.
* `POST /api/v1/plugins/enable` with `className`.
* `POST /api/v1/plugins/disable` with `className`.
* `POST /api/v1/plugins/config/set` with `className`, `key`, and `value`.
* `POST /api/v1/plugins/config/unset` with `className` and `key`.
* `POST /api/v1/use-item/item` with `sourceId`/`targetId` or `sourceName`/`targetName`.
* `POST /api/v1/use-item/npc` with `sourceId` and `npcName`.
* `POST /api/v1/use-item/object` with `sourceId` or `sourceName`, plus `objectName`.
* `POST /api/v1/use-item/ground-item` with `sourceId` and `groundItemId`.
* `POST /api/v1/sdk/read` with `method` and any helper-specific arguments. Accepts only explicit read-only SDK helper ids; rejects mutating helpers and gameplay actions.
* `POST /api/v1/ge/open`, `/api/v1/ge/close`, `/api/v1/ge/collect`, and `/api/v1/ge/cancel`.
* `POST /api/v1/shop/buy` with `name` or `id`, plus optional `quantity` of `1`, `5`, `10`, or `50`.
* `POST /api/v1/trade/accept`, `/api/v1/trade/decline`, and `/api/v1/trade/offer`.
* `POST /api/v1/equipment/equip` and `/api/v1/equipment/unequip`.
* `POST /api/v1/prayer/set`, `/api/v1/prayer/set-only`, `/api/v1/prayer/disable-all`, `/api/v1/prayer/quick-toggle`, `/api/v1/prayer/quick-open`, and `/api/v1/prayer/quick-set`.
* `POST /api/v1/magic/cast`, `/api/v1/magic/select`, and `/api/v1/magic/deselect`.
* `POST /api/v1/combat/toggle-spec`, `/api/v1/combat/set-attack-style`, and `/api/v1/combat/auto-retaliate`.
* `POST /api/v1/tab/open`.
* `POST /api/v1/production/choose`, `/api/v1/production/quantity`, and `/api/v1/production/amount`.
* `POST /api/v1/transport/travel` with a supported transport `type` and destination enum name.
* `POST /api/v1/deposit-box/deposit-all`, `/api/v1/deposit-box/deposit-equipment`, `/api/v1/deposit-box/deposit`, and `/api/v1/deposit-box/close`.
* `POST /api/v1/bank-inventory/deposit`.
* `POST /api/v1/bank-worn-equipment/deposit-all`.
* `POST /api/v1/drop-pattern`.

For dynamic widgets, `POST /api/v1/widgets/click` and `n3_click_widget` accept visible `text` plus an optional named `action` instead of a packed ID. Exact addressing still accepts an optional `index` alongside `widgetId`. The exact Grand Exchange Close tuple (`widgetId=30474242`, `index=11`, `action=Close`) uses the shared revision-cached native menu-action route. See [`menu-action-dispatch.md`](menu-action-dispatch.md) for resolution and cache behavior.

NPC and object reads accept optional `name`, `radius`, `interactableOnly`, and
`limit` filters. Direct MCP and the stdio bridge default these reads to 15
tiles, interactable targets, and 10 results. The stdio-only
`n3_batch_inspect` tool combines up to five common read surfaces in one call.

Tab selection, bank opening, deposit/withdraw inventory changes, dialogue
continue/select, prayer set, and quick-prayer toggle attach bounded observation
data and can promote `DISPATCHED` to `CONFIRMED` only after the declared
postcondition is observed. A fresh `observation.trace` records native transport
evidence; it never substitutes for an unobserved domain postcondition.

## Direct MCP endpoint

`/mcp` implements MCP Streamable HTTP in non-SSE mode for local agents connecting directly to the RuneLite plugin:

* `initialize`, `ping`, `tools/list`, and `tools/call` return JSON-RPC responses with `Content-Type: application/json`.
* `notifications/initialized` and accepted JSON-RPC notifications return HTTP 202 with no body.
* `GET /mcp` returns HTTP 405 because Agent Server omits an SSE stream.
* Protocol failures return JSON-RPC errors. Gameplay and business-logic failures return MCP tool results with `isError: true` and structured Agent Server or `InteractionResult` details.

## Plugin operations

Plugin config endpoints restrict to the target plugin config group. `set` and `unset` accept only keys belonging to the plugin descriptor or already stored under that plugin group, preventing callers from writing arbitrary cross-plugin config groups.

Plugin logs use a live-buffer only. Agent Server captures recent SLF4J/Logback events while running and filters by the target plugin class/package; it ignores historical RuneLite log files on disk.

## MCP bridge

`tools/agent-mcp` provides a local MCP stdio bridge for agents supporting tool calls. The MCP process operates outside the RuneLite plugin and respects Agent Server routing. It calls the same localhost HTTP endpoints above and returns the Agent Server JSON envelope as both MCP text content and structured content. It relies on REST APIs for compatibility and separates itself from the direct `/mcp` endpoint.

Setup:

```powershell theme={null}
cd tools\agent-mcp
npm install
npm run build
```

Example MCP client registration:

```json theme={null}
{
  "mcpServers": {
    "n3-agent": {
      "command": "node",
      "args": ["<repo-root>/tools/agent-mcp/dist/index.js"],
      "env": {
        "N3_AGENT_SERVER_URL": "http://127.0.0.1:<effective-port>",
        "N3_AGENT_SERVER_TOKEN": "<configured-or-generated-token>"
      }
    }
  }
}
```

The bridge intentionally exposes only REST-backed runtime tools plus its local `n3_batch_inspect` composition tool. SDK documentation/search tools (`search`, `get_class`, `get_method`, `list_packages`, `list_classes`, `get_examples`, `n3_describe_api`, `n3_search_api_docs`, and `n3_read_api_doc`) live on direct `/mcp`, bypassing the bridge. Runtime read tools include debug context and the state/stream/navigation/login/developer/widget/scene/plugin/menu/varbit tools plus `n3_get_suite_capabilities`, `n3_read_sdk`, `n3_get_world`, `n3_get_world_map`, `n3_get_camera`, `n3_get_line_of_sight`, `n3_get_item_metadata`, `n3_get_prices`, `n3_get_social`, `n3_get_progress`, `n3_get_recent_events`, `n3_get_questhelper`, `n3_get_production`, `n3_get_minigames`, and `n3_get_loadout_state`. Command tools mirror the matching endpoint, including navigation preview, memory-only login profile apply/start/clear, developer heartbeat, `n3_walk`, `n3_click_widget`, `n3_interact_inventory`, `n3_interact_player`, dialogue/plugin/config commands, `n3_use_item_on_*`, GE, shop, trade, equipment, prayer, magic, combat, tab, production, transport, deposit-box, bank-inventory, bank-worn-equipment, and inventory drop-pattern commands.

The MCP bridge creates its session at startup, acquires before its first write, renews every 10 seconds while owning the lease, generates UUID idempotency keys, and releases on clean shutdown. It omits retrying gameplay commands.

## Navigation state

`GET /api/v1/state` includes a `navigation` object for polling active walks:

* `activeGoal`: requested target point, when one tracks.
* `reachedDistance`: accepted radius for a `walkNear`/Agent Server walk request.
* `pathSize`: current tracked path size.
* `state`: active `WalkerStatus` name or `IDLE`.
* `distanceToDestination`: player distance to the tracked target, or `-1` when unavailable.
* `playerPosition`: current local-player world point when available.
* `activeRequestId`, `walkerStatus`, and `walkerFailureReason`: the active Shortest Path request and structured Walker outcome. Idle snapshots report `walkerStatus` as `IDLE`; the serializer omits nullable active-path fields.
* `requestedTarget` and `normalizedTarget`: the caller's target and the route planner's executable target.
* `currentStepIndex`, `totalStepCount`, `currentStepType`, `currentStepName`, `currentStepStart`, `currentStepEnd`: the active route stage and its bounds.
* `execution`: queued state, current land destination, last progress position, last decision, stall count, movement attempts, and recovery attempts.
* `recentTelemetry`: up to 20 recent Walker decision events in chronological order. After Walker clears its active path, read the terminal `status`, `failureReason`, request ID, target, position, and decision from this list.

Terminal history does not change operation-ledger convergence. A navigation operation may remain `DISPATCHED` while `recentTelemetry` records the Walker domain outcome; callers must inspect navigation state and the retained terminal event instead of treating dispatch as arrival.
