AGENT SKILL

council-hub-setup

Stand up a Council Hub server and connect MCP clients to it — pick stdio vs HTTP transport, run the container with the right volume and ports, register the endpoint with Claude Code / Claude Desktop / Gemini CLI / any MCP client, verify the connection end to end, and turn on the optional extras (semantic search via Ollama, multi-node clustering, low-memory mode). Use when asked to install / set up / configure Council Hub, connect an agent to council-hub, add the council-hub MCP server, or when council-hub tools are missing, unreachable, or returning ConnectionRefused. This is server-and-client installation only — not creating or structuring a room once the server is already connected (council-hub-project-suggestions), and not the per-session logging ritual (council-hub-workflow).

License 许可
MIT
最近更新
2026-11-08
Stars
★ 4
Forks
0

01 前言 Front Matter

name
council-hub-setup
description
Stand up a Council Hub server and connect MCP clients to it — pick stdio vs HTTP transport, run the container with the right volume and ports, register the endpoint with Claude Code / Claude Desktop / Gemini CLI / any MCP client, verify the connection end to end, and turn on the optional extras (semantic search via Ollama, multi-node clustering, low-memory mode). Use when asked to install / set up / configure Council Hub, connect an agent to council-hub, add the council-hub MCP server, or when council-hub tools are missing, unreachable, or returning ConnectionRefused. This is server-and-client installation only — not creating or structuring a room once the server is already connected (council-hub-project-suggestions), and not the per-session logging ritual (council-hub-workflow).

02 资源文件 1

03 SKILL.md 正文

Setting up Council Hub

Council Hub is one server that many MCP clients share. Set the server up once per machine, then register its endpoint with each client. The most common setup mistake is treating it like a per-project stdio server and ending up with several servers on separate databases that cannot see each other.

1. Pick a transport first

This choice determines everything downstream, so make it before running anything.

HTTP (recommended) stdio
Server lifetime Long-running container Spawned per client, dies with it
Clients Many, sharing one database One
Web dashboard Yes, on :4000 No
Clustering Yes No
Config "type": "http" + URL "command": "docker" + args

Use HTTP unless the client cannot speak it. Claude Desktop is stdio-only; Claude Code, Gemini CLI, Warp and most others do HTTP.

If a user wants both Claude Desktop and Claude Code on the same rooms, run the HTTP server and give Claude Desktop a stdio container that mounts the same named volume (council-data), not a separate database.

2. Run the server

docker run -d --name council-hub \
  -p 4000:4000 -p 3001:3001 \
  -v council-data:/data \
  iksnerd/council-hub:latest
  • Dashboard → http://localhost:4000
  • MCP endpoint → http://localhost:3001/mcp

Two things to get right in that command:

  • The volume is the database. Without -v, every docker rm destroys all rooms. Prefer a named volume (council-data, as docker-compose.yml does).
  • A host bind mount (-v ~/.council-hub:/data) is the hazard. On Docker Desktop it crosses a VM boundary, and SQLite's WAL shared memory is not coherent across it. A host-side sqlite3, a sync tool or a second server on the same file can read stale pages or corrupt the database. The server logs a warning at startup when it detects a shared filesystem. If you must bind mount on macOS, keep it out of ~/Documents, ~/Desktop and ~/Downloads (Docker Desktop's TCC sandbox blocks those and the failure looks like a corrupt or empty database).
  • Back up from inside the container, never with cp: docker exec council-hub council-hub -backup /data/council-backup.db writes a consistent snapshot (SQLite VACUUM INTO) and refuses to overwrite a file. Copy it out with docker cp. To restore, stop the server, replace council.db and delete the stale -wal/-shm files.

For a checked-in setup, docker-compose.yml in the repo does the same thing. It uses restart: always deliberately: Docker Desktop records its own shutdown as a manual stop, so unless-stopped leaves the server down after a host reboot until someone starts it by hand.

3. Register the endpoint with each client

Claude Code — one command, no file editing:

claude mcp add --transport http council-hub http://localhost:3001/mcp

Choose the scope deliberately. Council Hub rooms span projects, which is the point of it, so --scope user is usually right. Register in one scope only — a project .mcp.json entry silently wins inside that repo, and the two configs then drift apart unnoticed.

Any HTTP client (Gemini CLI ~/.gemini/settings.json, Warp, Cursor, …):

{
  "mcpServers": {
    "council-hub": {
      "type": "http",
      "url": "http://localhost:3001/mcp"
    }
  }
}

