npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nothumanwork/codemode

v0.1.2

Published

Run strictly typed code against configured MCP servers

Readme

codemode

codemode exposes configured MCP servers to ordinary JavaScript or TypeScript through a typed library. Its optional CLI can also generate exact types from each server's JSON Schemas, strictly check a complete program, and then execute it with Bun.

Codemode requires Bun 1.3 or newer. Run the published CLI without creating a package.json or installing it locally:

bunx --package @nothumanwork/[email protected] codemode --help

From this checkout:

bun install --frozen-lockfile
bun ./src/cli.ts --help

The package bin is named codemode and points to the bundled dist/cli.js. The examples below assume that bin is on PATH; from this checkout, replace codemode with bun ./src/cli.ts.

Compile one standalone executable

To produce a single executable containing Bun, codemode, and its dependencies:

bun run compile
./dist/codemode --help

The output is dist/codemode on macOS and Linux or dist/codemode.exe on Windows. It does not require Bun or node_modules at runtime. The compile workflow embeds the Bun runtime plus the Bun, Node, and TypeScript standard-library declarations that the strict runtime checker needs when it validates scripts.

On macOS, Bun 1.4 may leave an invalid embedded signature after attaching the standalone payload, causing the kernel to kill the executable before it reaches --help. The compile script ad-hoc signs the completed Mach-O and verifies that signature before reporting success. Ad-hoc signing is sufficient for local execution; distributing a trusted macOS binary still requires the appropriate Developer ID signing and notarization workflow.

Quick start: run an ordinary typed MCP script

The library loads the configured MCP servers, discovers their tools, makes the requested call, and closes its MCP connections. It does not require a local package.json, an init step, generated declarations, or the codemode command.

  1. Point codemode at an MCP configuration. If this variable is omitted, codemode uses ~/.pi/agent/mcp.json.

    export MCP_SERVER_CONFIG_PATH=/absolute/path/to/mcp.json
  2. Ensure that the configuration names the server you want to call. This minimal example connects to a Streamable HTTP server as mmr:

    {
      "mcpServers": {
        "mmr": {
          "url": "http://127.0.0.1:3000/mcp"
        }
      }
    }
  3. Create check-status.ts. Importing the package root provides editor types for the stable call_tool and search_tools API without an init command or generated declaration:

    import { call_tool, search_tools } from "@nothumanwork/codemode";
    
    const hits = await search_tools("workspace status");
    const result = await call_tool("mmr_status", {
      source: "codex",
      project: "codemode",
    });
    
    if (result.isError) {
      console.error(result.content);
      process.exitCode = 1;
    } else {
      console.log(JSON.stringify(result.structuredContent, null, 2));
    }
  4. Run the script:

    bun run ./check-status.ts

When no local install exists, Bun resolves the bare package import without requiring a project manifest. Each direct search_tools or call_tool invocation discovers the current catalog from the configured servers, performs the operation, and closes all connections before resolving. The script is responsible for printing any result.

The published package can type only the stable API because each user's MCP tools come from local configuration. To make those configuration-specific tool names, inputs, and outputs visible in the editor and strictly check them before execution, use the optional CLI: run codemode init ./check-status.ts, generate a sidecar with codemode generate --out ./check-status.codemode.d.ts, or execute the script with codemode run ./check-status.ts.

For a quick stdin program:

printf '%s\n' 'const result = await call_tool("mmr_status", { source: "stdin" });' 'console.log(result.structuredContent);' | codemode run - --loader ts

The call syntax is independent of configured server names, including names that are not valid JavaScript identifiers.

An ordinary library script may use normal imports and exports. The stricter codemode run workflow accepts the Codemode package root through its bare, npm:, or jsr: forms, but rejects other static imports and all exports. Neither mode is a sandbox; scripts retain their normal filesystem, environment, process, and network authority.

Agent discovery and skill

For a large catalog, prefer lexical tool search before dumping the full inventory:

codemode search "workspace status"
codemode search "billing" --json --limit 10

search loads the current artifact generation (regenerating when missing or stale), ranks tools with BM25F over name, description, parameter names/descriptions, and server name, and returns enough detail to call a match: name, server, optional description, score, full inputSchema, and a safe call_tool(...) snippet. Scores are corpus-relative implementation data; ranking and deterministic ties are the compatibility contract. Search is lexical only: it does not provide embeddings or semantic matching, stemming, fuzzy matching, synonym expansion, or live server search. Search is local after a fresh artifact is available and does not contact MCP servers. Regeneration contacts all configured servers the same way as generate/inspect. Remote catalog changes without a config edit still require codemode generate or a daemon-triggered refresh.

Use inspect when you need the complete catalog:

codemode inspect

inspect prints JSON describing the MCP abstractions codemode currently accepts. It ensures a current artifact generation exists, then returns the resolved config path, artifact and declaration identifiers, and a sorted servers array. Each server includes its configured name and the persisted callable definition for each tool: name, optional description, inputSchema, and optional outputSchema. Pass the exact tool name to call_tool.

For example, the inventory shape is:

{
  "schemaVersion": 2,
  "configPath": "/absolute/path/to/mcp.json",
  "artifactId": "...",
  "declarationPath": "/absolute/cache/path/mcp.d.ts",
  "servers": [
    {
      "name": "mmr",
      "tools": [
        {
          "name": "mmr_status",
          "description": "...",
          "inputSchema": { "type": "object", "properties": {} },
          "outputSchema": { "type": "object", "properties": {} }
        }
      ]
    }
  ]
}

Transport configuration, headers, environment values, internal config fingerprints, and non-callable tool metadata such as _meta, icons, annotations, and execution hints are not included. inspect reuses a fresh generation; after a server changes its tools without a config change, run codemode generate before inspecting again to force live discovery.

The CLI also carries a self-contained agent skill:

codemode skill

This prints skills/codemode/SKILL.md, including the discovery workflow, binding rules, result handling, and runtime safety constraints. The Markdown is embedded in a standalone executable. To install it for an agent that discovers skills from a directory:

mkdir -p ~/.codex/skills/codemode
codemode skill > ~/.codex/skills/codemode/SKILL.md

codemode skill does not read the MCP configuration or connect to any server.

Configure MCP servers

By default, codemode reads ~/.pi/agent/mcp.json. Set MCP_SERVER_CONFIG_PATH to use another file. Relative paths are resolved from the current working directory, and a leading ~/ is expanded.

The file must have one mcpServers object. A server selects exactly one transport by defining either command (stdio) or url (Streamable HTTP); do not add a transport field.

{
  "mcpServers": {
    "mmr": {
      "command": "bun",
      "args": ["/absolute/path/to/mmr-server.ts"],
      "cwd": "/absolute/path/to/project",
      "env": {
        "MMR_TOKEN": "replace-me"
      },
      "lifecycle": "lazy",
      "timeout": 10000
    },
    "remoteMmr": {
      "url": "http://127.0.0.1:3000/mcp",
      "headers": {
        "Authorization": "Bearer replace-me"
      },
      "lifecycle": "eager",
      "timeout": 15000
    }
  }
}

Stdio entries accept command, plus optional string-array args, string cwd, string-valued env, lifecycle, and timeout. HTTP entries accept url, plus optional string-valued headers, lifecycle, and timeout. Unknown fields are rejected. lifecycle is lazy by default or eager; eager servers connect when a runtime client manager starts, while lazy servers connect on first use. Schema discovery still contacts every configured server. timeout is a positive integer in milliseconds and applies to connection, tool listing, and tool calls; the default is 10 seconds.

