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

@euqns/nudge-mcp

v1.43.0

Published

Model Context Protocol server for the Nudge board (the in-house Trello).

Downloads

5,448

Readme

Nudge MCP

A Model Context Protocol server that exposes the Nudge board (this repo's Trello-like app) to MCP clients — Claude Desktop, Claude Code, Cursor, anything that speaks MCP.

It lets an LLM list boards, create cards, assign people, move tasks between columns, drop comments, and read your notifications — using your identity, so mentions and activity logs attribute correctly.

Two ways to connect

Remote HTTP (recommended). Nudge serves the same tool surface directly from Convex at <deployment>.convex.site/mcp, over MCP's stateless Streamable HTTP transport (spec revision 2026-07-28). Nothing to install and nothing to keep updated — tool changes ship with the backend deploy:

claude mcp add --transport http nudge https://<deployment>.convex.site/mcp \
  --header "Authorization: Bearer nug_..."

Generate the token under profile → MCP tokens in the web app; that panel prints the full command with the token already filled in. The endpoint also answers 2025-era clients on the same URL, so older MCP implementations keep working.

Local stdio (this package). Still the right choice when a client can only launch a subprocess, and it's what the local Nudge Agent companion uses. See below.

The two paths serve an identical tool surface — convex/mcpToolDefs.ts is generated from this package by scripts/gen-convex-tooldefs.mjs, so they cannot drift. Re-run it after changing any tool:

cd mcp && npm run build && node scripts/gen-convex-tooldefs.mjs

How it authenticates

Nudge's auth is Google-only with an @joygame.com domain restriction, and the JWTs are signed by the Convex deployment itself — there's no key the MCP server could re-sign with from a CLI. So the MCP uses personal access tokens, bootstrapped over a one-shot localhost handshake:

  1. Run npx -y @euqns/nudge-mcp setup in a terminal.
  2. The CLI opens your browser to Nudge's /mcp-setup page (you'll sign in with Google if you aren't already).
  3. Click Authorize. The page generates a token in your name and sends it to the CLI over http://127.0.0.1:<random-port>/callback. The token is never displayed, never typed, never logged.
  4. The CLI writes ~/.nudge/credentials at mode 0600 and prints the snippet you paste into your MCP client — that snippet contains no secrets.

Replay protection is via a fresh per-run state nonce; mismatched POSTs are rejected with 403. The listener only binds to 127.0.0.1 and times out after 5 minutes.

You can manage tokens (name, last-used, revoke) from profile → MCP tokens in the web app.

Nudge Agent chat

The same package can connect Nudge chat to a coding agent already installed and authenticated on the user's computer. It binds to loopback and opens Nudge with a one-time pairing secret in the URL fragment:

# Sign in to any or all supported CLIs first
codex login
claude auth login
cursor-agent login

# Run this from the repository the agent should inspect
npx -y @euqns/nudge-mcp@latest agent

To have the companion come back on its own after a reboot, install it as a login service from the same directory:

npm i -g @euqns/nudge-mcp && nudge-mcp service install agent --no-open

--no-open keeps it from opening a browser tab at every login; the install command waits for the service to come up and prints the pairing URL in the terminal. Open it once. An installed service keeps its pairing (the loopback port and bearer token) in ~/.nudge/pairings/<label>.json, owner-only like everything under ~/.nudge, and reuses it on every start — rebuild restarts, crash restarts, logout/login and reboot alike — so the browser's saved connection keeps working: Nudge chat and the Agent View row show paired, not reachable while the service is down and go back to connected on their own once it is up, with no new URL and no Forget pairing. Later, nudge-mcp service status agent prints the same URL again for pairing another browser (only while the process is alive — a dead companion's URL is never shown). To revoke a pairing, run nudge-mcp service restart agent --reset-pairing: the old token is refused from then on and a new URL is printed. A companion started by hand (nudge-mcp agent in a terminal) keeps the old behaviour on purpose: its pairing lives in memory and a new run mints a new URL, since a pairing that outlived the terminal would point at a port nothing listens on. See Always-on services below for start/stop/restart/logs.