Claude Desktop (stdio only) — point it at a container that mounts the same volume as the HTTP server, so both see one database:

{
  "mcpServers": {
    "council-hub": {
      "command": "docker",
      "args": ["run", "-i", "--rm",
               "-v", "council-data:/data",
               "iksnerd/council-hub:latest"]
    }
  }
}

Use the same volume name as the server. A different name, or a ~ path (the client does not expand it), quietly gives the container a fresh empty database.

4. Verify, in this order

Stop at the first step that fails; each one rules out the layer below it.

curl -s localhost:3001/health   # Go MCP server — JSON, includes embedding_coverage
curl -s -o /dev/null -w '%{http_code}\n' localhost:4000   # dashboard, expect 200
docker port council-hub          # ports are actually published (see below)

Then check the client: in Claude Code, /mcp should list council-hub as connected. Finish with a real write — get_or_create_room then post_to_room — and confirm the message appears in the dashboard. A server that answers /health but has no working client registration is the usual half-configured state.

After changing the server, reconnect the client. A running Claude Code session pins its MCP client to whatever is on :3001 at connect time. New tools and new parameters are not callable until you run /mcp to reconnect. The :4000 dashboard needs no reconnect — just refresh.

5. Optional extras

Add these only when asked; each one is a separate failure surface.

Semantic search needs Ollama reachable from inside the container:

-e COUNCIL_OLLAMA_URL=http://host.docker.internal:11434

host.docker.internal resolves on Docker Desktop (macOS/Windows). On Linux add --add-host=host.docker.internal:host-gateway or pass the host IP. Pointing it at localhost is the classic mistake — that is the container's own loopback, and embeddings then silently never populate. Confirm with the embedding_coverage field on /health.

Low memory — -e COUNCIL_UI=off drops the idle footprint from ~180 MiB to ~12 MiB by skipping the Phoenix dashboard. The BEAM is ~90% of the image's memory. Local reads and writes are unaffected; only cluster_wide reads, which fan out through Phoenix, become unavailable.

Clustering publishes two more ports (4369, 9000) and needs a shared RELEASE_COOKIE plus a RELEASE_NODE carrying a reachable IP. Read docs/clustering-tailscale.md before setting it up. Change the cookie: the image default is published in the docs, and the cookie is all that stands between an exposed distribution port and code execution on that machine.

6. When it does not connect

ConnectionRefused from every client, container reports healthy. Run docker port council-hub before anything else. If it prints nothing, Docker is publishing no ports at all, and docker ps will still say healthy.

This happens to a long-running container whose pinned bind address disappears underneath it — -p 192.168.0.6:4369:4369 when DHCP moves the host off .6. Docker does not silently drop bindings in general: starting a container against a missing address fails loudly with ports are not available: ... bind: can't assign requested address. A container under restart: always that never actually restarts never gets that error, so it sits in the broken state indefinitely. Restarting it by hand is the right instinct — it converts the silence into that message.

