Skip to content

ngn configuration reference

This page describes the current source-checkout ngn harness, which can differ from an installed release or another coding assistant. See release availability and installation and the installed release notes before assuming every current field or behavior is available.

Quick example

Configuration is JSON. Keep general settings in config.json and named connections in providers.json. profiles and the optional providers file reference are the nested settings in the general config:

{
  "providers": "./providers.json",
  "model": "gpt-6-luna",
  "agent": "assistant",
  "theme": "terminal",
  "animations": true,
  "max_subagent_depth": 2,
  "profiles": {
    "audit": {
      "mode": "reviewer",
      "instructions": "Prioritize regressions and missing tests. Do not edit files."
    }
  }
}

Unknown fields, wrong JSON types, invalid values, and literal api_key fields are errors rather than silently ignored options.

The checkout includes an example pair at examples/harness/config.json and examples/harness/providers.json. To try it without provider calls, run from the repository root:

poetry run ngn --config examples/harness/config.json --demo

Precedence

The order is lowest to highest priority:

  1. Built-in defaults.
  2. The active named connection in $XDG_CONFIG_HOME/ngn/providers.json, when present.
  3. NGN_* environment defaults, including NGN_PROVIDER_ID for a named selection.
  4. Global/user JSON.
  5. Trusted project JSON.
  6. An explicitly selected JSON file.
  7. Explicit CLI flags.

Environment variables are not the highest-priority override. For example, NGN_MODEL=environment-model loses to "model": "file-model" in global JSON; --model cli-model overrides that top-level setting. A flag omitted from the command line leaves the loaded value intact.

Files are layered, not exclusive: --config does not suppress the global file or a project file allowed by --trust-project. Re-selecting the global file with --config puts it at explicit-file priority, after the trusted project file. An explicit file must exist; absent global and project files are fine.

Each loaded file must be valid JSON with valid field types. CLI flags are applied after configuration loading and validation, so a CLI override is not a way to repair a malformed or invalid lower-priority configuration. Likewise, invalid environment conversions can fail before a file replaces the value.

Profile model selection happens at runtime after loading the top-level configuration; see profiles before combining a profile-specific model with --model.

Shared named provider connections

ngn and ngn serve share $XDG_CONFIG_HOME/ngn/providers.json (default ~/.config/ngn/providers.json) for global connections. Use Settings → Global → Provider connections to edit them and choose the default. Settings → Workspace → Provider connections manages connections and a selection for this workspace; its JSON lives at $XDG_CONFIG_HOME/ngn/workspaces/<workspace-hash>/providers.json. The workspace list includes global connections, which can be selected without changing the global default. Use global default clears a workspace selection. The TUI /provider menu also offers both scopes. Without an active connection, ngn serve starts for setup, but chat waits until one is selected. You can edit either JSON while ngn is stopped. A revision prevents an older UI from overwriting another process's edits. A separate connection can be selected for a trusted agent profile using profiles.NAME.provider: CONNECTION_NAME.

Provider connections contain kind, authentication source, endpoint where needed, HTTP API and environment variable names. Chat models are chosen separately: the model field in global $XDG_CONFIG_HOME/ngn/config.json or workspace $XDG_CONFIG_HOME/ngn/workspaces/<workspace-hash>/config.json provides shared defaults for the CLI, TUI and web UI. Trusted JSON model selections, agent-profile models, NGN_MODEL and explicit --model overrides retain their precedence. Switching connections does not change the chat model. Voice duplex preferences are edited separately in GPT-Live → Voice settings: Global supplies defaults, and the workspace may override individual fields. Use "api_key_env": "OPENAI_API_KEY" or "api_key_env": "${OPENAI_API_KEY}"; ngn resolves the variable when it makes a request. It never writes the key value to this file. Set environment variables in the environment of the ngn process (including the ngn serve process), not in the browser. For Microsoft Entra ID, "auth": "entra" uses the optional azure-identity package's DefaultAzureCredential chain; its SDK credential owns token caching and refresh. OpenAI supports auth: chatgpt for ngn's device login and "auth": "codex" for the library's local Codex discovery (CODEX_HOME or ~/.codex). These existing OAuth/Codex files are not copied into the provider registry. "auth": "auto" prefers an existing ngn ChatGPT login or local Codex configuration before an OpenAI API-key environment variable. Auto auth is available only for OpenAI; Codex auth uses only local Codex discovery. OpenAI uses a fixed API host; use openai_compatible for custom endpoints. Web voice uses the normal provider registry and defaults; an explicit Voice connection can select a different Live-capable provider. The browser sends audio only to ngn, which owns provider authentication and connections. ChatGPT/Codex login uses the server's native Codex media/control path, while API-key and Entra connections use supported Live WebSockets. Subscription tokens are never exposed to the browser or used as public Live API keys.

