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
- 参考文档EVOLUTION.md3.1 KB
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, everydocker rmdestroys all rooms. Prefer a named volume (council-data, asdocker-compose.ymldoes). - 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-sidesqlite3, 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,~/Desktopand~/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.dbwrites a consistent snapshot (SQLiteVACUUM INTO) and refuses to overwrite a file. Copy it out withdocker cp. To restore, stop the server, replacecouncil.dband delete the stale-wal/-shmfiles.
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
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).
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).
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`.
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).
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).
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).