Configured stdio environment values are added to the MCP SDK's default child environment. HTTP headers are sent on transport requests. Treat both as credentials. Static HTTP configuration cannot override the SDK-owned Mcp-Session-Id, Mcp-Protocol-Version, Mcp-Method, or Mcp-Name headers. On shutdown, codemode asks a Streamable HTTP server to terminate its session before closing the transport; HTTP 405 remains an allowed server response under the MCP contract.

Generate and locate types

codemode generate
codemode generate --out ./check-status.codemode.d.ts
codemode types-path

generate connects to all configured servers, lists their tools, and atomically publishes one complete artifact generation. It prints the generated declaration path. With --out, it also atomically copies the declaration to a stable path suitable for a script's triple-slash reference and prints that resolved output path. types-path does not contact servers or generate anything; it validates the current artifact against the config path, config contents, generator version, installed MCP SDK version, and artifact hashes, then prints its declaration path. It fails if no valid current generation exists.

Artifacts are isolated by a 24-character SHA-256 prefix derived from the normalized config path:

${CODEMODE_CACHE_DIR:-${XDG_CACHE_HOME:-~/.cache}/codemode}/<config-key>/
  current.json
  generation-status.json
  generations/<artifact-id>/
    manifest.json
    mcp.d.ts

CODEMODE_CACHE_DIR replaces the entire default cache root. Relative cache paths are resolved from the current working directory. The printed mcp.d.ts path is generation-specific and can change after regeneration, so consumers should ask types-path again rather than retain it indefinitely.

Generation is all-or-nothing. A successful generation moves current.json to the new content-addressed files and records stale: false. A discovery or schema-generation failure preserves the previous generation and records a redacted stale: true status when generation has begun. A configuration file that cannot be read or parsed fails before that generation status can be written.

codemode run uses a current artifact when it matches the config. If it is missing or stale because the config path/content, generator, SDK, or last generation outcome changed, run regenerates synchronously and fails if it cannot obtain current schemas. Codemode does not poll remote tool schemas: after a server changes its tools without a config change, run codemode generate (or rewrite/touch the config while the daemon is running) before relying on new types. A failed refresh leaves the last-good files and pointer in place for recovery, but run and types-path reject them until a generation succeeds.

Run code with the strict CLI

File input infers js, jsx, ts, or tsx from the filename (.mts and .cts are TypeScript):

codemode run ./check-status.ts
codemode run ./check-status.txt --loader ts

Use - for stdin. Stdin defaults to JavaScript, so pass --loader ts when it contains TypeScript syntax:

printf '%s\n' 'const result = await call_tool("mmr_status", { source: "stdin" });' 'console.log(result.structuredContent);' | codemode run - --loader ts

Programs may use top-level await. Named imports of call_tool and search_tools from the package root are checked and erased before execution:

import { call_tool, search_tools as search } from "@nothumanwork/codemode";

Within codemode run, versioned npm and JSR specifiers allow a self-contained script without a local package.json; editor support for these specifiers depends on the corresponding runtime extension:

import { call_tool } from "npm:@nothumanwork/[email protected]";
import { call_tool } from "jsr:@nothumanwork/[email protected]";

Use only one spelling in a script. The package supplies useful baseline input and MCP result types immediately. A generated *.codemode.d.ts is optional and adds exact, configuration-specific tool names and schemas that no registry package can know in advance.

The bindings may be aliased. Other static imports and all exports, including export modifiers and export assignments, are rejected with a CODEMODE_IMPORTS_UNSUPPORTED diagnostic; submitted source otherwise remains standalone. The legacy ambient globals remain available for existing scripts. Both TypeScript and JavaScript are checked with TypeScript's strict mode (checkJs is enabled) before runtime MCP clients are created. Invalid arguments, unknown tools, and invalid access to typed output therefore fail before a tool call. The CLI converts SIGINT/SIGTERM into a runtime interruption, closes MCP connections, reports a nonzero result, and then restores its signal handlers. This is cooperative lifecycle cleanup; synchronous trusted code cannot be forcibly preempted inside the same process.

