Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 65 additions & 23 deletions install.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,42 +45,65 @@ Check if Docker is installed and running on the user's system.

### Option A: Docker (Preferred)

If Docker is available, **pull the image first** before configuring anything:
Hyperstack uses a **persistent container + `docker exec`** pattern. One long-lived container serves every CLI invocation and every session, so container startup cost is paid once — not on every `claude` run.

**Step 1 — Pull the image:**

```bash
docker pull ghcr.io/orkait/hyperstack:main
```

This is required. MCP servers have a short initialization timeout — if Docker pulls the image on first use during MCP startup, it will time out and report as failed. Pre-pulling ensures the image is cached and starts in milliseconds.
Pre-pulling is required. MCP servers have a short initialization timeout — if Docker pulls the image on first use it will time out and report as failed.

**Step 2 — Start the persistent container (one-time setup):**

```bash
docker run -d --name hyperstack-mcp --restart unless-stopped \
--memory=512m --cpus=1 \
--entrypoint sleep \
ghcr.io/orkait/hyperstack:main infinity
```

The container stays alive in the background with `sleep infinity` as PID 1. Each MCP session `exec`s a fresh `bun` process inside this container. `--restart unless-stopped` auto-starts the container after Docker restarts. `512m/1 cpu` covers several concurrent sessions.

Verify it's running:

```bash
docker ps --filter name=hyperstack-mcp
```

**Step 3 — Configure the MCP client:**

Then add the following configuration to the appropriate MCP config file for the current environment (e.g., `~/.claude.json`, `~/.gemini/config.json`, or the relevant IDE config for Cursor/Windsurf):
Add the following configuration to the appropriate MCP config file for the current environment (e.g., `~/.claude.json`, `~/.gemini/config.json`, or the relevant IDE config for Cursor/Windsurf):

```json
{
"mcpServers": {
"hyperstack": {
"command": "docker",
"args": [
"run",
"-i",
"--rm",
"--memory=256m",
"--cpus=0.5",
"ghcr.io/orkait/hyperstack:main"
]
"args": ["exec", "-i", "hyperstack-mcp", "bun", "/app/src/index.ts"]
}
}
}
```

The `--memory=256m` and `--cpus=0.5` flags are intentional resource limits. Do not remove them. The server runs fine within these constraints.
Each CLI invocation spawns a new `bun` process inside the existing `hyperstack-mcp` container — no new container, no startup cost.

**Upgrading:** Pull again to get the latest version, then restart the CLI/IDE:
**Why not `docker run --rm` per session?** `docker run` creates a brand-new container on every invocation. Over several sessions this piles up container state, spends 100–300ms per session on cold startup, and (without proper stdin lifecycle handling) can leave orphaned containers running after Claude exits. The `exec` pattern has none of these problems.

**Upgrading the image:**

```bash
docker pull ghcr.io/orkait/hyperstack:main
docker rm -f hyperstack-mcp
docker run -d --name hyperstack-mcp --restart unless-stopped \
--memory=512m --cpus=1 \
--entrypoint sleep \
ghcr.io/orkait/hyperstack:main infinity
```

Then restart the CLI/IDE so open sessions reconnect to the new container.

### Option B: Local Bun (Fallback)

If Docker is NOT available, run the server locally using Bun:
Expand All @@ -107,9 +130,14 @@ There is no build step. Bun runs TypeScript directly from source.

**Pre-check: confirm the MCP server starts before opening the IDE.**

For Docker (Option A), run this directly in a terminal:
For Docker (Option A), first confirm the persistent container is running:
```bash
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | docker run -i --rm ghcr.io/orkait/hyperstack:main
docker ps --filter name=hyperstack-mcp
```

Then test the exec path directly:
```bash
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}}}' | docker exec -i hyperstack-mcp bun /app/src/index.ts
```

Expected output (server is working):
Expand Down Expand Up @@ -152,15 +180,13 @@ If installation failed at any step, report the specific error and what would nee

## Troubleshooting

### MCP server shows as failed on first use (cold-start timeout)
### MCP server shows as failed on first use

The most common cause: Docker pulls the image during MCP startup and exceeds the initialization timeout.
Most common causes:

Fix:
1. `docker pull ghcr.io/orkait/hyperstack:main` — wait for it to finish
2. Restart the CLI/IDE

Only needs to happen once. All subsequent starts use the local cache.
1. **Persistent container not running.** Check: `docker ps --filter name=hyperstack-mcp`. If empty, run Step 2 from Option A to start it.
2. **Image not pulled.** Run `docker pull ghcr.io/orkait/hyperstack:main` and retry.
3. **Wrong container name in config.** The config must use `hyperstack-mcp` as the exec target — must match the `--name` used in Step 2.

### MCP server shows as failed / cannot pull the Docker image

Expand All @@ -171,10 +197,26 @@ If the pull fails, confirm Docker is running and you have an internet connection
### MCP server starts but tools return no results

The MCP config file may point to the wrong binary or the server is not running. Verify:
- Docker: run `docker run -i --rm ghcr.io/orkait/hyperstack:main` and confirm it starts without error
- Docker: run `docker exec -i hyperstack-mcp bun /app/src/index.ts` manually — it should accept JSON-RPC on stdin and respond. If the container isn't running, start it per Step 2 of Option A.
- Local Bun: confirm the absolute path in `args` exists (`ls /path/to/hyperstack/bin/hyperstack.mjs`)
- Restart the CLI/IDE after any config change - MCP servers are loaded at startup

### Too many hyperstack containers piling up

If you see multiple `ghcr.io/orkait/hyperstack` containers running:

```bash
docker ps -a --filter "ancestor=ghcr.io/orkait/hyperstack:main"
```

Your MCP config is using the legacy `docker run --rm` pattern instead of `docker exec`. Clean up and switch to the new config:

```bash
docker ps -aq --filter "ancestor=ghcr.io/orkait/hyperstack:main" | xargs -r docker rm -f
```

Then follow Step 2 of Option A to start the single persistent `hyperstack-mcp` container, and update your MCP config to the `docker exec` form shown in Step 3.

### SessionStart hook does not fire

On Claude Code, hooks live in `.claude/hooks.json`. Confirm the file exists in the repository root and references `session-start.mjs`. If the hook is missing or malformed, the `using-hyperstack` skill will not be injected automatically. You can still invoke skills manually with `/using-hyperstack`.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@orkait-ai/hyperstack",
"version": "1.0.0",
"version": "1.0.1",
"description": "Disciplined MCP server + skill system. 11 plugins, 79 tools, 21 skills with adversarial enforcement. Designer/DESIGN.md pipeline, shadcn/ui, React Flow, Motion, Lenis, React 19, Echo, Go, Rust, design tokens, UI/UX.",
"bin": {
"hyperstack": "bin/hyperstack.mjs"
Expand Down
6 changes: 6 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ loadPlugins(server, [
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);

const shutdown = () => process.exit(0);
process.stdin.on("close", shutdown);
process.stdin.on("end", shutdown);
process.on("SIGTERM", shutdown);
process.on("SIGINT", shutdown);
}

main().catch((err) => {
Expand Down
Loading