The companion checks Codex, Claude Code, and Cursor independently, starts when at least one has a valid subscription login, and reports which CLIs are ready. One pairing exposes every ready provider, so each chat can choose its own agent and resume with that same agent later. --provider codex|claude|cursor remains an optional initial preference. The companion does not accept or store provider API keys. It keeps local filesystem access read-only and allows only the exact Nudge origin that launched it. Authorized Nudge MCP tools can still update board data.

The Codex and Claude Code adapters supply their own Nudge MCP transport, so a fresh computer does not need a manual mcp_servers.nudge entry in ~/.codex/config.toml. Each computer still needs the one-time npx -y @euqns/nudge-mcp setup authorization so Nudge can identify that user. Cursor reads MCP servers from its normal .cursor/mcp.json configuration, so the Nudge MCP server must also be present there for board mutations.

Headless runner jobs isolate Cursor's MCP config to ~/.nudge/cursor-config/<job> via CURSOR_CONFIG_DIR, but deliberately leave HOME/XDG_CONFIG_HOME untouched — cursor-agent reads its login from there, not from CURSOR_CONFIG_DIR. A runner host needs either cursor-agent login once (the usual case) or CURSOR_API_KEY set in the runner's own environment; the CLI's own error message names both.

For Codex, the sidecar reads the installed model catalog and subscription account metadata from the local Codex app server; it does not query provider quota or rate-limit endpoints. Claude Code exposes its subscription model aliases and reasoning efforts, while Cursor reads the models available to the signed-in account from cursor-agent --list-models. Users can change models for every provider; Claude Code and Codex also expose reasoning effort. A model or effort change starts a fresh chat; prior chats are kept in browser-local history and resume their persisted local agent session when reopened. The folder button browses directories on the machine running the companion and switches new chats to the selected workspace; reopening an old chat restores its saved workspace first. Per-turn token telemetry remains available where the provider reports it.

When chat is opened from a board or canvas, Nudge sends the active board id, title, source, view, canvas id, and lightweight counts with each prompt. The agent therefore has the right board context without asking the user to repeat it; board contents are still read through the authorized Nudge MCP tools.

Create tools return a versioned structuredContent payload in addition to their model-readable text result. create_board, create_card, and create_canvas include the created entity's id, title, board id, kind, and relative Nudge URL. Canvas node creation also points back to the selected canvas when canvasId was explicit. Nudge Agent chat renders these entities as deduplicated, clickable result cards; other MCP clients can consume the same nudge.tool-result/v1 contract without parsing assistant prose.

Self-hosted runner CI/CD

Automatic npm releases

.github/workflows/publish-mcp.yml releases @euqns/nudge-mcp from main using npm Trusted Publishing (OIDC). No npm login, OTP, or NPM_TOKEN is needed for each release. The npm trusted publisher must specify GitHub owner kubilayege, repository Nudge, workflow publish-mcp.yml, no environment, and allow direct npm publish.

After a push affecting mcp/, the workflow installs locked dependencies, tests the release tooling and MCP, and builds the package. semantic-release then considers only commits that changed mcp/:

| Commit message | Release | | --- | --- | | fix(mcp): ... or perf(runners): ... | Patch | | feat(mcp): ... | Minor | | feat(mcp)!: ... or a BREAKING CHANGE: footer | Major | | docs: ..., chore: ..., or no MCP changes | None |

The scope is descriptive; file changes determine whether a commit belongs to MCP. Use a Conventional Commit message for the squash-merge title as well. Release tooling changes also trigger the workflow, but do not themselves bump MCP. Any pending releasable MCP commits will still be included.

The release bumps package.json, package-lock.json, and src/version.ts together in the CI checkout, rebuilds, publishes to npm, and creates an mcp-vX.Y.Z Git tag and GitHub release. Version changes are not committed back to main; tags and npm are the release history. Main's checked-in version can therefore lag behind npm. The first run starts from the npm gitHead for the last manual release, 1.33.0, rather than replaying the full repository history as a new 1.0.0 release.

