Skip to content

@invokta/installer

@invokta/installer is a binary-only terminal application that detects supported MCP clients, updates their user configuration, and keeps enough local state to manage only the entries it owns. It never invokes an Action Engine.

The shortest path is generated with every starter engine:

Terminal window
npm create invokta-engine@latest my-engine
cd my-engine
npm run mcp:install
# Later, remove the same logical engine from every managed client:
npm run mcp:uninstall

mcp:install builds the engine, validates its local installation manifest, preselects every eligible client, and requests one confirmation before any write.

The generated project already includes the installer. Add it manually to an existing engine when needed:

Terminal window
npm install --save-dev @invokta/installer

The package is native ESM, requires Node.js 22.20.0 or later, and publishes the invokta-installer binary plus one embeddable subpath export, @invokta/installer/engine. The package root exports no JavaScript API.

The bundled capability registry may remain empty. Explicit local-engine and remote-HTTP commands supply their own reviewed descriptors; new managed state persists the launch descriptor required for later lifecycle operations.

All interactive commands require both standard input and standard output to be attached to a TTY. Help and version output do not require a TTY.

invokta-installer
invokta-installer install --engine <project-directory>
invokta-installer install --http <server-name> <url>
[--bearer-token-env <NAME>] [--header-env <HEADER=NAME>]...
invokta-installer status
invokta-installer enable
invokta-installer disable
invokta-installer remove
invokta-installer remove --engine <project-directory>
invokta-installer --help
invokta-installer --version

The argument-free command remains a read-only inventory. It shows detected client evidence, safe configuration paths, and reload guidance without reading configuration contents or changing files.

Terminal window
invokta-installer install --engine .

The directory must contain a built entry point and invokta.mcp.json. A generated project provides the build-first package script below, which is the recommended interface:

{
"scripts": {
"mcp:install": "tsc -p tsconfig.json --pretty false && invokta-installer install --engine .",
"mcp:uninstall": "invokta-installer remove --engine ."
}
}

The manifest is strict UTF-8 JSON with this closed version-one shape:

{
"schemaVersion": 1,
"id": "support-engine",
"version": "0.1.0",
"title": "Support Engine",
"description": "Classify and route support tickets.",
"capabilityIds": ["support.classify-ticket"],
"server": {
"name": "support-engine",
"entrypoint": "dist/mcp-stdio.js",
"forwardEnv": ["SUPPORT_API_TOKEN"]
}
}

The manifest declares only metadata, a project-relative entry point, and environment variable names. It cannot choose an arbitrary command or persist an environment value. The installer validates owned, no-follow path components and the built regular file, then records the current Node executable and absolute entry-point path. It does not import, reflect on, or execute project code.

Moving or deleting the project invalidates the recorded launch descriptor. Build and install again from the new location to update it explicitly.

The uninstall script is intentionally build-free. It validates the project manifest but does not require the compiled entry point, recorded Node executable, forwarded environment variables, or an installed client executable. The manifest id selects the same logical engine across every managed target; project location and mutable manifest metadata are not provenance.

Embed install commands in an engine binary

Section titled “Embed install commands in an engine binary”

A packaged engine can ship its own installation command instead of asking the consumer to locate the package directory. Generated Invokta starters remain private by default, so registry publication requires an explicit author choice of package access and license metadata plus removal of "private": true. The @invokta/installer/engine subpath exports runEngineInstallerCli, which accepts exactly install, uninstall, and --help and runs the same engine-scoped interactive sessions against the embedding package root:

#!/usr/bin/env node
import { fileURLToPath } from "node:url";
import { runEngineInstallerCli } from "@invokta/installer/engine";
process.exitCode = await runEngineInstallerCli({
argv: process.argv.slice(2),
binaryName: "support-engine",
packageRoot: fileURLToPath(new URL("..", import.meta.url)),
});

With a package bin entry pointing at the compiled module, the consumer runs:

Terminal window
npm install --global support-engine
support-engine install
# Later:
support-engine uninstall

Generated MCP stdio projects include this composition root, the bin entry, and @invokta/installer as a runtime dependency. The embedded surface keeps the manifest contract, TTY requirement, confirmations, diagnostics, and exit statuses of the standalone commands, and it never builds, downloads, imports, or executes the engine.

Register an existing Streamable HTTP endpoint directly:

Terminal window
invokta-installer install --http support-engine https://support.example.com/mcp \
--bearer-token-env SUPPORT_API_TOKEN \
--header-env X-Support-Tenant=SUPPORT_TENANT

This command performs no network request. The URL must be canonical HTTPS with a canonical MCP mount path: /mcp or an unreserved ASCII prefix ending in /mcp, with no empty or dot segments and at most 256 bytes. For example:

Terminal window
invokta-installer install --http brain http://127.0.0.1:3100/e/brain/mcp

Literal 127.0.0.1 and [::1] HTTP origins are accepted for local development. User information, query strings, fragments, embedded credentials, percent encoding, and other noncanonical paths are rejected.

Mounted paths require installer 0.8.2 or later (ADR 0040). Version 0.8.1 and earlier accept only /mcp. To select the compatible release explicitly:

Terminal window
npx @invokta/installer@0.8.2 install --http brain http://127.0.0.1:3100/e/brain/mcp

The installer registers the URL; the selected client handles OAuth when it connects.

Authentication and header options name environment variables; they never read or persist their values. --header-env may be repeated. Header names are normalized and duplicate, reserved, or conflicting authorization headers fail closed.

An installation includes targets that have either a safely discovered user configuration or an installed client executable. A missing configuration file may be created only when executable evidence exists. Compatibility checks then remove clients that cannot represent the requested transport or environment references.

All remaining targets are preselected. You may change the selection, then one confirmation authorizes the complete set of per-client transactions. A failure in one client is reported independently and does not undo a different client that already committed. Repeating the same installation is idempotent.

status is read-only and reports installer-managed entries as enabled, disabled, outdated, drifted, unavailable, or missing their runtime requirement.

enable and disable present only eligible managed entries, select one interactively, and require confirmation. Disable uses the client’s native toggle when available and otherwise detaches the server definition while retaining its descriptor in installer state. Enable restores the exact managed definition.

remove lets you select one or more managed installations, including a remote engine such as brain across several clients:

Terminal window
invokta-installer remove

No entries are preselected. Use the arrow keys to move, Space to select an entry, or A to select all eligible entries. Press Enter to review the selected engine/client pairs, then confirm once. Removal deletes only those owned server entries and state records, preserving other installations and the surrounding client configuration. It reports each result and continues if an individual removal fails; successful removals remain committed.

Multiple selection requires installer 0.9.0 or later (ADR 0041). Version 0.8.2 and earlier select one entry at a time. To use the release with multiple removal:

Terminal window
npx @invokta/installer@0.9.0 remove

An entry changed outside the installer is drift and is never overwritten or removed automatically. A different server using the same name is a conflict.

remove --engine <project-directory> is the project-scoped overload. It preflights every state record matching the manifest ID in stable target order, shows removable and blocked clients, and requests one confirmation for the complete removable set. Each target remains an independent transaction, so a failure stays unchanged without rolling back a different target that already committed. Repeating a successful removal is an idempotent no-op.

Every removable record must contain its persisted launch descriptor. Legacy records without one remain unavailable, and this command never reconstructs ownership metadata from the current manifest or bundled registry. A record using the manifest’s current server name under another identity fails the whole preflight as ENGINE_IDENTITY_MISMATCH before confirmation or writes.

The catalog recognizes twelve executable surfaces mapped to eleven user-scope configuration targets:

Target Executable evidence Default user configuration
Antigravity agy or antigravity ~/.gemini/config/mcp_config.json or an established ~/.gemini/antigravity/mcp_config.json
Claude Code claude ~/.claude.json
Claude Desktop Claude macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
Codex codex ~/.codex/config.toml
Cursor cursor or cursor-agent ~/.cursor/mcp.json
Grok Build grok ~/.grok/config.toml
Hermes Agent hermes ~/.hermes/config.yaml
Kimi Code kimi ~/.kimi-code/mcp.json
OpenClaw openclaw Its supported home or state directory
OpenCode v2 opencode2 ~/.config/opencode/opencode.json or .jsonc
Visual Studio Code code Linux: ~/.config/Code/User/mcp.json; macOS: ~/Library/Application Support/Code/User/mcp.json; remote server: its data/User/mcp.json

Antigravity CLI and IDE share one target. Cursor’s executable aliases also share one target. Claude Desktop mutation is macOS-only; it is unsupported on Linux and Windows, and VS Code mutation is unsupported on Windows because both clients use platform-specific configuration locations there.

Antigravity path selection is finite and fail-closed. One populated safe global configuration wins over an empty sibling, two populated variants report HARNESS_CONFIG_AMBIGUOUS, and a selected empty placeholder is initialized as a new server collection. The installer does not read configuration contents during detection.

When code resolves inside the exact VS Code Server or VS Code Server Insiders remote-cli layout, the installer writes the matching server-side remote user configuration. It does not infer a Windows host profile or add project, workspace, or named-profile configuration. If a previous Invokta release managed the same engine and target at the native Linux path, the next confirmed install removes that exact owned definition and installs it at the remote path.

On Windows, where no POSIX user id exists, the installer validates paths with a Windows ownership identity (ADR 0027): symbolic links and junctions are rejected, path identities are captured and revalidated before every write, and containment inside the user profile is preserved, so a globally installed engine package such as one under the npm global root installs and removes normally. Exact POSIX file modes are not asserted on Windows; directory confinement follows the user profile’s access-control lists.

VS Code uses its JSONC servers object and ${env:NAME} variables. Claude Desktop uses the JSON mcpServers object for credential-free local stdio servers. Its file contract cannot safely express forwarded environment names, and remote servers must be added through Claude’s connector interface, so those descriptors are filtered out for this target. Existing comments, trailing commas, unrelated servers, byte-order marks, newline conventions, and safe file modes are preserved according to each target contract.

Established client-specific path overrides remain supported, including CLAUDE_CONFIG_DIR, CODEX_HOME, GROK_HOME, HERMES_HOME, KIMI_CODE_HOME, OPENCODE_CONFIG_DIR, XDG_CONFIG_HOME, and the documented OpenClaw variables.

Each selected target is one transaction. The installer acquires bounded state and configuration locks, revalidates ownership and contents, atomically replaces the configuration, then atomically replaces its state. If the state commit fails, it restores the exact configuration pre-image. A rollback failure is reported explicitly for manual inspection.

State defaults to ~/.local/state/invokta/installer.json and honors an absolute XDG_STATE_HOME. It contains normalized launch structure, environment variable names, ownership fingerprints, and timestamps. It contains no environment values or configuration-file copies.

The installer makes no network request, package import, engine invocation, or shell execution while detecting or mutating clients. Diagnostics omit rejected values, configuration bytes, credentials, causes, and stacks.

Code Meaning
0 Help, version, inventory, installation, management, or an idempotent result succeeded
1 An operational validation, compatibility, lock, conflict, drift, write, or rollback failure occurred
2 Usage was invalid, a TTY was unavailable, or initialization failed
130 The user cancelled an interactive prompt

Installer diagnostics use stable installer-specific codes. They are not EngineError values and do not extend the seven capability invocation errors.

{
"exports": {},
"bin": {
"invokta-installer": "./dist/cli.js"
}
}

Registry parsers, target adapters, ownership planners, state transitions, locks, and filesystem primitives are internal implementation details.