Repository navigation
MCP Server
← Architecture · JSON-Output · Command-Registry · Compat-V15
This page describes how machine surfaces (MCP today, the HTTP API next) run nSelf commands. Both call one package, internal/invoke, so they cannot drift apart. nself mcp builds its tools, resources and search from the machine registry (see Generated tools). The 26 hand-written tools stay primary in v1.4 mode and become deprecated names in v1.5 mode; see cmd-mcp.
A machine request names a command from the machine registry and gives typed parameters. The invoker:
- looks the command up in the registry (an unknown path is
E432), - applies the exposure rules (a refusal is
E421, with the reason incause), - applies the confirmation gate (see Gate): the request runs, gets a confirmation back, or is refused (
E424,E425), - builds an argv from the typed parameters (a bad request is
E420), - runs
nself <path> --json ...as a child process of the same binary, never in the server process, - returns the child's single JSON document and its exit code, or an error envelope (
E422for no single document,E423for a timeout).
The child refuses to run anything but the command the invoker checked: the invoker sets NSELF_INVOKE_PATH to the classified path, and the child resolves its own argv with the same lookup it is about to execute and answers E421 when the two differ (for example after a plugin upgrade gave a leaf new subcommands, or the binary was replaced, while the server kept its registry from start-up). The variable is removed before the command runs, so nothing the command spawns inherits it.
Running as a child keeps global state, os.Exit and the project operation lock identical to a typed command. There is no shell anywhere: every request value is exactly one argv element.
The machine registry is the v1.5 registry of the running binary with installed plugin commands mounted. It is the same in both compat modes (Compat-V15), so tool names and routes are the v1.5 shape in 1.4.x too. The plugin set is read when the server starts; install a plugin, then restart the server.
A command is served only when all of these hold:
- it is runnable, not hidden, not deprecated, and its canon status is
core,subcommandorplugin(notpending,deprecated-shimorbuiltin), - its registry
surfaceis notcli-only(for exampleconfig export), - its JSON support is
envelope, - its effective output is
document. Stream commands are served only as HTTP NDJSON, and only when the effective side effect isread, - its effective side effect is
readorwrite, or it isremoteordestructiveand its registry row has aconfirmblock.
The effective side effect and output apply the flags a request sets: a flag that escalates a command to destructive, or that switches a document command to stream, changes the verdict for that request.
A remote or destructive command without a confirm block is refused with E421 ("no gate"). One with a block goes through the confirmation round trip below.
Flags that are never exposed: --json, --help, the root switches, hidden, deprecated and cli_only flags (every --reveal), the consent and plan-id flags of a confirm block, and, in document mode, a flag that carries the output: stream override. Secret values never leave the process: a flag or argument marked secret in the registry is replaced by [REDACTED] in the request id and in any E422 stderr tail.
A request is a JSON object. Every member is optional. Member names are matched exactly (args, not ARGS), a repeated member name (at the top level or inside flags) is E420, and so is an unknown member.
| Member | Type | Meaning |
|---|---|---|
args |
array of strings | positional values, in declaration order |
flags |
object | flag name to boolean, integer, number, string or array of strings |
argv |
array of strings | arguments for a plugin command that declares no args and no flags and has no subcommands (a leaf); items naming a root flag such as --no-monorepo are E420 because the plugin proxy would drop them |
confirm |
string, 64 hex characters | a plan id or a server-issued nonce (commands with a confirm block only) |
The params schema of each command (JSON Schema 2020-12, additionalProperties: false) is derived from the registry: prefixItems per declared argument, one typed property per exposed flag, defaults from the registry.
Example, a read:
{"args": ["BASE_DOMAIN"]}runs nself config get --json -- BASE_DOMAIN and returns the same bytes as nself config get BASE_DOMAIN --json.
Example, flags and a value that looks like a flag:
{"args": ["-x"], "flags": {"tags": ["a", "b"], "quiet": true}}becomes <path> --json --quiet=true --tags=a --tags=b -- -x. Flags are sorted by name, a boolean is always --name=true or --name=false (never a bare --name: a mounted plugin node parses no flags, and cobra's command lookup would read a bare unknown flag as taking the next word, swallow the -- and run the first positional as a subcommand), and a list is one --name=value per item. Positional values always come after --, so none of them can be read as a flag. A stringSlice flag is split on commas by the child, so an item that holds a comma, a quote or a newline is refused instead of being silently changed.
argv on a plugin command that has subcommands is E421: a mounted plugin node parses no flags, so the child would read the first word as a subcommand and run a node the exposure rules never checked. Its params schema does not offer argv. For a leaf, argv is the plugin's own input: the exposure decision covers the command at the requested path, not what the plugin binary does with its words. Accepted risk: free-form argv can carry the plugin's own undeclared flags (for example --force), which the side-effect classification cannot see, so a plugin leaf declared read that takes undeclared mutating flags can write. Plugin authors declare every flag with its side effect in the manifest to get the gate.
Refused with E420, naming the flag but never echoing its value: an unknown or unexposed flag, a value of the wrong type (1.5 for an integer), too few or too many args, a NUL byte anywhere, an element over 32 KiB or an argv over 128 KiB.
The request id names a request in logs. It is the lowercase hex SHA-256 of the canonical JSON {"args":[...],"argv":[...],"command":"<path>","flags":{...}}: keys sorted, no whitespace, confirm left out, missing members empty, and every secret argument and flag value replaced by "[REDACTED]" first. Redaction fails closed: a positional past the declared args and a flag the command does not declare are replaced too. Free-form plugin argv items cannot be marked secret, so every one is replaced by "[REDACTED]" before hashing (the id keeps their count) and scrubbed from E422 stderr tails. Two requests that differ only in a secret value share an id, and no id is derived from a secret. The id is not a confirmation: anyone can compute it.
The child gets the server's environment with these changes:
| Variable | Value |
|---|---|
NSELF_MCP_TOKEN |
removed |
NSELF_V15 |
1 |
NSELF_NONINTERACTIVE |
1 |
CI |
1 |
NO_COLOR |
1 |
NSELF_INVOKED_BY |
mcp or http
|
NSELF_INVOKE_DEADLINE_MS |
milliseconds left until the timeout, set at exec time; absent for NDJSON requests |
Any inherited value of these names is dropped first. The working directory is the server's project directory and the child has no standard input. Arguments are visible to other local users in the process list while the child runs, as for any command typed in a terminal.
A document request times out after 300 seconds by default (the invoker takes the timeout as an option; the servers expose it as --timeout). The child leads its own process group. The group gets SIGTERM, and SIGKILL five seconds later or as soon as the child is gone, so grandchildren (docker compose, ssh, helpers) do not outlive a timed-out or cancelled request; the result is E423. An NDJSON request has no timeout and ends when the client disconnects. A long-polling command such as nself ci runs wait reads NSELF_INVOKE_DEADLINE_MS and returns before the kill.
The child's standard output is capped at 32 MiB and must be exactly one JSON object. Anything else is E422 (exit class infra); its cause is the last 2 KiB of the child's standard error, with the request's secret values replaced and the usual redaction applied. A child envelope that carries meta.deprecations or meta.warnings passes through byte for byte.
The sse and http transports of nself mcp check a bearer token on every request. The token comes from, in order:
-
NSELF_MCP_TOKEN(sourceenv). - The file
~/.nself/mcp-token(sourcefile). It must be a regular file (a symlink is refused), mode0600on macOS and Linux, and not empty. A file that group or others can read is refused withE426; the message names the path andchmod 600. - Neither. In v1.5 mode (
NSELF_V15=1) the first loopback start generates 32 random bytes as 64 hex characters, creates~/.nselfat0700if it is missing (an existing one is narrowed to0700, never widened; if that fails and group or others can write to it, the start is refused), writes the file at0600and prints its path and acat <path>hint on standard error. The token value is never printed to any stream. Later starts read the file. In v1.4 mode (the default today) a start with no token serves an open loopback port and prints one warning.
Long-lived responses (SSE streams, a tool call that runs for minutes) are not cut by the read timeout: the request body is read under it, then the read deadline is cleared. A request without the right Authorization: Bearer <token> header gets 401 unauthorized with WWW-Authenticate: Bearer realm="nself-mcp". The comparison hashes both sides first, so it does not depend on the token length. With a token set, behaviour is the same in both modes. Both modes also read an existing token file, so a malformed or group-readable ~/.nself/mcp-token stops a v1.4 start with E426 too (the file is new in 1.5; nothing created it before). stdio needs no token. The invoker removes NSELF_MCP_TOKEN from the environment of the commands it runs.
| Flag | Default | Meaning |
|---|---|---|
--transport |
stdio |
stdio, sse or http. |
--port |
3825 |
Port for sse and http. |
--host |
127.0.0.1 |
Bind address for sse and http. |
--allowed-host |
none | A host name the server is reachable under. Repeatable. |
stdio ignores --host and --allowed-host. On sse and http the checks below run before the listener opens, in both compat modes, and a refused start writes no token file:
- Loopback hosts (
localhost, any127.0.0.0/8address,::1) are always allowed. - Any other host, including the empty host,
0.0.0.0and::, exits non-zero withE426unless at least one--allowed-host <name>is given and a token is configured on that run throughNSELF_MCP_TOKENor an existing~/.nself/mcp-token. A token that this run would generate does not count, and a second start after a generated run is refused the same way. -
--allowed-hostgates the bind. In v1.5 it also feeds the Host check:sseandhttpserve only a Host oflocalhost,127.0.0.1,[::1]or an--allowed-hostname, each with the listener port (ASCII case-insensitive), and answer anything else421withE428before the token is checked. An invalid--allowed-host(a port, a URL, an unbracketed IPv6) exitsE430. Behind a reverse proxy or TLS terminator in v1.5, forward Host as<allowed-host>:<listener port>(the same rule asnself admin serve); otherwise requests get421withE428. In v1.4 the Host header is checked only when no token is configured: such a server serves onlylocalhost,127.0.0.1or[::1]with the listener port, ignores--allowed-host, and answers anything else421withE428(a security exception to the v1.5 gate, D-0739). With a token configured, v1.4 does not check Host. - An accepted non-loopback start prints one warning that the bearer token crosses the network over cleartext HTTP. The server has no TLS: put a TLS terminator in front of it.
The HTTP server sets ReadHeaderTimeout 10 s and ReadTimeout 30 s and no write timeout, so SSE streams stay open. The bearer check runs first, so an unauthenticated request is answered 401 before any body byte is read; an authenticated body is capped at 4 MiB (413 above it). Headers, bodies and tokens are never logged.
The gate decides on the effective side effect of the request (flag escalation applied). A command's registry confirm block names its consent flags (--yes, --force, ...) and, optionally, its plan form (plan.flag, plan.id_flag).
- Consent and plan-id flags belong to the invoker. A request that sets one is refused with
E425before anything runs. -
readruns. -
writewithout a plan form runs, with no consent flags. - A command with a plan form and no
confirm: the invoker runs the plan form once (<path> --json --<plan.flag> ...) and readsdata.plan_idanddata.requires_confirmationfrom the child's change plan. Whenrequires_confirmationis true, or the class isremoteordestructive, it returns a confirmation of kindplanwhoseconfirmis the plan id. Otherwise it runs the command once with--<id_flag>=<plan_id>. A plan exec that fails returns the child's error envelope unchanged; a plan without a validplan_idorrequires_confirmationisE422. The plan exec and the run are two children, each with the full invocation timeout. -
remoteordestructivewithout a plan form and noconfirm: a confirmation of kindrequest, whoseconfirmis a nonce. - A server without a confirmation store refuses every remote and destructive command with
E421.
The gate is not a prompt-injection control: an agent that was told to confirm will confirm. The controls are the token, the loopback default, request validation, the confirmation round trip and the audit log; the HTTP API adds Host and Origin checks (nself mcp checks the Host header in v1.5, not the Origin).
A confirmation is a success envelope (HTTP 428 on the HTTP API, isError: false over MCP) whose data is:
{"confirmation_required": true, "kind": "request", "confirm": "<64 hex>", "side_effect": "destructive", "reason": "...", "plan": null}For kind plan, plan is the child's change plan. Nothing has changed yet: the plan exec reads only.
To go ahead, send the identical request again with "confirm": "<value>".
- Kind
plan: the command runs with its consent flags and--<id_flag>=<confirm>. It recomputes its plan and refuses withE450when the state moved since the plan; that envelope comes back unchanged. Start again withoutconfirmto get the new plan. - Kind
request: the nonce is 32 random bytes fromcrypto/rand, held in the server's memory with a digest of the full, unredacted request (so a different secret value is a different request), valid for 10 minutes and good for one run. The confirmed call must present a live nonce for the identical request; the nonce is then spent and the command runs with its consent flags. A reused, expired or unknown nonce, a value the client computed (the request id included), or a different request isE424, and the nonce is spent either way. Nonces die with the server process: after a restart, start the round trip again. The server keeps at most 1024 live nonces.
Mutations serialise through the project operation lock the child takes; contention in v1.5 mode is the child's E460.
Accepted risks:
- A plan confirmation also passes the command's consent flags, as D3 states. For
build,--yesalso accepts the second render after plugin effects, so the plan id binds the first render only. - The live nonce store is first in, first out. A client holding the token can issue 1024 confirmations and push out one that a person is about to approve; the round trip then starts again.
Every non-read invocation and every refusal is appended to ~/.nself/logs/machine-audit.jsonl (contract cli.machine-audit v1). Reads that run are not logged. The directory is created 0700 and the file 0600; writers append under an exclusive file lock, and the file rotates at 10 MiB to .1, .2, .3 (older ones are dropped).
One compact JSON object per line, keys in this order:
| Key | Value |
|---|---|
time |
RFC 3339 UTC with milliseconds |
transport |
mcp-stdio, mcp-sse, mcp-http or http
|
project |
absolute project directory |
command |
registry path |
side_effect |
effective side effect |
decision |
run, confirmation_required or refused
|
confirm_kind |
plan, request or null
|
request_id |
the request id (nonces are never logged) |
request |
{args, argv, flags} with secret-marked args and flags, flag values whose name looks secret (config.IsSecretKey: SECRET, PASS, KEY, TOKEN, CREDENTIAL, WEBHOOK, PRIVATE, DSN, SALT) and free-form argv items replaced by "[REDACTED]", then the usual redaction on every string |
exit_code |
the child's exit status, or null for a confirmation or an invoker refusal |
error_code |
the invoker's E42x, the child's error.code, or null
|
duration_ms |
wall time of the invocation |
Before any command that is not a read runs, the server checks that the audit file can be opened for append (a regular file, never a symlink); when it cannot (or the server has no audit log), the run is refused with E421 and nothing changes. A writer waits at most 5 seconds for another process's lock on the file. A write that fails after the run (a full disk) prints one line with the operating-system error on the server's standard error (never the request) and does not stop the server. An existing log directory that group or others can read (other nself commands create ~/.nself/logs at 0755) is narrowed to 0700.
What the gate is: a consent step. A remote or destructive call first gets a confirmation back, then runs only when the same call is repeated with the nonce. That proves the caller saw the confirmation. It does not prove a person agreed, and it is not authorization: whoever holds the token (or runs the stdio process) can run every exposed command, and an agent told to confirm will confirm. Treat the token like a shell on the project.
What protects the server:
-
Token.
sseandhttpneed the bearer token on every request (see Machine token).stdiohas no network surface and no token. The token is never printed, never put in a tool result or the audit log, and removed from the environment of every command the invoker runs. -
Loopback only by default. A non-loopback
--hostis refused unless--allowed-hostand a configured token are given. -
Host and Origin.
nself mcpanswers a Host it does not know with421(v1.5) and sends no CORS headers; the HTTP API also checks Origin. See Transports and --host. -
Request validation. Every request value is exactly one argv element, after
--for positional values. Unknown members, flags and tools are refused. A flag name, the consent flags and the root flags cannot be smuggled in as values. -
Single-use nonces. A nonce is bound to one request, lives 10 minutes, dies with the process and is spent by a confirmed call that reaches the nonce check, right or wrong. A request that fails validation first (for example a wrong argument count,
E420) does not reach the check and does not spend it. A nonce of one tool never confirms another. -
One operation per project. Write, remote and destructive commands take the project operation lock in the child. When two confirmed calls reach one project, one runs and the other is refused with
E460(HTTP409). The lock is taken by the child process, so it holds across servers and clients. - Audit log. Every mutation and refusal is recorded (see Audit log); a run is refused when the log cannot be written.
-
Tool names and text. A tool name is
nself_plus the command path, and onlya-z,0-9and_, at most 64 characters. A plugin command whose name is invalid, too long, taken by an earlier tool or a shortcut tool, or that has a flag name which is not plain text (at most 64 characters ofA-Za-z0-9_.-) is skipped with a warning on standard error and never stops the server; core commands claim names first. Summaries and flag descriptions from a plugin manifest are cleaned before they reach a tool description: controls, bidirectional and zero-width characters are removed, white space is collapsed and the text is cut at 500 characters. The side-effect sentence is always kept.
Known limits:
- Injection text in a plugin summary stays text. The generator bounds and cleans it; it cannot tell a hostile sentence from an honest one, and look-alike letters survive. Install plugins you trust.
-
nself://registryreturns the registry as the plugins declared it (JSON, not cleaned). - The live nonce store holds 1024 entries, first in first out (see the round trip above).
-
nself mcpdoes not check Origin. The Host check and the token are its defence against DNS rebinding. - The lock covers commands that take it. A read never does.
The adversarial suite runs with go test: TestMCPGenAdversarial (internal/mcpgen: hostile names, descriptions, schemas, arguments), TestMCPAdversarial (cmd/commands: concurrent destructive calls, nonce reuse, token leaks, hostile plugins, smuggling), and the invoker, HTTP and machine-auth suites in their packages. The Fuzz Nightly workflow fuzzes FuzzBuildArgv and FuzzDecodeRequest (internal/invoke), FuzzRoutePath and FuzzHostHeader (internal/httpapi) and FuzzToolName (internal/mcpgen) for five minutes each on hosted runners. A crasher fails the job and its input is uploaded as an artifact; commit it as a seed with the fix.
internal/mcpgen turns the machine registry into MCP tools. There is no hand-registered tool in v1.5 mode: every tool is a generated tool or a row of the legacy table.
| Part | Value |
|---|---|
| Name |
nself_ + the path words joined by _, - becoming _ (config secrets rotate is nself_config_secrets_rotate). A name over 64 characters or a collision fails the start. |
| Which commands | every command the exposure rules serve over MCP, installed plugin commands included. A plugin subcommand that declares no side effect counts as destructive and has no confirm block, so it is not listed. |
| Input | the machine request schema (args, flags, argv, confirm), additionalProperties: false. Flags with the output: stream override are not offered; a call that sets one is E421. |
| Output | the command's committed data schema (schemas/commands/) as outputSchema; a command with none gets {"type":"object"}. A command that can ask for confirmation allows the confirmation shape too. Data that is not an object is wrapped as {"value": ...} in the schema and in the result. |
| Annotations |
readOnlyHint for read, destructiveHint for destructive, openWorldHint for remote, taken at the worst class a flag can reach. |
| Result |
structuredContent is the envelope data, the text content is the full envelope, isError is true when the envelope has error. A confirmation is a success: isError is false and structuredContent.confirmation_required is true (see Confirmation round trip). |
| Errors | a bad request is an E420 error envelope in the result, never a transport error. A document that is not an envelope is E422. |
| Plugin tools | a plugin command whose tool name is over 64 characters, is another generated tool's name or is a legacy alias name is skipped with a warning on standard error; the other tools are unaffected. A plugin name that only collides with the shortcut or an alias added after the generated tools still stops the start. The same fault in a core command stops the start. In v1.4 mode a start that cannot build the generated surface serves the 26 hand tools and prints why. |
The committed tool list for the hermetic tree (no plugin installed) is internal/mcpgen/testdata/tools.golden.json; go test ./internal/mcpgen/ -run ToolListGolden -update rewrites it.
| v1.4 mode (default) | v1.5 mode (NSELF_V15=1) |
|
|---|---|---|
| Legacy tools | the 26 hand tools, behaviour unchanged | none registered by hand |
| Generated tools | added under names no legacy tool uses | all of them |
| Same-name tools | the hand tool answers | one generated tool with a dual-shape input |
| Other legacy names | the hand tool answers | deprecated aliases |
| Resources | the four hand resources, plus the new ones | all served from the registry |
| Server info | the same identity, no experimental flag | no flag |
No name is ever registered twice.
internal/mcpgen/legacy_aliases.yaml has one row per legacy name.
Merged (13). nself_status, nself_doctor, nself_logs, nself_config_get, nself_config_show, nself_config_set, nself_build, nself_start, nself_stop, nself_restart, nself_db_migrate_status, nself_backup_list, nself_deploy_status are the generated name of their command. Their input schema is the machine request plus the old parameters as Deprecated: properties.
- The call is in the legacy shape when at least one old-only property is present.
{}is the machine shape. - Old properties map through the row (
nself_logs {service, lines}runslogswithargs [service]andflags {tail: lines}). A legacy call applies the old defaults (linesis 50) and the old required members (key,value); a missing one isE420naming it. The machine shape keeps the command's own defaults. - A legacy call adds
meta.deprecations(removal v1.6.0). Mixing machine members and old properties in one call isE420. -
confirmworks in both shapes, so a legacynself_config_setcan still complete its confirmation. - The hand tools' tolerance is kept in the legacy shape: an empty
valueclears a key, a JSONnullcounts as not given (so the default applies), andlinesorinterval_secondsof zero or less means the default (50 and 60). A non-integerlines, or one too large for 64 bits, isE420. - The result changes from the old text to the command's
data. This output-schema change is a v1.5 break, listed in Compat-V15.
Aliases (12). nself_urls is status urls, nself_service_list is config services list, nself_env_list is config env list, nself_plugin_list is config plugins list, nself_plugin_install is add, nself_get_schema and nself_get_permissions are db hasura schema and db hasura permissions. The five sentry_* names map to the sentry monitors, sentry incidents and sentry status commands of the sentry-cli plugin; without that plugin a call is E404 and names nself add sentry-cli. Every alias description starts with Deprecated:, and every call adds meta.deprecations.
Shortcut (not a legacy name). nself_capabilities {query, limit} is the short form of nself_help_capabilities. It is registered in both modes, is not deprecated and adds no deprecation note.
Removed (1). nself_run_migration ran raw SQL outside the migration ledger. A call is E433 and names nself db migrate create and nself db migrate up.
Two limits come from the exposure rules, which this change does not touch:
-
envonnself_config_get,nself_config_showandnself_config_set. The--envflag belongs to theconfigparent, and the request schema offers only the flags a command declares itself. A call that setsenvisE420. Select the environment withnself config env usebefore the call. - A legacy name whose command is not exposed yet (no envelope:
start,stop,restart,config services list,config plugins list,add) stays registered, its description says so, and a call returns the invoker'sE421. It starts working when the command gets an envelope, with no change here.
| URI | Content |
|---|---|
nself://registry |
the machine registry document with installed plugin commands, always in the v1.5 command spelling. In v1.5 mode it equals the data of nself help --json. |
nself://errors |
every registered error code with summary, cause, fix and exit class |
nself://schemas/{name} |
a committed schema by file name; nself://schemas/commands/{name} and nself://schemas/invoke/{name} reach the subfolders. The bytes are the committed ones. |
nself://plugins/catalog |
the plugin catalogue as the local cache holds it. The server never fetches: with no cache the read fails. |
nself://config, nself://services, nself://env, nself://urls
|
in v1.5 mode the data of config show, config services list, config env explain, status urls, through the invoker. A URI whose command has no envelope yet keeps its hand handler. |
nself help capabilities <query> [--limit 5] searches the commands and the installable plugins by plain words, so an agent can find what to run. It is an ordinary registry command, so it is also the tool nself_help_capabilities and an HTTP route.
Each result is {kind, path or slug, summary, run, score}; run is the exact text (nself config secrets rotate, nself add k8s). A command scores 3 for each query word found in its path and 1 for each found in its summary. A plugin scores 2 for each query word found in its slug, summary, category or bundles. Words match when equal, when they share a stem (diagnose, diagnostics) or by a short synonym list (database and db, kubernetes and k8s). A parent command scores two below its best matching subcommand when that is higher. Plugin hits come from the cached catalogue only. cmd/commands/testdata/capabilities/queries.json records eight queries and the hit each must return in the top three.
The three prompts (diagnose-failure, add-service, prepare-deploy) are written by hand. A test checks that every nself ... command, tool name and resource they mention exists in the v1.5 registry and on the v1.5 server.
nself mcp setup prints, or with --write writes, the stdio config an AI client needs to start the server for a project. The config holds no token: stdio needs none.
--client |
File (with --write) |
Output |
|---|---|---|
claude-code (default) |
.mcp.json |
{"mcpServers":{"nself":{"command":"nself","args":["mcp"]}}} |
cursor |
.cursor/mcp.json |
the same JSON |
codex |
none (print-only) | a [mcp_servers.nself] TOML block for ~/.codex/config.toml
|
generic |
none (print-only) | the same JSON |
- Printing also puts a note on standard error: start the client in the project directory, because
nself mcpfinds the project from its working directory. -
--writecreates the file or adds thenselfentry to it. Every other key and server entry stays where it was, and annselfentry that already exists is left alone, so a second run changes nothing. A file that is not valid JSON is never overwritten. Files are written only under the project directory, and a symlink in the way is refused. -
--client codex --writeand--client generic --writeexit 1 withE401: copy the printed config yourself. -
--jsonprints one envelope;datais{client, path, content, written}.
nself mcp setup --agents-md prints the project AGENTS.md rendered from the command registry: the canon verbs with their summaries, the two-command flow, the JSON and exit-code contract and the MCP setup line. The body holds nothing that varies per project or machine, so the same binary renders the same bytes everywhere (nself.org builds its own AGENTS.md from the pinned CLI this way).
nself init writes the same file without prompting and reports it with one line on standard error (nothing on standard output changes; --quiet stays silent). The first line is a marker with the SHA-256 of the body:
<!-- GENERATED BY nself (mcp setup) sha256:<hash of the body> — edit freely; nself will not overwrite an edited file -->
init and mcp setup --agents-md --write write the file only when it is absent or its body still matches the marker's hash. A file you edited, or one nself did not generate, is never touched.
Getting Started
Commands
- Commands, Overview
- Lifecycle: cmd-init · cmd-build · cmd-start · cmd-stop · cmd-restart · cmd-dev
- Monitoring: cmd-status · cmd-logs · cmd-health · cmd-urls · cmd-doctor · cmd-monitor · cmd-alerts · cmd-sentry · cmd-watchdog
- Data: cmd-db · cmd-backup · cmd-dr · cmd-queue · cmd-webhooks
- Config: cmd-config · cmd-service · cmd-env · cmd-promote
- Networking: cmd-ssl · cmd-trust · cmd-dns-setup
- Security: cmd-access · cmd-security · cmd-secrets
- Tenancy: cmd-tenant · cmd-billing
- Plugins: cmd-plugin · cmd-license · cmd-dogfood (extracted, CLI-R11) · cmd-k8s (extracted, CLI-R11) · cmd-encryption (extracted, CLI-R11) · cmd-waf (extracted, CLI-R11) · cmd-federation (extracted, CLI-R11) · cmd-mail (extracted, CLI-R11) · cmd-dlq (extracted, CLI-R11)
- AI: cmd-ai · cmd-claw · cmd-model
- Templates: cmd-template
- Utilities: cmd-exec · cmd-clean · cmd-reset · cmd-update · cmd-upgrade · cmd-version · cmd-admin · cmd-migrate · cmd-migrate-firebase · cmd-migrate-supabase · cmd-completion
Features
- Features, Overview
- Feature-Auth
- Feature-Storage
- Feature-Search
- Feature-Functions
- Feature-Email
- Feature-Monitoring
- Feature-Plugins
- Feature-nClaw, AI Assistant
- Feature-nChat, Messaging
- Feature-nFamily, Family Social
- Feature-nCloud, Managed Hosting
- Feature-Memory-Rooms, Knowledge Organization
- Feature-Agent-Dashboard, Agent Metrics
- Feature-Image-Generation, AI Image Generation
Configuration
- Configuration, Overview
- Config-Env-Vars
- Config-Postgres
- Config-Hasura
- Config-Auth
- Config-Nginx
- Shared-Proxy
- Config-Optional-Services
- Config-Custom-Services
- Config-System
Plugins (87 + 10 monitoring)
- Plugin-Overview
- Plugin-Catalog
- Plugin-Install
- Plugin-Licensing
- Plugin-Architecture
- Plugin-Dev-Guide
- Plugin-Migrations-Contract
Free (25)
- plugin-backup
- plugin-content-acquisition
- plugin-content-progress
- plugin-cron
- plugin-donorbox
- plugin-feature-flags
- plugin-github
- plugin-github-runner
- plugin-invitations
- plugin-jobs
- plugin-link-preview
- plugin-mdns
- plugin-mlflow
- plugin-monitoring
- plugin-notifications
- plugin-notify
- plugin-paypal
- plugin-search
- plugin-shopify
- plugin-stripe
- plugin-subtitle-manager
- plugin-tokens
- plugin-torrent-manager
- plugin-vpn
- plugin-webhooks
Pro (62)
- plugin-access-controls
- plugin-activity-feed
- plugin-admin-api
- plugin-analytics
- plugin-auth
- plugin-bots
- plugin-browser
- plugin-calendar
- plugin-cdn
- plugin-chat
- plugin-claw
- plugin-claw-budget
- plugin-claw-news
- plugin-claw-web
- plugin-cloudflare
- plugin-cms
- plugin-compliance
- plugin-ddns
- plugin-devices
- plugin-documents
- plugin-entitlements
- plugin-file-processing
- plugin-game-metadata
- plugin-geocoding
- plugin-geolocation
- plugin-google
- plugin-home
- plugin-idme
- plugin-knowledge-base
- plugin-linkedin
- plugin-livekit
- plugin-media-processing
- plugin-meetings
- plugin-moderation
- plugin-mux
- plugin-object-storage
- plugin-observability
- plugin-photos
- plugin-podcast
- plugin-post
- plugin-realtime
- plugin-retro-gaming
- plugin-rom-discovery
- plugin-social
- plugin-sports
- plugin-support
- plugin-tmdb
- plugin-voice
- plugin-web3
- plugin-workflows
Planned (26)
plugin-auditplugin-blogplugin-checkoutplugin-commerceplugin-drmplugin-exportplugin-flowplugin-importplugin-ldapplugin-mailgunplugin-mediaplugin-oauth-providersplugin-pagesplugin-postmarkplugin-rate-limitplugin-reportsplugin-samlplugin-schedulerplugin-sendgridplugin-ssoplugin-subscriptionplugin-thumbplugin-transcoderplugin-twilioplugin-wafplugin-watermark
Guides
- Guide-Production-Deployment
- Guide-SSL-Setup
- Guide-Multi-Tenancy
- Guide-Security-Hardening
- Guide-Monitoring-Setup
- Guide-Backup-Restore
- Guide-Custom-Services
- Guide-Migration-from-v1
Architecture
- Architecture
- Architecture-Guardrails
- Service-Graph
- Compose-Generation
- Nginx-Generation
- Plugin-Architecture
Reference
Machine interface
-
JSON-Output,
--jsonenvelope -
Command-Registry,
nself help --json - Exit-Codes
- error-codes, Error Codes
-
Compat-V15,
NSELF_V15gate
Licensing
Security
Brand
Operations
- operations/release-cascade, Release Cascade
- operations/self-healing, Self-Healing Schema
- operations/redis-tuning, Redis Pool Tuning
- operations/meilisearch-warmup, MeiliSearch Warm-Up
- operations/jwt-rotation, JWT Key Rotation
- operations/windows-wsl2-setup, Windows / WSL2 Setup
- operations/gemini-oauth-reauth, Gemini OAuth Reauth
Contributing
Admin
- USER-ACTION-QUEUE, Pending Admin Actions
All commands (21)
- A: cmd-add · cmd-admin
- B: cmd-backup · cmd-build
- C: cmd-clean · cmd-config
- D: cmd-db · cmd-deploy · cmd-doctor
- E: cmd-exec
- I: cmd-init
- L: cmd-license · cmd-logs
- M: cmd-mcp
- R: cmd-remove · cmd-reset · cmd-restart
- S: cmd-start · cmd-status · cmd-stop
- U: cmd-update