To preview: GitHub Actions → Publish MCP → Run workflow, select main, and leave Preview the next release without publishing checked. Uncheck it to release pending changes manually. A preview skips publishing and remote tag creation; only a real publish verifies that npm accepts the configured trusted publisher. Concurrent releases are serialized.

Release-tool tests can also run locally:

npm ci --prefix .github/release --ignore-scripts
npm test --prefix .github/release

Manually bump a development version

From either the repository root or the mcp/ directory, bump the MCP package when a local development version needs changing:

npm run mcp:bump -- patch
# also accepts: minor, major, or an explicit version such as 0.15.0

This updates mcp/package.json, both root version fields in mcp/package-lock.json, and mcp/src/version.ts. It does not commit, tag, or publish the package.

Runner tasks start directly in one writable implementation turn. Claude Code uses Opus/high, Codex uses GPT-6 Astra/high, and Cursor uses auto. The former planning, review, and up-front human-interference turns are not part of the task pipeline. Intake rewrites and comment questions remain separate read-only flows.

A runner works one job at a time by default. Start it with --concurrency N (persisted by --save, capped at 8) to work up to N queued jobs at once — each job is its own agent conversation, so queueing several cards behaves like opening several chats. All of them share the runner's checkout: parallel coding turns can mix uncommitted changes across tasks, so prefer tasks that touch different areas, or run one clone per runner (--instance) instead.

One machine, one entry

The runner mints a machine id on first launch and keeps it in ~/.nudge/machine.json, independent of --save. Restarting, rebooting, or re-running the setup command therefore re-registers the SAME machine rather than adding another row to the board's runner list — the board just shows it going online and offline. --instance <name> is the deliberate exception: it registers as a second machine, for running two processes from one box.

While it runs the process heartbeats once a minute so the board can show it as online, and reports offline on Ctrl-C. Startup probes each agent CLI (codex, claude, cursor-agent) for version, sign-in state, and available models, and sends the result with the registration — that is what the board's runner settings render. Installed or signed in to an agent after startup? Press Reload there and the running process re-probes and re-reports without a restart.

Globally installed, supervised runners check npm's latest release on startup and every five minutes. When a newer stable version exists, the runner stops claiming jobs, finishes active work, updates its own npm prefix, and restarts. Failed checks or installs are logged and retried at the next five-minute check. Set NUDGE_RUNNER_AUTO_UPDATE=0 in the runner's environment to disable release checks. Local checkouts and npx installs are not automatically upgraded.

Runners sharing an npm prefix serialize updates with a process lock outside the package directory. The winner installs into a staging directory and checks the real CLI, its imports, package version and bundled Codex executable before activation. Waiting runners reuse that verified version. Failed installs leave the running package intact; failed activation restores the previous build. The previous package is retained beside the installation as .nudge-mcp-previous until the next successful activation. Restart watchers and supervisors wait for a complete, unlocked installation before loading it.

A running runner picks up new builds by itself. The process supervises a child that watches its own installed code; when dist/ changes (a rebuild, or an npm upgrade in place) it stops claiming jobs, lets in-flight jobs finish on the code they started with, then restarts and re-registers under the same machine id. Opt out with --no-auto-restart.

Always-on services: survive reboots, crashes and logouts

A runner or agent started from a terminal dies with that terminal, so a reboot takes the machine off the board until someone relaunches it. Install it as a per-user login service instead — the OS starts it at login and brings it back after a crash — and manage both kinds with the same verbs:

npm install -g @euqns/nudge-mcp          # a stable install path; npx's cache may be pruned
cd /path/to/checkout
nudge-mcp service install runner --convex-url https://<deployment>.convex.cloud \
  --token nrun_... --concurrency 8 --cwd . --save
nudge-mcp service install agent --no-open   # the Nudge Agent companion, same idea