Example:

{
  "version": 2,
  "revision": "0000000000000000000000000000000000000000000000000000000000000000",
  "active": "work",
  "providers": {
    "work": {
      "kind": "foundry",
      "base_url": "https://resource.openai.azure.com/openai/v1",
      "api": "responses",
      "auth": "entra",
      "request_timeout": 120.0
    }
  }
}

request_timeout is the total deadline, in seconds, for each ngn-issued model HTTP request on this named connection, including connection setup and streaming response reads. It accepts positive finite integers or decimals; omission keeps the existing 120-second default. Booleans, numeric strings, zero, negative values, and nonfinite values are rejected. There is no additional upper cap.

The same setting applies to API-key, ChatGPT/Codex, and Foundry/Entra model requests, including agents and native children selecting this connection. Editing an active connection replaces its HTTP client so the new deadline takes effect on subsequent requests. Global and workspace connection scope works as for its other fields. Model catalogs retain their separate cap of min(request_timeout, 30) seconds.

This field does not change shell limits, whole-run or child deadlines, Live session duration, Live backend waiting limits, or authentication SDK deadlines. Existing retries remain unchanged; retries and backoff can make a generation last longer than one request deadline. There is no top-level Harness JSON field or NGN_REQUEST_TIMEOUT environment alias. Use the named connection's JSON, web provider editor, or TUI /provider editor.

The UI generates a fresh revision on save and writes indented JSON. Workspace JSON uses the same schema: its active may reference a global connection, and an empty active inherits the global default. Other provider types (openai, openai_compatible, openrouter, anthropic, gemini, litellm, both Azure routes) have their own authentication/API/endpoint requirements; the UI shows the allowed fields for the selected type. A provider catalog is a best-effort list of model IDs, not a guarantee of access. Providers without a catalog or a compatible /models endpoint require manual model entry. Saving a named connection does not perform network authentication.

To place the registry elsewhere, set "providers": "./providers.json" in config.json. A reference in the user config selects the global provider file; a reference in a trusted project or explicit config selects the workspace file. Use "providers": {"global": "./shared.json", "workspace": "./team.json"} to set either scope explicitly. Paths are relative to the containing config file. Omitted references use the default providers.json paths. An explicit reference to a missing file starts with an empty registry; the UI creates that file on save. The references must name distinct .json files. The full resolved provider definitions are visible as config.providers during CLI startup.

Voice defaults are stored in $XDG_CONFIG_HOME/ngn/voice.db, and workspace overrides in the workspace session database. Provider connections contain no chat or Voice model and no API-key values.

Create a named connection in the web UI or TUI and set its environment variable; the provider editor does not offer an API-key-value field.

File locations and trust

