You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add upstash setup to connect AI agents to Upstash - #23
Local plugin in ~/.cursor/plugins/local/upstash (no plugin CLI, and not in the Cursor marketplace yet)
VS Code, Copilot CLI, OpenCode
Remote MCP (https://mcp.upstash.com/mcp) merged into the agent's config + the combined upstash skill
Plugin agents fall back to MCP + skill when the plugin can't be installed (CLI not on PATH, install error), --project is used for a user-only plugin, or --auth api-key is set (the plugins are OAuth-only). Reruns refresh the marketplace and update the plugin.
Flags
--claude --codex --cursor --gemini --vscode --copilot --opencode, --mode auto|plugin|mcp, --auth oauth|api-key (api-key uses the existing resolveAuth: flags, env, or upstash login; header is Authorization: Bearer email:key), -p/--project, -y/--yes, --ref, --dry-run, --json. With no agent flags it prompts (TTY) or uses detected agents (non-TTY / -y).
Differences from ctx7 setup
ctx7 never installs plugins; this does, since upstash/skills already ships Claude/Codex/Cursor/Gemini manifests that bundle the MCP.
No rules files — the combined skill already tells the agent when to prefer MCP tools.
No new dependencies (the CLI only has commander + dotenv). Skills come from one codeload.github.com tarball download, parsed with a ~60-line tar reader; no GitHub API rate limits.
Warns when a manual upstash MCP entry and the plugin would both load the server.
End-to-end in a sandbox with the real claude 2.1.263 and codex 0.153.4 CLIs: plugin installs are idempotent and show up in claude plugin list / ~/.codex/config.toml; TOML and JSON merges keep other servers; project scope writes .claude/settings.json with the plugin enabled.
Not tested: Gemini CLI install (not in the sandbox) and Cursor actually loading the local plugin — worth a manual check on a Mac before merging.
Follow-ups
Switch Cursor to the marketplace install once the listing is accepted.
Document upstash setup in the upstash-cli skill and the docs "Install by agent" page.
upstash setup --remove, and an API-key option for plugins (e.g. a headersHelper like context7's Claude plugin).
Installs the upstash/skills plugin (MCP server + skills) through each agent's
own CLI where plugins are supported (Claude Code, Codex, Gemini CLI), as a
local plugin for Cursor, and otherwise writes the remote MCP server into the
agent's config and installs the combined `upstash` skill (VS Code, Copilot
CLI, OpenCode). Falls back to MCP + skill when a plugin can't be installed.
Supports --project, --mode auto|plugin|mcp, --auth oauth|api-key, --dry-run,
--json, and agent detection. No new dependencies: skills come from the
upstash/skills tarball, parsed with a small tar reader.
Replaces the numbered readline prompt with an arrow-key flow when run in a
terminal: pick scope, multi-select agents (detected ones pre-checked,
already-connected ones marked), pick OAuth or API key (prompting for
credentials when none are saved), confirm a plan, then a spinner per agent
with its steps. Flags still answer questions up front; -y, --json and
non-TTY runs keep the existing plain output unchanged.
@clack/prompts is pinned to ~1.0.1, the last line that runs on Node 18.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWUubWwYWp1dZTK1S9Ek95
Every agent entry and plugin installer now cites the documentation page its
config paths, entry shape and CLI commands come from. Where the docs are
silent (where .claude.json lives under CLAUDE_CONFIG_DIR, the dotted
OpenCode file names) the comment says so and what the code relies on
instead. Comments only; no behavior change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWUubWwYWp1dZTK1S9Ek95
Drop --auth api-key and the API-key prompts. Setup now never writes a
credential: every agent gets the bare server entry and signs in with OAuth
on first use. Rerunning setup replaces an `upstash` entry left by an older
API-key setup, so its header is removed too.
Also fixes the other review findings:
- Only a missing config file reads as empty; other read errors (EACCES,
EISDIR) now stop the write instead of overwriting the file from {}.
- JSONC configs with trailing commas parse.
- TOML table matching resolves quoted keys, so [mcp_servers."upstash"] is
replaced instead of declared a second time.
- Skill/plugin trees are validated and written to a staging directory
before the old install is removed.
- --ref with a non-default branch uses MCP + skill for Claude Code and
Codex, whose plugin installs cannot pin a ref, instead of silently
installing the default branch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWUubWwYWp1dZTK1S9Ek95
Handle inline and dotted MCP tables before appending TOML sections
src/setup/mcp-config.ts:167
This only detects explicit table headers. A valid Codex config can instead contain mcp_servers.upstash = { url = "old" }; appending [mcp_servers.upstash] then declares the same table twice and makes the entire TOML config invalid. Handle dotted assignments/inline tables before appending, or use a TOML parser for the merge.
- Remove --ref. Everything installs from the default branch of
upstash/skills, so no user-supplied string reaches an agent CLI (Gemini's
install runs through a shell on Windows) or the download URL.
- writeTree moves the previous install to a backup before renaming the
staged tree into place, and restores it if that rename fails.
- Codex config: refuse to append [mcp_servers.upstash] when the server is
already defined inline or with dotted keys (mcp_servers.upstash = {...},
or upstash = {...} under [mcp_servers]), which would declare the table
twice and break the file. The step fails with a message saying how to fix
the file, and hasMcpEntry recognises those forms too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWUubWwYWp1dZTK1S9Ek95
Resolves the package.json conflict by keeping both dependency sets:
@clack/prompts and picocolors from this branch, @upstash/blob and mime from
main. package-lock.json is main's lockfile with those two packages added by
npm install.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWUubWwYWp1dZTK1S9Ek95
The reason will be displayed to describe this comment to others. Learn more.
Reviewed revision d500d1d. Five P2 findings are attached inline.
Validation: all 134 unit tests, build, and typecheck passed locally. The TOML merge failures and ignored Claude update failure were reproduced separately. Live agent installation was not exercised.
The reason will be displayed to describe this comment to others. Learn more.
[P2] Detect inline parent tables before appending
A valid Codex config such as mcp_servers = { other = { url = 'https://example.com/mcp' } } passes this guard because the key is an ancestor of mcp_servers.upstash. Setup then appends [mcp_servers.upstash], which illegally extends an inline TOML table. I reproduced writeMcpEntry returning success while Python's tomllib rejects the resulting file with Cannot declare ('mcp_servers', 'upstash') twice. Detect inline ancestors and refuse the write, or merge them safely.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 0dda809. The TOML edit is still line-based, so comments and formatting are kept, but it now checks its own result: it parses the new file with smol-toml and compares it with the expected document before writing. Your mcp_servers = { other = {...} } case, and any other layout the line edit can't rewrite safely (the server defined inline or with dotted keys), now fails the step with a clear message and leaves the file untouched. This also replaces the hand-written inline/dotted detection. Test: "refuses layouts it cannot rewrite safely".
The reason will be displayed to describe this comment to others. Learn more.
[P2] Ignore table-like text inside multiline strings
A [mcp_servers.upstash] line inside a valid multiline developer_instructions string matches this search. Replacement removes the remaining string content, including its closing delimiter, up to the next unrelated table header; the resulting config fails to parse with an unterminated-string error. I reproduced this starting with valid TOML. The multiline handling in definedOutsideTable also needs to apply to header discovery and replacement boundaries.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 0dda809. Header detection now skips lines inside """ / ''' strings, so a [mcp_servers.upstash] line in developer_instructions is left alone and the real table is appended. If anything still changed a string, the parse-and-compare check would refuse the edit. Test: "ignores table-like lines inside multi-line strings".
The reason will be displayed to describe this comment to others. Learn more.
[P2] Remove Upstash subtables regardless of their position
Replacement stops at the next unrelated table. In a valid config ordered as [mcp_servers.upstash], [mcp_servers.other], then [mcp_servers.upstash.http_headers], the old Authorization header survives the migration to OAuth. I reproduced the resulting Upstash entry retaining Authorization = 'Bearer expired' after setup replaces its URL. Remove all matching descendant sections throughout the document, rather than only contiguous ones.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 0dda809. Every [mcp_servers.upstash] and [mcp_servers.upstash.*] section is now removed wherever it sits, and the new block goes where the first one was. With your ordering (upstash, other, upstash.http_headers), the old Authorization header is gone afterwards. Test: "removes upstash sub-tables wherever they sit, and keeps TOML dates".
The reason will be displayed to describe this comment to others. Learn more.
[P2] Honor CODEX_HOME for user configuration
The MCP installer always writes ~/.codex/config.toml, even when Codex uses a custom CODEX_HOME. Thus setup --codex --mode mcp (or the automatic MCP fallback) reports success while writing a config the active agent never reads. Detection and isPluginInstalled have the same assumption. Resolve these user-level paths from CODEX_HOME, which defaults to ~/.codex, as described in the official configuration documentation.
The reason will be displayed to describe this comment to others. Learn more.
[P2] Report failed Claude plugin updates
The update result is discarded. On a rerun, plugin install can succeed without upgrading the existing plugin; if the subsequent update fails, setup still reports ok: true with every step done and no warning. I reproduced this with a runner returning success for marketplace/install commands and failure for plugin update. Record and report the update failure instead of returning the earlier install result.
The reason will be displayed to describe this comment to others. Learn more.
Fixed in 0dda809. The update result is no longer discarded: a failed claude plugin update shows up as a failed "Update upstash@upstash" step with the CLI's error, and setup exits non-zero. The plugin is still installed, so setup doesn't fall back to MCP + skill, which would load the server twice. Test: "reports a failed Claude plugin update instead of claiming success".
…t failed Claude updates
Addresses the review on d500d1d:
- The Codex config is still edited line by line to keep comments and
formatting, but the result is now parsed with smol-toml and compared with
the expected document before it is written. Layouts the line edit can't
rewrite safely, such as an inline parent table
(mcp_servers = { other = {...} }) or the server defined inline or with
dotted keys, now fail the step with a clear message instead of producing
an invalid file. This replaces the hand-written inline/dotted detection.
- Header lines inside multi-line strings are no longer taken for tables.
- Every [mcp_servers.upstash] and [mcp_servers.upstash.*] section is removed
wherever it sits, so a non-adjacent http_headers sub-table from an older
API-key setup no longer survives.
- hasMcpEntry reads TOML configs with the parser too.
- Codex user config, detection and the plugin check use CODEX_HOME,
defaulting to ~/.codex.
- A failed `claude plugin update` is reported as a failed step and a
non-zero exit instead of a silent success; the plugin stays installed, so
there is no MCP fallback.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LWUubWwYWp1dZTK1S9Ek95
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Upstash's answer to
npx ctx7 setup: one command that wires the Upstash MCP server and skills into the user's agents.What it installs
claude plugin marketplace add upstash/skills+claude plugin install upstash@upstash(user or project scope)codex plugin marketplace add upstash/skills+codex plugin add upstash@upstashgemini extensions install https://github.com/upstash/skills --consent~/.cursor/plugins/local/upstash(no plugin CLI, and not in the Cursor marketplace yet)https://mcp.upstash.com/mcp) merged into the agent's config + the combinedupstashskillPlugin agents fall back to MCP + skill when the plugin can't be installed (CLI not on
PATH, install error),--projectis used for a user-only plugin, or--auth api-keyis set (the plugins are OAuth-only). Reruns refresh the marketplace and update the plugin.Flags
--claude --codex --cursor --gemini --vscode --copilot --opencode,--mode auto|plugin|mcp,--auth oauth|api-key(api-key uses the existingresolveAuth: flags, env, orupstash login; header isAuthorization: Bearer email:key),-p/--project,-y/--yes,--ref,--dry-run,--json. With no agent flags it prompts (TTY) or uses detected agents (non-TTY /-y).Differences from ctx7 setup
codeload.github.comtarball download, parsed with a ~60-line tar reader; no GitHub API rate limits.upstashMCP entry and the plugin would both load the server.Tested
tests/unit/setup.test.ts): synthetic tarball, fake agent-CLI runner, tempHOME.claude2.1.263 andcodex0.153.4 CLIs: plugin installs are idempotent and show up inclaude plugin list/~/.codex/config.toml; TOML and JSON merges keep other servers; project scope writes.claude/settings.jsonwith the plugin enabled.Follow-ups
upstash setupin theupstash-cliskill and the docs "Install by agent" page.upstash setup --remove, and an API-key option for plugins (e.g. aheadersHelperlike context7's Claude plugin).