nudge-mcp service status                 # both services: state, version, machine, board / pairing URL
nudge-mcp service status runner --all    # every installed runner instance
nudge-mcp service status agent --json    # machine-readable
nudge-mcp service start   runner|agent   # bring an installed service up now
nudge-mcp service stop    runner|agent   # stop it (it still returns at next login)
nudge-mcp service restart runner|agent   # e.g. after `npm i -g @euqns/nudge-mcp@latest`
nudge-mcp service logs    runner|agent [-n 100] [-f]
nudge-mcp service uninstall runner|agent # stop it and remove the service for good

Every verb takes --instance NAME for a second runner installed with --instance. Everything after install runner is passed to the runner unchanged; a relative --cwd is pinned to the directory you ran the command in. Add --dry-run to print the service file without installing.

install and start wait for the process to report in and then print what matters — the runner's registration (board, agents, checkout) or the agent's fresh pairing URL — so you never open a log to find out whether it worked. If it did not come up, the same command prints the exit code and the last log lines inline, followed by the exact command to run next. status does the same for each service:

runner  com.nudge.runner
  Service:   launchd · running (pid 4242) · ~/Library/LaunchAgents/com.nudge.runner.plist
  Process:   nudge-mcp 1.24.0 · pid 4243 · started 3m ago · managed by launchd
  Machine:   mac-mini · id 0123abcd…
  Board:     Feedback Loop (org/repo) · https://<deployment>.convex.cloud
  Checkout:  /path/to/checkout
  Agents:    codex, claude · up to 8 jobs at once
  Logs:      nudge-mcp service logs runner   (~/.nudge/logs/com.nudge.runner.log)

How it works: both processes write ~/.nudge/state/<label>.json at startup (version, pid, machine, board / listener + pairing URL). The CLI treats a report as current only when its pid is alive and it was written after the current boot, and install/start only accept a report written after the command began — which is what keeps a stale pairing URL from ever being shown. The agent service also keeps ~/.nudge/pairings/<label>.json (token + port, mode 0600); a fresh process reads it before listening so the URL stays valid across restarts. A pairing file that is malformed, a symlink, or readable by anyone else is ignored and replaced by a fresh token. Service files carry only PATH, HOME, locale and stable NUDGE_* settings — never the pairing token, the runner-job id or the supervisor marker.

Platforms. macOS writes ~/Library/LaunchAgents/com.nudge.<kind>.plist (mode 0600, since it can carry the token), loads it with launchctl, and logs to ~/.nudge/logs/. Linux writes ~/.config/systemd/user/nudge-<kind>.service, enables it with systemctl --user, and logs to the journal; run loginctl enable-linger $USER if it should start at boot before anyone logs in. Both run the same node binary and installed copy you invoked, with your current PATH baked in so git, gh, claude, codex, and cursor-agent resolve exactly as they do in your shell. Once a runner is a service, the auto-restart above still handles rebuilds; the service manager handles reboots and crashes. Windows has no user-level equivalent here yet: every lifecycle verb prints the Task Scheduler action to use instead, and status still reports a runner or agent started from a terminal.

Known limitations. stop keeps the service installed, so it comes back at the next login (use uninstall to remove it). launchd services live in the GUI login session: they start when you log in, not at boot, and a stop from one login session is undone by the next. The agent service keeps its pairing across restarts and reboots (see Nudge Agent chat); only if its saved port is taken by another process when it starts does it fall back to a free one, and paired browsers then need the URL service status agent prints. A companion started by hand mints a new pairing per run. A runner started with --no-auto-restart outside a service has nothing to respawn it, so Agent View does not offer it a remote restart.

Agent View. The web app's Agent View lists every registered machine with health (online / running N jobs / stopped cleanly / offline with no heartbeat), last heartbeat, nudge-mcp version, machine name and OS, board, checkout, and how it is kept alive (managed by launchd, managed by systemd, or terminal). Online machines get Reload (re-probe the agent CLIs) and, where the runner reported a supervisor or a service, Restart (drain in-flight jobs, then respawn on the new build). An offline machine cannot be reached from a browser, so its row shows the exact local recovery command to copy — nudge-mcp service start runner, or the relaunch-from-checkout command for a terminal runner. The same section shows this browser's paired Nudge Agent (connected / paired but unreachable / not paired) with its version, machine and workspace, and the recovery command when it is down.