The CLI does not automatically print the program's completion value. Use console.log, console.error, or other Bun APIs in the program when output is required.

MCP bindings and results

Codemode follows FastMCP Code Mode's compact call style and adds local BM25F discovery:

import { call_tool, search_tools } from "@nothumanwork/codemode";

const hits = await search_tools("workspace status", { limit: 5, minScore: 0 });
// hits[0]: { name, server, description?, score, inputSchema, snippet }

const output = await call_tool("mmr_status", {
  source: "codex",
  project: "codemode",
});

In an ordinary imported script, search_tools discovers the live catalog from every configured server and closes those connections before resolving. Under codemode run, the injected binding instead reads the generated search-index.json artifact and does not issue an MCP request after artifacts are current. Empty, stopword-only, and unknown-term queries return an empty array. Invalid query/options types throw TypeError at runtime (and fail TypeScript checking). Adding discovery does not change the trusted, non-sandbox runtime model.

Tool names must be unique across all configured servers so the two-argument call is unambiguous. Artifact generation fails with both server names when two servers expose the same tool name.

Tool calls return the full MCP SDK CallToolResult, not only the structured payload. This preserves fields such as content, isError, and _meta, while structuredContent is narrowed from the tool's outputSchema:

const result = await call_tool("mmr_status", { source: "codex" });

console.log(result.content); // full MCP content blocks
console.log(result.isError); // tool-level failure, when present
console.log(result.structuredContent?.source); // typed from outputSchema

The MCP SDK receives the discovered tool definition on each call and validates successful structuredContent against its outputSchema. Because structuredContent is optional in CallToolResult, the generated type remains optional. If a tool declares no outputSchema, its structuredContent type is unknown rather than an invented shape.

JSON Schema boundary

Codemode accepts JSON-serializable object inputSchema values and optional object outputSchema values that json-schema-to-typescript can compile without producing any. Standard nested object, array, composition, and local-reference shapes are supported subject to that compiler. Server-supplied schema titles are deterministically renamed so they cannot replace or collide with allocated TypeScript names, and the generated declaration is checked for its expected root type. External $ref values, tsType, tsEnumNames, invalid type keywords, non-object schemas, and schemas that require any fail the entire generation with server/tool context.

Generated TypeScript is a static approximation, not a replacement for server-side/runtime validation. Schema format does not narrow the TypeScript type, and tuple length constraints from minItems/maxItems are not represented. Codemode does not claim support for arbitrary JSON Schema dialects or extensions.

Daemon

codemode daemon start
codemode daemon status
codemode daemon restart
codemode daemon stop

The detached daemon watches the configured file's parent directory, generates immediately, and then regenerates after matching file changes (including atomic replacement). Changes are debounced and generations never overlap. It watches the config file, not remote servers.

start waits up to 15 seconds for the initial outcome and prints ready (pid ...) on success or degraded (pid ...): ... with exit code 1 on failure. Starting a live duplicate fails. restart stops the matching daemon first. stop prints stopped or not running and is idempotent.

status reports the live phase (starting, generating, ready, or degraded) and PID. It exits 1 for degraded, stale daemon state, or stopped; healthy live phases exit 0. A failed refresh retains the last-success timestamps/fingerprint in daemon status and can recover after the next valid config change. Graceful shutdown removes the PID and lock files, retains a stopped status and logs, and rotates the log near 1 MiB.

Daemon state is isolated by the same config-path key:

${CODEMODE_STATE_DIR:-${XDG_STATE_HOME:-~/.local/state}/codemode}/<config-key>/
  daemon.lock
  daemon.pid
  daemon-status.json
  daemon.log
  daemon.log.1

CODEMODE_STATE_DIR replaces the entire default state root; relative values are resolved from the current working directory. Daemon ownership checks currently rely on ps outside the daemon process, so reliable status and stop behavior is currently limited to non-Windows systems.