Item Location or selection Behavior
Global/user JSON $XDG_CONFIG_HOME/ngn/config.json; defaults to ~/.config/ngn/config.json when XDG_CONFIG_HOME is unset or empty Automatically trusted and loaded if present.
Global provider registry $XDG_CONFIG_HOME/ngn/providers.json; defaults to ~/.config/ngn/providers.json Shared across workspaces by web, TUI, and headless ngn; no API key values.
Workspace provider registry $XDG_CONFIG_HOME/ngn/workspaces/<workspace-hash>/providers.json Workspace-only connections and selection; inherits global connections and default.
Workspace chat-model preference $XDG_CONFIG_HOME/ngn/workspaces/<workspace-hash>/config.json The workspace model field overrides the global model preference; no credentials.
Project JSON <workspace>/.ngn/config.json Ignored with a diagnostic unless --trust-project is supplied or that exact file is selected explicitly.
Explicit JSON --config /any/path/settings.json Any filename/location ending in .json is accepted. Selecting it explicitly trusts it, including endpoint and plugin settings.
Workspace --workspace PATH or -C PATH; defaults to the shell's current directory Must already be a directory. Determines project configuration, file-tool boundaries, and session scope.
State root data_dir; default $XDG_DATA_HOME/ngn or ~/.local/share/ngn when XDG_DATA_HOME is unset or empty Stores workspace-scoped session databases.
OpenAI login store $XDG_DATA_HOME/ngn/auth/openai.json or ~/.local/share/ngn/auth/openai.json Separate credential store, not a general config setting or a session database. Changing data_dir does not move it. Never include it in a config example or bug report.
Provider login store $XDG_DATA_HOME/ngn/auth/login.json or ~/.local/share/ngn/auth/login.json Login credentials beside the ChatGPT store; this does not select a named provider. ngn logout removes it. Never include it in a config example or bug report.

The workspace's .ngn/config.json is the project file; ngn does not search every ancestor directory for more configuration layers. The trust decision is made from CLI arguments before project configuration is read. There is no trust_project = true JSON escape hatch or NGN_TRUST_PROJECT shortcut.

ngn --workspace /path/to/project --trust-project
ngn --workspace /path/to/project --config /path/to/project/.ngn/config.json
ngn --workspace /path/to/project --config /another/location/review.json

The second command explicitly trusts the selected project file without needing --trust-project. The third trusts only the explicitly selected file in addition to global configuration; it does not implicitly trust a different workspace project file.

Review trusted configuration and its referenced Python code. It can redirect requests and select executable plugins. There is no safe-settings subset loaded from an untrusted project file: the entire file is skipped.

Relative paths

  • --workspace, --config, and file paths passed with --plugin resolve from the shell's current working directory, not from each other. -C selects a workspace; it does not change how another CLI path is resolved.
  • data_dir, provider references, and .py plugin paths written in JSON resolve from the directory containing that JSON file. In .ngn/config.json, "data_dir": "state" means .ngn/state, and plugins: ["../extension.py:setup"] refers to extension.py at the workspace root.
  • NGN_DATA_DIR is a path relative to the shell's working directory unless absolute. It is the full state root; ngn does not append another /ngn to it.
  • Supported path values expand ~ and resolve to absolute paths. Use absolute XDG environment paths. General $VARIABLE or ${VARIABLE} interpolation in JSON strings is not supported.
  • An installed-module plugin such as my_package.extension:setup is imported from the selected Python environment, not resolved as a filesystem path.

Complete top-level schema

Defaults below are built-ins, before environment, JSON, CLI, profile activation, or authentication-route selection. String choices are case-sensitive.

Model and named provider selection

Field JSON type Default Accepted values and meaning
provider_id string "" Optional named connection from providers.json; otherwise the registry's active connection is used.
providers string or object omitted Relative .json file reference for this scope, or {"global": "path.json", "workspace": "path.json"}. Omitted scopes use their default providers.json. The web UI saves the selected file.
model string "gpt-6-astra" Nonempty after trimming whitespace; a model ID supported by the endpoint/account, or an Azure deployment name. No model-catalog validation occurs at startup.

Connection details—kind, api, base_url, api_key_env, auth, and api_version—belong only to entries under providers.json → providers.NAME. They are rejected as top-level config.json fields. The resolved connection is available by name at CLI startup: config.provider is the selected name and config.providers maps names to provider definitions.