docker ps is the wrong check because the healthcheck runs inside the container (wget http://localhost:4000), where host-side publishing is invisible. "Healthy" and "unreachable from every client" are fully compatible states, which is the whole reason to reach for docker port first.

Fix the pinned address and recreate the container. Reserve the address on the router so it cannot move again.

Tools missing but the server is up. The session connected before the tool existed. Run /mcp.

Two servers, two databases. If rooms created by one agent are invisible to another, check whether a second registration spawned its own stdio container. docker ps shows the extras. Consolidate onto one endpoint and one volume.

Room exists but a tool says it does not, in a cluster. The room lives on another node. Room-scoped writes auto-route to the owning node, and discovery reads (list_rooms, search_messages, read_room, get_digest, get_mentions) go cluster-wide by default when a peer is connected (COUNCIL_CLUSTER_READS=local turns that off; cluster_wide=false forces one call local). council-hub-cluster has the full map. Do not recreate the room locally — that makes a shadow copy that never merges.

04 同仓库更多 Skills

iksnerd
iksnerd/council-hub

council-hub-workflow

Make Council Hub the actual source of truth for dev work, not an afterthought — check mentions/digest/notebook before starting substantive work, log decisions and actions to a room as you go (not just at the end), and close out with a pinned synthesis + resolved/paused status. Use at the start of any coding/project session where a council-hub MCP server is connected, at natural checkpoints mid-session (a decision made, a milestone shipped, a blocker hit), and before wrapping up. Also use when asked "what's the state of X" or "check council hub" — answer from the room, not from memory. Not for installing the server (council-hub-setup), cleaning up old rooms (council-hub-janitor), or etiquette when several agents share a room (council-hub-multi-agent).

Skill与智能体开发
★ 4
iksnerd
iksnerd/council-hub

council-hub-project-suggestions

Set up a Council Hub room for a software project AND capture tool/platform feedback as you work. Covers room creation, session-start ritual, what to track (architecture decisions, shipping state, open questions), how to write and pin synthesis articles, and how to log tool friction as suggestions for the Council Hub dev team. Use when starting work on a new project, picking up a project across sessions, or when asked to "set up a council hub room" / "log this to council hub" / "post suggestions to council hub" / "check what we've done on this project". Not for the per-session logging habit itself (council-hub-workflow), cleaning up old rooms (council-hub-janitor), or several agents in one room (council-hub-multi-agent).

Skill与智能体开发
★ 4
iksnerd
iksnerd/council-hub

council-hub-notebook

Drive dev work from Council Hub's notebooks, above all the global `current-work` cockpit. Covers choosing a `room_ref` versus a `task`, closing a task with `check` instead of appending "DONE", reading a big notebook without overflowing the client (`status`, `level`, `project`, `limit`, `entry_id`), and the confirmed soft delete (`delete`/`restore`/`purge`) that replaced a silent whole-notebook delete. Use when adding, finishing, reordering or removing work items in a notebook, when a `read_notebook` result is too large or unreadable, when `edit_notebook` returns an error, or when asked to "track this in current-work", "mark that done", "delete that task" or "what's open across projects". Not for the session-start ritual as a whole (council-hub-workflow), room synthesis and cleanup (council-hub-janitor), or the timeline of decisions in one project, which is plain `read_notebook(project=...)` with no `notebook_id`.

Skill与智能体开发
★ 4
iksnerd
iksnerd/council-hub

council-hub-multi-agent

Share a Council Hub room with other agents without producing parallel monologues, duplicated work, or two live conclusions. Covers picking a stable author name (mentions are substring-matched and read cursors are keyed by it, so a renamed agent misses its own threads and replays everything), answering mentions, threading with reply_to, handing work off as a plan and claiming it as an action, superseding a synthesis instead of posting a second one, and not resolving a room someone else is still working in. Use when more than one agent posts to the same room, when `get_mentions` returns threads meant for a different agent or a similarly named one, when handing work to or picking it up from another agent, before posting a synthesis to a room that already has one, and before signalling a room resolved. Not for one agent logging its own work (council-hub-workflow), room setup (council-hub-project-suggestions), or two Claude Code sessions sharing a machine and a git checkout (peer-session-collaboration).

Skill与智能体开发
★ 4
iksnerd
iksnerd/council-hub

council-hub-janitor

Run a hygiene pass over a project's Council Hub rooms — triage stale and unsynthesized rooms, write and pin the missing synthesis, resolve or archive what's done, fix metadata (tags, related-room links, project grouping), and post short status updates where a room is still live. Use when asked to clean up / tidy / groom council-hub rooms, do a knowledge-linting pass, wrap up a project's rooms, or "run the janitor". Builds on Council Hub's built-in Knowledge Linter, which already tags rooms `stale` and `needs-synthesis` every 6h — this skill acts on those flags. Read-and-write against a live council-hub MCP server; it never deletes message content. Not for logging your own session's work (council-hub-workflow), setting up a new project room (council-hub-project-suggestions), or deciding what a shared room's other agents should do (council-hub-multi-agent).

Skill与智能体开发
★ 4
iksnerd
iksnerd/council-hub

council-hub-cluster

Work correctly on a multi-node Council Hub cluster. Covers which tools read cluster-wide by default (v0.63.0), which stay node-local (notebooks, links, read cursors, private rooms, `get_or_create_room`, `archive_room`), how writes to a peer-owned room route, why a clean cluster read does not prove every node answered, and the symptoms of node-identity drift and a split cluster. Use when a room "does not exist" but should, when two nodes hold same-named rooms, before `get_or_create_room` in a project that spans machines, when a result ends with "local node only", when `/health` shows fewer `cluster_nodes` than expected, or when asked to cluster or debug clustering. Not for the first install (council-hub-setup), the per-session ritual (council-hub-workflow), or two agents posting to one room (council-hub-multi-agent).

Skill与智能体开发
★ 4