claude-openmax
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@claude-openmaxcreate a task 'update docs' in workspace"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
claude-openmax
The Claude Code runtime adapter for OpenMax / CWS. A thin Category-B
(bare runtime) adapter: it owns none of the CWS protocol — that all comes from
@openmaxai/openmax-agent-sdk (CwsAgentBridge) — and does
only the two runtime-specific translations plus capability exposure:
Inbound — bring a workspace message into Claude Code's visible context via an experimental
claude/channelMCP push (raft-channel-wake.v1).Outbound — send Claude's reply back to cws-core via the SDK's
CommService.Capability exposure — the SDK's six service clients (
tm/kb/as/comm/core/conn) as MCP tools, so the agent can operate the workspace (create issues/tasks, query the KB, upload files, reply).
It follows the proven reference implementation,
raft-external-agents v0.3.1
— the only one of the four external runtimes with a shipped Claude Code plugin —
and the claude-openmax adapter design.
Architecture
Layer 1 @openmaxai/openmax-agent-sdk (CWS HTTP/WS contract only)
CwsAgentBridge: per-org WS lifecycle · auth/heartbeat/reconnect · atomic dedupe
· /sync + inbox-ledger · frame dispatch · access-policy · normalized InboundMessage
services: tm / kb / as / comm / core / conn (one CwsHttpClient)
providers: StorageProvider · RuntimeStateProvider · InboundDelivery(★) · Logger
▲ import + inject
Layer 2 claude-openmax (this repo)
┌ bridge host (Node) ────────────────┐ ┌ Claude Code (agent) ──────────┐
│ new CwsAgentBridge({providers,cbs}) │ │ MCP `openmax` server: │
│ providers.inbound.deliver ─────────┼─wake──▶│ experimental claude/channel │
│ = derive WakeRequest → push │ │ → pushes notice into context │
│ storage=local data dir · logger │ │ MCP tools: tm kb as comm core │
│ holds 6 SDK service clients ◀──────┼─call───┤ conn + comm_send │
└─────────────────────────────────────┘ └───────────────────────────────┘
│ CommService.send() / bridge.send() → cws-core
▼
cws-core REST ◀── cws-comm WS (inbound frames) ── COCO Workspace (user)Topologies
In-process (MVP, default) — one Node process is both the stdio MCP server (
claude/channel+ tools) and the host ofCwsAgentBridge.InboundDeliverypushes wakes straight to the channel; the/wakeHTTP hop is skipped but theWakeRequestwire shape is preserved. Run: Claude Code loads the plugin.Split (design topology 1,
CLAUDE_OPENMAX_MODE=channel-only) — the MCP plugin runs only the channel + an HTTPPOST /wakeserver; a separate residentbridge.jsholds the WS and POSTs wakes. The bridge survives Claude Code session restarts and redelivers via the SDK's/sync+ inbox-ledger.
The ok:true delivery invariant
The single most important rule (from the SDK's wake-result schema and
CwsAgentBridge): ok:true MUST mean the message genuinely entered the
runtime's visible context. On ok:true the SDK commits dedupe + ledger +
read markers and stops /sync retry for that message — so a false ok:true
loses the message forever.
This adapter returns ok:true only when the wake injection resolved
(ClaudeChannel.notifyWake / POST /wake succeeded). Anything else —
channel not connected, notification write failed, malformed inbound — returns
{ok:false, failureClass, retryAfterMs}, so the SDK holds all markers and
redelivers on the next /sync sweep. See src/inbound-delivery.js and its
tests.
Files
File | Purpose |
| MCP channel plugin entrypoint (Claude Code loads this over stdio); default in-process bridge host. |
| Standalone resident bridge for the split topology; POSTs wakes over HTTP |
| MCP |
| Pure |
|
|
| Debounced wake coalescing (raft EAB-8): leading-edge inject + window merge. |
| HTTP |
| Wraps the six SDK service clients as MCP tools (one dispatch tool per service + |
| Loads adapter config; builds |
| Assembles |
| File-backed |
| stderr logger + empty |
| Registers the |
|
|
| Agent-facing instructions: how wakes arrive, how to read/reply, tool map. |
|
|
Session / context management
Uses Claude Code's built-in autocompact (and /clear / /compact). This
adapter implements no extra compression logic — by design.
Installation
Install as a Claude Code plugin from this repo's marketplace:
claude plugin marketplace add openmaxai/claude-openmax # register the marketplace (once)
claude plugin install openmax-channel@openmax # install the pluginClaude Code fetches the plugin, installs it into its managed plugin directory,
and keeps it updated — no manual git clone needed. Then create your config
(see Configuration) and start Claude Code.
Experimental wake — one caveat. The MCP tools (
tm/kb/as/comm/core/conn+comm_send) work from a plain plugin install with no flags. The inbound wake (claude/channel, how workspace messages reach the agent) is still an experimental Claude Code capability and currently requires launching with the development-channels flag:claude --dangerously-load-development-channels plugin:openmax-channel@openmaxReference the plugin, not a bare server name. When installed via
claude plugin install, Claude Code namespaces this MCP server —/mcpshows it asplugin:openmax-channel:openmax, notopenmax. So the bare--dangerously-load-development-channels server:openmaxdoes not match a plugin install (it only works if you register the server directly in a.mcp.jsonunder the literal nameopenmax); use theplugin:…@…form above. If in doubt, run/mcpand reference the exact server name it prints. Onceclaude/channelgraduates from experimental, install alone will be enough.
Running (from source / dev)
npm install # resolves @openmaxai/openmax-agent-sdk@alpha from npm
cp config.example.json ~/.config/claude-openmax/config.json # fill in real values
npm test # node --testLoad into Claude Code as a plugin (dev):
claude plugin marketplace add --scope local /path/to/claude-openmax # local checkout
# or point Claude Code at .claude-plugin/plugin.json directlyBuild (maintainers)
The plugin ships a dependency-free bundle. Claude Code installs a marketplace
plugin by cloning the repo and does not run npm install, so the MCP server
must run with zero node_modules. scripts/build.js (esbuild) inlines every
dependency into dist/index.mjs (the MCP server, referenced by
.claude-plugin/plugin.json) and dist/bridge.mjs (the split-topology bridge).
npm run build # rebuild dist/ after changing src/ or bumping a dependencydist/ is committed (it is the shipped artifact); CI rebuilds it and fails if the
committed bundle is stale, and smoke-tests that it loads with no node_modules.
Split topology (resident bridge + channel-only plugin):
# terminal A: Claude Code loads the plugin with
CLAUDE_OPENMAX_MODE=channel-only CLAUDE_OPENMAX_WAKE_PORT=47600 CLAUDE_OPENMAX_WAKE_TOKEN=... claude ...
# terminal B: resident bridge (config.wake.endpoint = http://127.0.0.1:47600/wake)
CLAUDE_OPENMAX_WAKE_TOKEN=... node src/bridge.jsConfig / env
Config file at $CLAUDE_OPENMAX_CONFIG (or ~/.config/claude-openmax/config.json);
see config.example.json. As of the config-parity refactor the on-disk shape is a
1:1 structural mirror of the OpenMax (zylos-openmax) component's config — see
the migration note below. The shape:
enabled?: bool
server: { bff_url, ws_url, frontend_base_path } // frontend_base_path default "/workspace"
agent: { identity_id, api_key, device_id, app_version }
cf_access: { client_id, client_secret }
orgs: { "<org_id>": { enabled?, org_id, org_name?,
owner: { member_id, name },
self: { member_id, name, display_name },
access:{ dmPolicy, dmAllowFrom?, groupPolicy?, groups?:{ "<convId>": { mode, allowFrom } } } } }
wake: { endpoint } // claude-openmax ONLY (openmax has no wake)
metricsReport?: { dashboardApiKey } // RESERVED / forward-compat — inert (no reporter yet)
ws?: { reconnectMaxMs?, heartbeatIntervalMs?, pingIntervalMs? } // claude-openmax WS tuning knobsEnv fallbacks (map onto the nested fields): COCO_API_URL→server.bff_url,
COCO_WS_URL→server.ws_url, COCO_FRONTEND_BASE_PATH→server.frontend_base_path,
COCO_API_KEY→agent.api_key, COCO_DEVICE_ID→agent.device_id,
COCO_CLIENT_VERSION→agent.app_version, COCO_ORG_ID→default org. Other knobs:
CLAUDE_OPENMAX_DATA_DIR, CLAUDE_OPENMAX_MODE, CLAUDE_OPENMAX_DEBOUNCE_MS,
CLAUDE_OPENMAX_CONTENT_FREE, CLAUDE_OPENMAX_WAKE_{HOST,PORT,TOKEN}.
orgs is keyed by org_id (openmax-style), end to end: the SDK orchestrator
keys its per-org runtime records by org_id too, so the adapter hands it an
org_id-keyed map directly — there is no separate per-org key to derive. Every
self-healing write-back (self.member_id, self.name, owner bind) resolves the
org by org_id and lands back in the org_id-keyed on-disk structure.
agent.identity_id is the agent's global identity. Leave it empty and the
adapter resolves it from cws-core GET /me at startup and caches it back to
config.json. It is the leadAgentId for the guided-autonomy flow (an Issue's
Lead agent = the agent itself).
server.frontend_base_path is wired into the SDK's CwsHttpClient.frontendUrl()
so the agent can build clickable workspace links (<bff_url><frontend_base_path>/…,
default /workspace).
Migrating from the openmax (zylos-openmax) component
The claude-openmax config is now structurally identical to the openmax
component's config.json — you can drop an openmax config in as-is. The only
differences are additive and claude-openmax-specific:
wake.endpoint— required for the split-topology bridge; openmax has no wake block.metricsReport— accepted for parity but inert (claude-openmax has no metrics reporter yet); it round-trips untouched.ws— optional WS tuning knobs (reconnectMaxMs,heartbeatIntervalMs,pingIntervalMs) that openmax hardcodes;ws_url/device_id/app_versionlive underserver.*/agent.*, NOT here.
The old claude-openmax shape (top-level http/auth + an array orgs) is
still accepted: it is translated to the new shape on load with a one-time warning,
so an existing live config won't break — but you should migrate it.
Session files auto-migrate. Per-org state (incl. the /sync cursor) is now
keyed by org_id (sessions/<org_id>.json), where earlier builds used a derived
slug (sessions/<slug>.json). On first load, if only a legacy sessions/<slug>.json
exists (matching an explicit slug or slugify(org_name)), it is copied forward
to the org_id key so the cursor is preserved — no duplicate message delivery after
upgrade. The old file is left in place (harmless); no manual step is needed.
Each org also honors an enabled: false flag — such orgs are kept on disk but
not connected to (parity with the openmax component).
Verified vs. spike (honesty ledger)
Verified locally (node --test + MCP client smoke):
WakeRequest derivation/validation,
ok:truegating, coalescing, tool dispatch — 32 unit tests green.The MCP server boots, advertises
capabilities.experimental["claude/channel"], and lists all 7 tools to a real MCP client.POST /wake(token-guarded) →server.notification({method:"notifications/claude/channel", ...})is actually transmitted over the MCP transport and received by the connected client with correct content + routing meta, and the server returns{ok:true, runtimeSession}.server.notificationwith the customclaude/channelmethod does not throw on@modelcontextprotocol/sdk1.22+.
⚠️ SPIKE — requires a live Claude Code to confirm (biggest technical uncertainty):
claude/channelrendering. We proved the notification reaches an MCP client; we have not proved Claude Code (a) enables this experimental capability, (b) renders the pushed notice into the agent's visible context, and (c) does so promptly / can steer an in-progress turn. Until confirmed,ok:truemeans "notification written to the MCP transport", which is the strongest local signal but weaker than "the model has seen it". If Claude Code proves fire-and-forget here, we should fall back to a more conservativeok:truegate (and lean harder on/sync).runtimeSessionbinding. We mint a stable per-process id; the canonical value is Claude Code's real session id, which must be sourced from the live runtime.Session lifecycle / auth jitter under the in-process topology (WS drops when the Claude Code session exits) needs a live soak test; the split topology is the mitigation and also needs end-to-end restart verification.
Tool budget. We collapsed ~150 sub-commands into 6 dispatch tools +
comm_sendto stay within Claude Code's tool budget; the exact budget and whether dispatch-style tools are ergonomic for the model is unconfirmed.
Boundaries
Consumes the SDK for all protocol/transport/sync/dedup/access-policy logic; reimplements none of it. Does not pass a custom
callbacks.dedupe(uses the SDK's built-in atomic deduper).No dependency on
zylos-openmax; independent local data dir (no~/zylos).
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/openmaxai/claude-openmax'
If you have feedback or need assistance with the MCP directory API, please join our Discord server