At the CLI's serve(config, ...) breakpoint, inspect config.provider and config.providers[config.provider] for its kind, auth, api, base_url, api_key_env, and api_version. The api value selects the provider's HTTP protocol, such as messages, responses, or chat_completions; allowed values depend on the provider kind and authentication mode. There is no connection wrapper or provider_registry field, and no flat config.auth or API fields. General settings such as config.model, config.workspace, and limits remain separate. The profile's key_env property resolves the environment-variable name; loading configuration does not read its secret value.

Provider string Aliases Default endpoint/behavior
openai_compatible openai https://api.openai.com/v1; API-key requests use the OpenAI-compatible protocol.
openrouter None https://openrouter.ai/api/v1; choose a model exposed by OpenRouter.
anthropic None https://api.anthropic.com/v1; native Messages protocol with api: auto.
gemini_native gemini, google https://generativelanguage.googleapis.com/v1beta; native Gemini protocol with api: auto.
azure_openai_compatible azure Explicit Azure resource endpoint and nonempty api_version required.
azure_openai_compatible_v1 None Explicit Azure endpoint required; use the endpoint form expected by that route, rather than assuming the versioned Azure configuration is interchangeable.
foundry None Azure AI Foundry OpenAI-compatible v1; explicit API prefix; API key or Microsoft Entra ID.
litellm None Explicit base_url required. This is a connection to your gateway, not an embedded proxy or a LiteLLM SDK dependency.

api: auto retains provider-specific routing: Chat Completions for OpenAI-compatible, OpenRouter, LiteLLM, and Azure; Messages for Anthropic; native Gemini for gemini_native. The three explicit API choices refer to /chat/completions, /responses, and /messages respectively under the configured API base, where that provider/endpoint supports the route. Native Gemini routing is not one of those OpenAI/Anthropic-style paths. OpenAI-compatible, OpenRouter, and LiteLLM support selecting the explicit contracts, subject to server/model support. Anthropic accepts only auto or messages; Azure accepts only auto or chat_completions; native Gemini requires auto. OpenAI-compatible messages requires an explicit compatible base_url rather than the default OpenAI endpoint.

auth: auto prefers a saved ChatGPT login for the default OpenAI provider endpoint only when api: auto; otherwise it uses api_key_env. "api-key" forces API-key selection. "chatgpt" requires an ngn login and uses the dedicated Codex Responses route, not a general-purpose OpenAI API credential. It requires provider: openai or "openai_compatible" without base_url and with api: auto. Explicitly combining auth: chatgpt with an api override is rejected, even for api: responses. When explicitly selecting an API-key protocol, set auth: api-key alongside api instead of relying on automatic login selection. Saved OAuth credentials are never sent to a custom endpoint, including LiteLLM. Do not use api to attempt to redirect a ChatGPT login to a gateway or to the ordinary paid API.

The key's value is read from the variable named by the active connection when a live request needs it. Put the variable name in providers.json, not config.json; no API-key value belongs in either file. ngn does not automatically load .env files or borrow another coding assistant's credentials.

Agent and tool limits

Field JSON type Default Accepted values and meaning
agent string "assistant" The built-in "assistant" or an explicitly configured profile name.
read_only boolean false Enforce read-only operation for the assistant and child agents.
shell_timeout integer or float 60.0 Seconds, strictly greater than 0 and at most 600. Booleans are not numbers here.
max_output integer 32768 1024 through 1048576, inclusive; bounds tool output in bytes.
max_file_bytes integer 262144 1024 through 4194304, inclusive; file-size limit in bytes for guarded file operations.
max_tool_rounds integer 30 1 through 1000, inclusive; maximum model/tool rounds in the agent loop. This is not the subagent count.
max_subagent_depth integer 2 0 through 8, inclusive. Root depth is 0, child depth 1, grandchild depth 2. 0 disables delegation; the default allows children and grandchildren, not great-grandchildren.
demo boolean false Offline scripted provider, no workspace mutations or shell execution, and no plugin imports. Local demo conversations are still saved.

Integer fields reject floats and booleans even if numerically equivalent. Subagent concurrency, per-run job counts, timeouts, and result-size budgets are additional safeguards, not extra JSON fields. See subagents.