Verify a reboot by hand. The install-time checks are automated; the reboot itself is not. Once per machine, after installing:

  1. nudge-mcp service status — both services running, the runner shows its board, the agent shows a pairing URL.
  2. kill -9 <runner pid> (the Process: pid) — within ~10 s status shows it running again with a new pid, and the board's machine row never went offline for longer than a heartbeat.
  3. Log out and back in (macOS) or loginctl terminate-user $USER and log in (Linux) — status shows both running.
  4. Reboot — after login, status shows both running; the runner is online in Agent View; the Nudge Agent row and chat in the already-paired browser go back to connected by themselves, and service status agent prints the SAME pairing URL as before (Pairing: … (persistent …)).
  5. nudge-mcp service stop runner then start runner — the board row goes "Stopped cleanly" and back to "Online".

Record the date and OS version alongside the board's runner settings when you have done this; it is the one check the package cannot run for you.

During the work turn, get_card, update_card, add_comment, and subtask completion use the job's board-scoped runner token. They are limited to the active task and its direct subtasks, so task management does not depend on a separate personal MCP login on the runner machine.

An @agent task created from the cloud-board composer is an intake job: it is queued with its frozen title, instructions, assignees, labels, priority, due date, and team before any card exists. The runner reads the linked checkout as context, then performs one rewrite-only turn that creates exactly one card with a polished title and structured Markdown description covering context, scope, acceptance criteria, and verification. Intake checkout access is read-only and its MCP surface allows only reads, create_card, and ask_human; it cannot implement, edit, run CI, complete, move, comment, create subtasks, or request review. Creation atomically writes the rich description, derived preview and checklist fields, frozen metadata, and the job attachment. A cardless intake cannot finish successfully.

Runner work turns expose two job-scoped GitHub Actions tools when the board has a linked repository: list_ci_workflows discovers its workflows and run_ci_workflow dispatches one with explicit workflow_dispatch inputs. The repository is fixed to the board setting, and the tools are absent during the read-only intake/question turns. The runner uses the machine's existing gh login; no GitHub credential is stored in Convex.

By default, a dispatched workflow stays attached until GitHub reports its terminal result. A successful workflow that fulfills the card is treated as completed work: the agent sends the card to Review and finishes instead of asking the requester to verify the run separately.

By default a dispatch waits for completion. Queue, running, and conclusion updates stream back to the card as a pipeline rail with the GitHub run link. Only input names are persisted; input values remain local to the gh process. Install and authenticate GitHub CLI on the runner machine before using it:

gh auth login -h github.com

Nudge MCP actions are pre-approved inside the companion because the SDK runs Codex headlessly and cannot display or answer a second approval prompt. The browser pairing and the user's revocable personal Nudge token authorize the server; Convex still applies the same board membership, role, validation, and rate-limit checks as the web app to every call.

Useful options:

nudge-mcp codex --cwd /path/to/project
nudge-mcp codex --app-url http://localhost:5173
nudge-mcp codex --port 47653 --no-open

For local development from this repository:

npm --prefix mcp run dev -- codex \
  --app-url http://localhost:5173 \
  --cwd ..

Install

TL;DR

If you already have Node.js ≥ 18 on your machine:

npx -y @euqns/nudge-mcp setup

Then paste the snippet it prints into your MCP client (see Configure). Done.

If node -v errors or prints something older than v18, follow the section for your OS below first.


Step 1 — Install Node.js

npx and npm ship with Node.js, so this is the only prerequisite. Pick one option per OS.

macOS

Option A — nvm (curl, no admin, lets you switch Node versions later):

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
# close and reopen your terminal (or: source ~/.zshrc)
nvm install --lts

Option B — Homebrew:

# install brew first if you don't have it
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install node

Option C — official installer: download the .pkg from https://nodejs.org (LTS), double-click, follow the prompts.

Windows

Option A — winget (built into Windows 10/11, open PowerShell):

winget install OpenJS.NodeJS.LTS

Option B — official installer: download the .msi from https://nodejs.org (LTS) and run it. Leave "Add to PATH" checked.