Troubleshooting

  • Configuration cannot be read: print or set MCP_SERVER_CONFIG_PATH, prefer an absolute path, and validate that the JSON has only mcpServers and the transport-specific fields above.
  • types-path reports missing or stale artifacts: run codemode generate. Check <cache>/<config-key>/generation-status.json if generation fails.
  • Daemon is degraded: fix the reported config/server/schema problem, then save the config again. Inspect daemon-status.json and daemon.log under the state directory for bounded, redacted details.
  • stale daemon state: the recorded process is gone or does not match its instance identity. daemon start, restart, or stop cleans stale PID/lock/status files before proceeding.
  • Artifact generation reports a duplicate tool name: rename or namespace that tool at one MCP server. call_tool(name, params) requires tool names to be unique across the configured catalog.
  • A program fails before making a request: resolve the file/line TypeScript diagnostic. This is expected for schema mismatches and unsupported static module syntax.
  • A server changed tools without a config edit: run codemode generate, or trigger the watcher by saving the config, then obtain the new declaration path with types-path.

Design rationale and prior art

Codemode borrows the typed-tool idea while deliberately serving a different execution model:

  • TanStack AI Code Mode turns a tool set into an execute_typescript tool, generates stubs for the model prompt, and runs code through interchangeable Node, QuickJS, or Cloudflare isolate drivers. Its separate @tanstack/ai-mcp package provides MCP discovery and one-shot code generation; it does not provide this project's config watcher.
  • Cloudflare Code Mode exposes compact typed APIs (including search/execute for its large API surface) and runs generated code in isolated Dynamic Workers with outbound access blocked by default. The reusable implementation lives in cloudflare/agents/packages/codemode.
  • FastMCP Code Mode uses BM25 discovery before call_tool execution inside its Python sandbox. This project adopts the compact call style plus local BM25F search while retaining generated TypeScript input/output types.
  • This project provides a direct typed library plus a local, trusted-code CLI. The library discovers and calls MCP tools over stdio or Streamable HTTP. The CLI additionally publishes content-addressed declarations and a search index, strictly checks a complete script, injects search_tools and call_tool, and watches the MCP config for regeneration. Neither mode inherits the sandbox guarantees of FastMCP, TanStack's isolate drivers, or Cloudflare's Dynamic Workers.

Security

Codemode is not a sandbox. Ordinary imported scripts run as normal Bun programs. Programs submitted to codemode run are compiled into an async function and run in the codemode process. Both have full Bun, filesystem, network, environment, and process authority. Only run code you trust. The MCP configuration is also trusted: stdio entries can execute arbitrary commands, and HTTP entries can send configured credentials to their URLs.

Generated artifacts persist an allowlisted callable surface—server/tool names, descriptions, input/output schemas, derived search terms, and non-sensitive fingerprints—rather than transport configuration, environment, headers, or arbitrary tool metadata. Each generation directory holds manifest.json, mcp.d.ts, and search-index.json under the same content-addressed id. Surfaced generation errors are bounded and drop a secret-bearing cause. These are credential-hygiene defenses, not isolation from malicious code or servers.

Development

bun test
bun run typecheck
bun run format:check
bun run build
bun run compile

Publish releases

The npm package contains two independent surfaces: the bundled codemode bin and the typed, executable script library exported from the package root. JSR publishes the same library source and its runtime dependency graph; the CLI remains an npm/Bun distribution.

Keep the versions in package.json and jsr.json identical, then validate both package payloads without uploading:

bun run publish:check

Publish npm after authenticating. The scoped package is configured as public:

bun run login:npm
bun run publish:npm:dry-run
bun run publish:npm

JSR's publish command opens an interactive browser login when needed:

bun run publish:jsr:dry-run
bun run publish:jsr

The account used for each registry must control the nothumanwork scope. Registry versions are immutable, so bump both files before every subsequent release. This repository does not yet declare an open-source license; choose and add one before publishing if downstream users should be granted reuse rights.