The model receives the active shell timeout, output cap, and whole-file read limit in the native tool descriptions. These descriptions refresh when settings change and when a child harness starts. A shell call may omit timeout or use 0 for the configured default; a positive value cannot exceed shell_timeout. Reading fewer lines does not bypass max_file_bytes: guarded reads take a snapshot of the whole file. Invalid arguments and limit failures return tool errors for the model to correct; they do not automatically relax limits or retry the operation.

Terminal behavior

Field JSON type Default Accepted values and meaning
theme string "terminal" "terminal", "graphite", "ocean", or "ember".
theme_background string "auto" "auto", "terminal", or "theme". Automatic preset behavior, terminal-native background, or the theme's background. Separate from palette selection.
animations boolean true Enable motion; false disables the busy animation, animated scrolling, and cursor blinking. TEXTUAL_ANIMATIONS=none also disables them; brief button feedback remains.
submit_mode string "queue" "queue" or "interrupt": queue new prompts during work, or cancel/await current work before sending the new prompt.
tab_action string "agent" "agent", "complete", or "focus": cycle profiles, complete slash commands, or navigate widgets.

These are ngn settings, not arbitrary Textual theme names or CSS tables. Preset colors can evolve; there is no user-defined palette table in this schema. With theme_background: auto, terminal uses native terminal surfaces and the other presets use painted surfaces. "terminal" retains native surfaces for any preset, with preset-specific ANSI accents. "theme" paints the background; the terminal preset then uses a dark graphite-based surface fallback.

Browser GPT-Live preferences are configured in GPT-Live → Voice settings at either scope. See GPT-Live setup.

Storage and extensions

Field JSON type Default Accepted values and meaning
data_dir string path $XDG_DATA_HOME/ngn or ~/.local/share/ngn Full session-state root; converted to an absolute Path. JSON-relative paths use the file's directory.
mcp_servers mapping {} Named stdio MCP servers (command, args, optional env and cwd). Higher-priority maps replace lower-priority maps.
plugins list of strings [] Ordered "path.py:setup" or "installed.module:setup" references. Converted to a tuple internally. Each higher-priority JSON list replaces the previous list.
profiles mapping of profile mappings No custom profiles Entries under profiles.NAME, merged by name with whole-profile replacement. Built-in profiles remain available.

workspace (Path), trust_project (bool, default False), and diagnostics (tuple[str, ...], initially empty) also exist on the Python HarnessConfig object. They are runtime/loader inputs or outputs, not accepted JSON fields. Select workspace and trust through the CLI; diagnostics are generated by loading.

Environment defaults

Every configurable top-level scalar field in the tables above has an NGN_<UPPERCASE_FIELD_NAME> environment default. This includes NGN_API, NGN_MAX_SUBAGENT_DEPTH, and NGN_THEME_BACKGROUND as well as provider, model, limits, and state storage.

NGN_THEME=ocean NGN_ANIMATIONS=false ngn --demo
NGN_SUBMIT_MODE=interrupt NGN_MAX_SUBAGENT_DEPTH=1 ngn
NGN_PROVIDER_ID=team-openai ngn --model gpt-6-luna

JSON can override every one of these defaults. Booleans accept case-insensitive true/false or 1/0 in the environment. Integer fields are converted with integer parsing; shell_timeout uses floating-point parsing. Normal value/range validation still applies.

There is no NGN_PLUGINS list or NGN_PROFILES mapping parser, no per-profile environment-variable convention, and no NGN_CONFIG, NGN_WORKSPACE, or NGN_TRUST_PROJECT configuration mechanism. Use JSON, --config, --workspace, --trust-project, and repeatable --plugin flags as appropriate. Unknown NGN_* environment names are not a substitute for supported fields.

Agent profiles

assistant is the only built-in agent and is reserved. Other agents come from JSON profiles or discovered Markdown and TOML definitions.