Option C — nvm-windows (multi-version management): grab the latest nvm-setup.exe from https://github.com/coreybutler/nvm-windows/releases, install, then:

nvm install lts
nvm use lts

After install, close and reopen PowerShell/CMD so node and npx are on PATH.

Linux

Option A — nvm (curl, recommended):

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
source ~/.bashrc   # or ~/.zshrc
nvm install --lts

Option B — distro package manager (versions vary, confirm node -v ≥ 18 afterwards):

# Debian / Ubuntu
sudo apt install -y nodejs npm
# Fedora / RHEL
sudo dnf install -y nodejs
# Arch
sudo pacman -S nodejs npm

If apt gives you something older than 18, use NodeSource:

curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs

Step 2 — Verify

node -v    # v18.x or higher
npx -v     # any version number

If either command is "not found", restart your terminal first. On Windows, sign out and back in if PATH still doesn't pick up.


Step 3 — Run setup

npx -y @euqns/nudge-mcp setup

First run downloads the package (~5 seconds, one-time), spins up a localhost listener, and opens your browser to /mcp-setup. Sign in with Google, click Authorize, and the CLI writes ~/.nudge/credentials and prints the JSON snippet for your MCP client.

Corporate / locked-down machines: if npx can't write to its cache or hits a proxy, set npm config set cache "$HOME/.npm-cache" and try again. If your network blocks the public npm registry, you'll need an internal mirror or the standalone-binary route (open an issue).


Local development of this repo

Skip the npm package and run from source:

cd mcp
npm install
npm run build
node dist/index.js setup   # uses the default app URL

Configure

After setup succeeds, copy this into your MCP client. The token lives in ~/.nudge/credentials, so the client config is the same on every machine and contains no secrets:

Claude Desktop

~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "nudge": {
      "command": "npx",
      "args": ["-y", "@euqns/nudge-mcp"]
    }
  }
}

Restart Claude Desktop. The Nudge tools appear under the tools menu.

Claude Code

claude mcp add nudge npx -y @euqns/nudge-mcp

Cursor

.cursor/mcp.json:

{
  "mcpServers": {
    "nudge": {
      "command": "npx",
      "args": ["-y", "@euqns/nudge-mcp"]
    }
  }
}

Overrides

If you need to point at a non-production deployment (PR preview, local dev against convex dev), setup accepts --app-url:

npx -y @euqns/nudge-mcp setup --app-url http://localhost:5173

You can also bypass the credentials file entirely by setting NUDGE_CONVEX_URL and NUDGE_TOKEN in the MCP client's env block — useful for CI or containers.

Tools

| Tool | What it does | |---|---| | whoami | Identity bound to the token (id, email, displayName, team). | | list_boards | Every board the user is a member of. | | get_board | Board layout: lists, labels, members, your role. | | create_board | New board (caller becomes owner, default Backlog/In progress/Review/Done columns). | | list_lists / create_list | Read or add columns. | | list_cards | All cards on a board (subtasks hidden by default). | | my_cards | All cards across all boards where the caller is assigned, soonest due first. | | get_card | One card incl. its description doc. | | create_card | New card in a list (pass parentCardId for real, independently tracked subtasks). | | update_card | Patch title/icon/dueDate/assigneeIds/labelIds/priority/listId/description. Description accepts Markdown and becomes native rich editor content; pass 'none' to clear dueDate/priority. | | move_card | Drop into another list. Auto-completes if landing in a Done list. | | set_card_completed | Flip the complete bit explicitly. | | delete_card | Hard-delete with cascade (subtasks, comments, activity, notifications). | | create_label | Board-scoped label. | | list_comments / add_comment | Comment threads on a card. @username mentions notify. | | list_members | Board members (use the returned ids for assigneeIds). | | find_user_by_email | Resolve an email to a user id — handy before assigning. | | my_notifications | Recent notifications (assigned / mentioned / commented / moved / updated). | | update_runners | Update @euqns/nudge-mcp on every active self-hosted runner and restart them gracefully. plan (default) previews; apply needs confirm: true; status re-reads a run. Runners finish in-flight jobs first; the result lists per-runner success/failure and which interrupted jobs must be rerun. Locally it also updates this server's own global install. |

Boundary notes

  • The server runs every call as the user the token belongs to — board access is checked against memberships, the same table the web app reads. There is no "elevated" mode.
  • Convex actions run on the same backend as the app, so MCP-driven changes appear in real time in any open browser tab.
  • The token is sent as a regular argument over the wire (HTTPS to Convex). Anyone reading the JSON payload would see it, which is why we hash on the server and only ever return the plaintext at creation time.

Development

npm run dev   # tsx watcher — useful with `node --inspect` on the MCP host
npm run build # tsc to dist/

The server doesn't reload tools at runtime; restart the MCP client when you change tool definitions in src/index.ts.

Local Codex runner configuration

The runner regenerates ~/.nudge/codex-home/config.toml for each job. Put machine-owner TOML overrides in ~/.nudge/codex-overrides.toml instead; they are appended after the generated model defaults. This can explicitly trust a project so Codex loads its .codex/config.toml and project MCP servers:

[projects."/absolute/path/to/project"]
trust_level = "trusted"

Project workspace networking can be configured with [sandbox_workspace_write] network_access = true in that project's config. The runner still sets the job sandbox mode and approval policy explicitly. Overrides are read at each job startup; already-running turns retain their existing settings. Do not repeat generated top-level model/auth keys in the TOML overrides. Desktop MCP servers remain isolated unless explicitly enabled.

T3 Code desktop runner threads

When T3 Code is running on the runner's machine, new Codex jobs on macOS and Linux execute through T3 itself. Look for Nudge · running · Board · task … under the matching checkout's project. Open the thread to see the prompt, streaming responses, and tool activity in the conversation pane. Nudge receives the final answer and token usage, and updates the title with the job status.

Nudge resumes and steers these jobs in the same T3 conversation. Cancelling or steering a job in Nudge interrupts its T3 turn. Keep using Nudge for job questions, review, and scheduling; T3's composer also operates the underlying conversation, but does not create or reopen a Nudge job. Completed conversations remain readable in T3. Their dedicated Nudge provider is removed after completion so it disappears from both the model picker and Providers settings; resume the job through Nudge to recreate it automatically. Cleanup also removes previously disabled providers for completed jobs. Active jobs and jobs waiting for human input keep their provider.

The Codex bridge preserves the runner's Nudge MCP tools and sandbox: intake and question turns stay read-only, ordinary tasks use workspace-write, and jobs that explicitly need repository/network actions retain their existing access. T3 gets a separate Codex provider instance per conversation using Nudge's isolated Codex home. Job credentials live in an owner-only context under ~/.nudge/t3-native, are removed when execution ends, and are never put in T3 titles or prompts.

Claude, Cursor, Windows, and conversations originally started by an external CLI continue using the terminal log view. Their previous conversation IDs are kept with the original engine. Existing log-only threads and jobs already running are not converted or backfilled.

Discovery reads ~/.t3/userdata/server-runtime.json (or the dev variant) and only connects over loopback. On macOS it finds T3 Code or T3 Code (Nightly) in /Applications or ~/Applications; other installs need a t3 executable on the runner's PATH. The installed CLI issues an expiring local session and revokes it afterward (24 hours maximum for native execution, five minutes for status updates). T3 API credentials are held in memory. Windows gets the fallback thread and status updates, with its log saved locally.

| Runner environment variable | Purpose | | --- | --- | | NUDGE_T3=0 | Disable automatic T3 threads. | | T3CODE_HOME=/path/to/.t3 | Find a nonstandard T3 data directory. | | NUDGE_T3_CLI=/path/to/t3 | Use a specific installed T3 CLI or executable wrapper. |