Use read_only: true (or NGN_READ_ONLY=true) to constrain the assistant and its children to read-only operation. This startup restriction cannot be relaxed by switching profiles or saving web preferences. Select assistant in JSON and NGN_AGENT; use read_only for its read-only mode. Other names must refer to explicitly configured custom profiles.

Profile Mode Purpose
assistant build Default interactive agent and default delegation target. Guarded changes and shell require approval; child agents inherit their parent's permission ceiling.

Custom profile names match [A-Za-z0-9_-]{1,128}. Each profile accepts these fields:

Profile field Default Meaning
mode "build" "build" for normal guarded operation or "reviewer" for read-only operation. These are permission modes, not additional built-in agents.
instructions "" Trusted profile instructions, appended to the harness context. They do not grant extra permissions.
model "" Optional model override on profile activation. Empty keeps the current/top-level model.
provider "" Optional named connection ID in providers.json. Empty follows the active connection. The profile's model, if set, overrides the shared chat-model choice.
description "" Description shown to the model and agent picker.
tools omitted Tool-name array; omission inherits, [] permits no tools.
denied_tools [] Additional tool-name denials.
allowed_agents omitted Delegation target-name array; [] disables delegation.
user_invocable true Boolean controlling user selection.
subagent_available true Boolean controlling delegation eligibility.
{
  "agent": "audit",
  "profiles": {
    "audit": {
      "mode": "reviewer",
      "instructions": "Report bugs with file references and explain missing tests."
    },
    "implementation": {
      "mode": "build",
      "instructions": "Make the smallest correct change and verify it."
    }
  }
}

Activate with --agent audit or /agent audit; Tab normally cycles profiles. A nonempty profile model is applied when the harness activates that profile, after top-level model loading, so it can also supersede the value supplied by --model. Leave a profile's model empty if you want top-level model selection to govern it. In-process /model changes are runtime state, not edits to JSON.

Merge and replacement rules

Profiles merge by name, not recursively by field. Different names from different files remain available, but a later profiles.audit entry constructs a new profile and replaces the earlier audit completely. Omitted fields in that replacement return to the profile defaults.

For example, if global JSON defines audit with mode: reviewer and a model, then a trusted project file containing only:

{
  "profiles": {
    "audit": {"instructions": "Focus on tests."}
  }
}

replaces it with a build-mode profile and an empty model override. It does not inherit mode: reviewer. Repeat the intended mode when redefining a read-only profile. An empty "profiles": {} mapping does not clear previously defined profiles, and no profile deletion syntax is provided.

Delegation defaults to agent, rather than silently forcing every child into reviewer. Child authority can never exceed the parent's effective permissions: a reviewer parent cannot create a build-capable child by selecting agent, build, or a custom profile. Depth limits count the root as zero. Profile instructions cannot bypass approvals, depth limits, or the permission ceiling.

MCP servers and live reload

Both ngn serve and the TUI load MCP tools through the same harness. Configure stdio servers in the user config or an explicitly trusted project config:

{
  "mcp_servers": {
    "docs": {
      "command": "python",
      "args": ["/opt/my-mcp-server.py"],
      "cwd": ".."
    }
  }
}

cwd resolves relative to the configuration file; omitted cwd uses the workspace. command and args are executed directly, without a shell. Servers inherit the ngn process environment; optional env entries override it. Keep secret values in the deployment environment instead of committing them to configuration. Remote HTTP/SSE transports are not supported by this stdio loader.

The harness rereads the trusted configuration sources, tool selections, discovered skills and applicable instructions after every completed model response, even when files are unchanged and even for a final answer. It reloads Python extension source and reuses healthy MCP connections whose declaration, direct script source, and effective process environment are unchanged. A server's tools/list_changed notification triggers fresh discovery on the same connection at the next boundary. Added, changed and removed definitions become available without restarting the UI or losing conversation history. User-message boundaries also refresh. A changed server can briefly have overlapping old and new instances while calls finish. The tool-batch boundary ensures that a tool's own configuration/source edits are visible to the immediately following model request.

Tool calls returned by a response execute against that response's original implementation and schema. Its old MCP process stays alive until those calls and approvals finish; current permissions still apply. Saved tool allowances match captured source/configuration/schema identities, so unchanged reloads preserve permissions and changed implementations require approval again. Changes to project instructions invalidate old file-edit snapshots. Configuration parsing and Python extension errors retain the last working generation and produce a warning. MCP startup and discovery failures are isolated per server: other healthy tools, including newly added servers, stay available. An unavailable server is skipped at later response, tool, and message boundaries until its configuration, direct script source, or effective environment changes, or an operator explicitly retries. Environment values enter only a private digest; diagnostics never expose them. New configuration owners and parallel chat runtimes inherit known failures so saving settings or opening a chat does not silently retry a failed process. Cancellation/shutdown closes owned processes. Offline demo starts no MCP servers, and read-only profiles cannot launch custom server subprocesses. Native child agents do not inherit parent MCP servers.

A newly created <workspace>/.ngn/config.json does not become trusted just because it was written from the web chat. Without --trust-project or an explicit selection of that file, the running harness ignores it. Server logs report the trusted reload locations; Tools → MCP discovery and ngn doctor show the ignored source. An operator can merge the specifically approved MCP declaration into an already trusted user/host configuration, preserving its other settings. Preserve relative path semantics when moving a declaration: an explicit absolute cwd is useful. Restart with --trust-project only when trusting the entire project configuration is intended.

Reload locations preserve their logical paths. For example, a process started with --config /etc/ngn/config.json follows a replacement Kubernetes ConfigMap ..data symlink on its next reload instead of retaining an obsolete timestamped target. Relative provider files, data directories, Python plugin paths, and MCP working directories in an explicit symlink config retain their target-directory meaning. Each extension reload resolves that logical source again and reads the same target used as its relative-path base. Startup-only settings still require their normal restart or settings action. ngn serve logs an MCP discovery summary at startup and on every reload, including unchanged settings and zero configured servers. The summary includes configured and connected server counts, registered/available tool counts, logical ngn config paths and their read/trust status, and the effective MCP source. ngn doctor and the web Tools → MCP discovery section show the same metadata. When a reload fails as a whole, the counts and source list describe the retained working generation and the failure is reported separately. A partially available generation lists each unavailable server with its safe category and recovery guidance. Retry in Tools → MCP discovery attempts that server again; during active work it queues the attempt for the next safe boundary. Healthy connections remain open, and settings stay available while a retry is waiting. An attempt can still fail; its warning remains visible until recovery or removal from the trusted configuration. A higher-priority "mcp_servers": {} still clears earlier declarations; the effective source makes that precedence visible. Warnings distinguish configuration parsing, MCP startup and tool discovery failures. Tool-call authorization failures are separate from registration. Diagnostics do not echo server arguments, environment values, request/response bodies or credentials.

Startup provider/configuration details are logged once when the harness initializes and remain available through ngn doctor. They are not repeated in chat or Live delegation responses. New resource reload failures still produce an actionable conversation warning; their current status remains visible in MCP discovery.

Workspace .vscode/mcp.json and opencode.json files produce an ignored-config server log and appear in MCP discovery when present. ngn checks their existence without reading or importing their contents. Add only the approved mcp_servers declarations to a trusted ngn config; foreign files do not grant project trust. An explicitly selected --config path still follows ngn's configuration schema, regardless of its filename.

Verify activation in the running web Tools catalog and a subsequent model request. A successful connection from a separate MCPManager or CLI process only proves that separate process can discover the server. Tool discovery also does not prove that the server is authorized to call GitHub or another external service.

Reloading never expands trusted paths or replaces provider/login, session storage, permission ceilings, task ownership, or host approval handlers. Newly created files are discovered only at the trusted paths selected at startup. Changes to those host settings still use their existing settings/restart mechanisms.

Plugins

{"plugins": ["./extension.py:setup", "team_extensions.review:setup"]}

The setup name must be a Python identifier. Installed module names must be dot-separated identifiers. References are checked when loading configuration; Python import and setup happen when the harness initializes and at each model response boundary, not during JSON parsing. A setup function may be synchronous or asynchronous and may return an AgentPlugin or None.