T3 must be open before the job starts. Missing T3 is silently skipped for new jobs. A native conversation must resume in its original T3 environment; if that environment is unavailable, the job reports an error. Once a native turn has been submitted, the runner never falls back to a second CLI agent. Verified against T3 Code Nightly 0.0.41-nightly.20260908.1400; native execution requires its HTTP orchestration and provider settings RPC interfaces. Fallback JSONL logs are owner-only under ~/.nudge/t3-logs and retain up to 8 MiB of live output per run, followed by its final result. They remain available after completion and can be removed when no longer needed.

Runner output files

A coding turn can optionally write the JSON manifest at the absolute path supplied in its prompt (also NUDGE_RUNNER_ARTIFACT_MANIFEST). It is an array of { "path": "report.md", "contentType": "text/markdown", "name": "report.md" }. Paths are absolute or relative to the checkout; name defaults to the basename. Only explicitly listed files are uploaded. Engine integrations may instead return EngineResult.artifacts with the same descriptors. No manifest means no uploads.

The runner uploads before runners:finish using uploads:getRunnerArtifactUploadUrl, a direct signed S3 POST, then uploads:confirmRunnerArtifactUpload. Both actions require the runner token, runner ID and job ID; confirmation also requires the returned upload ID. The server checks token ownership, board, active lease and job attempt, verifies the CDN object, and persists { name, contentType, size, url, key } in job artifacts. Card job history and job details show Output files with names, types and download links. Text (including JSON, Markdown, CSV and HTML source), images and videos open in an in-app preview popup. Video uses native browser controls; codec support depends on the browser. Other types remain download-only.

Limits: 20 files per job across attempts, 10 MiB per file (empty files allowed), 255 characters for names/types, and a 64 KiB manifest. Types are opaque metadata, including custom types and parameters; bytes are never converted. Files are served as attachments. Previews fetch the bytes and use local blob URLs for media, so attachment disposition does not force a download. The CDN must allow cross-origin GET requests from the app origin, as for Docs text previews. Text previews show at most the first 256 KiB with a truncation notice; HTML is displayed as text, never executed. Download always links to the original file. URLs use the existing public CloudFront attachment model: anyone with a URL can read it.

Confirmed files survive success, failure, cancellation and retries. A failed upload fails an otherwise successful run, retaining earlier confirmed files. Unconfirmed reservations expire after 15 minutes and their S3 objects are deleted; confirmed artifacts live as long as the job output. Deploy the updated Convex backend before updating runners; existing S3/CloudFront configuration is reused.

Canvas HTML and live editor embeds

create_html_node and update_html_node accept self-contained HTML/CSS/JS with inline assets (up to the existing ~900KB limit). They do not install Canva or another hosted level editor. URL-only input, nested iframe/object embeds, external resource attributes, CSS imports/URLs and common network APIs are rejected before saving. Failed creation adds no Canvas node; failed updates preserve the previous HTML. There is no network permission or provider override.

For example, html: "https://www.canva.com/design/ID/TOKEN/view", the tokenless form, malformed-token URLs, an iframe wrapper, a CDN script, and a localhost fetch all produce an unsupported-content error. Supply an offline editor with inline scripts, styles and data instead. Ordinary outbound links can still open in a separate tab; that is navigation, not a live Canvas embed.

Input checks provide diagnostics, not a JavaScript security proof. The actual preview uses an opaque iframe (no same-origin, forms or top-navigation grants) and an injected CSP before authored content: no network connections, external scripts/styles, nested frames, objects, base URLs or form submissions. Inline scripts/styles and data/blob images/media remain available. Existing unsupported HTML shows an explanation in Canvas. Dependencies constructed dynamically are blocked by CSP and display a warning in the preview. A returned node id confirms saved content, not successful execution of arbitrary JavaScript. Verify the preview before reporting a working editor; documentation alone is not installation.

Canva rich document embeds are a separate, provider-validated preview flow. They canonicalize supported HTTPS Canva design links to /view?embed, preserve share tokens, and use Canva's provider-specific sandbox. This does not authorize network access inside Canvas HTML nodes or promise live editing. Tokenless public links remain parseable; missing/expired share access can still be refused by Canva. Syntax validation cannot verify a token's validity or the viewer's access. Malformed paths, credentials, non-HTTPS URLs and unexpected ports are rejected.