JSON plugin lists replace the previous list, including "plugins": [] to clear lower-priority entries. Repeated --plugin flags append to the final JSON list in CLI order. There is no automatic deduplication of plugin references; listing one twice can invoke setup twice and can fail on duplicate registrations.

ngn --config /path/to/settings.json --plugin /path/to/extension.py:setup
ngn --plugin examples/harness/commands.py:setup

Plugins are fully trusted Python code, not a sandbox or just prompt text. They can access the network and filesystem during import/setup. ngn doctor normally initializes them too, so it is not a safe way to inspect unknown Python code. ngn doctor --demo skips plugin imports but still validates configuration and initializes local demo state. Subagents do not re-import parent plugin files. See Python extensions for the actual hook and command interfaces.

Provider recipes

These snippets are provider entries under providers.json → providers.NAME; the active name belongs in that file's active field. Set the chat model in config.json or the model selector. Put API-key values in your shell or secret manager, not in these files, URLs, or command arguments.

OpenAI API key

{
  "kind": "openai",
  "api": "auto",
  "auth": "api-key",
  "api_key_env": "OPENAI_API_KEY"
}

For eligible ChatGPT/Codex access instead, leave base_url unset, use "auth": "auto" or "chatgpt", and follow device login. That route can select a different account-supported default model.

OpenRouter

{
  "kind": "openrouter",
  "api": "chat_completions",
  "auth": "api-key",
  "api_key_env": "OPENROUTER_API_KEY"
}

The default endpoint is https://openrouter.ai/api/v1; no base_url override is needed. Set the referenced key in your launcher environment. Choose a model your OpenRouter account exposes and check that it supports tools. An explicit environment-file launch through uv is shown in the installation guide.

Anthropic

{
  "kind": "anthropic",
  "api": "auto",
  "auth": "api-key",
  "api_key_env": "ANTHROPIC_API_KEY"
}

Native Gemini

{
  "kind": "gemini",
  "api": "auto",
  "auth": "api-key",
  "api_key_env": "GEMINI_API_KEY"
}

LiteLLM gateway

{
  "kind": "litellm",
  "base_url": "http://127.0.0.1:4000/v1",
  "api": "chat_completions",
  "auth": "api-key",
  "api_key_env": "LITELLM_API_KEY"
}

Configure the base URL and choose a model alias for your running server; the example does not start a gateway or guarantee a route exists. For a server exposing Responses or Messages, choose responses or messages and the corresponding API base. The library also exposes legacy Completions, but ngn rejects that text-only route during harness setup. Gateway authentication uses the referenced LITELLM_API_KEY, never an ngn ChatGPT OAuth token. Legacy Completions remains text-only, not a promise of coding-tool support.

LiteLLM 1.100.0 has a legacy streaming usage-frame serialization issue. On that one provider/route combination, ngn does not request the optional extra usage frame; it still parses usage if the gateway supplies it. Error envelopes and incomplete streams remain errors, not successful responses.

Another OpenAI-compatible endpoint

{
  "kind": "openai_compatible",
  "base_url": "http://127.0.0.1:8000/v1",
  "auth": "api-key",
  "api_key_env": "LOCAL_MODEL_API_KEY"
}

Use the endpoint's required key in that environment variable. The harness still expects a nonempty referenced key for a live API-key request; do not assume it detects anonymous local servers automatically.

Inspect and diagnose

ngn doctor --demo provides local configuration diagnostics without model calls or Python plugin imports. /context shows effective runtime context, and /plugins shows configured/loaded extensions. A real ngn doctor loads trusted plugins, which can perform arbitrary I/O. These commands can create local state; they are not side-effect-free JSON parsers.

If a setting is unexpected, check the global file, the workspace selected with -C, whether project trust was granted, the explicit file, and CLI flags in that order. Also check profile-specific models and runtime /model or /agent changes. Do not attach credential files, session databases, .env contents, or literal API keys to a diagnostic report.