mycouncil — MCP server
Run multi-LLM myCouncil debates from Claude Code, Claude Desktop, Cursor, and any other MCP-aware client.
Thin wrapper over the public myCouncil API. Rounds, billing, and auto-config mode live on your account — the MCP server just relays calls.
What's new
0.7.1 — install fix. Pins mcp<2: the MCP Python SDK 2.0
(July 2026) renamed FastMCP, so fresh installs of earlier releases fail
at startup with No module named 'mcp.server.fastmcp'. Upgrade to 0.7.1;
no behaviour changes.
0.7.0 — RAG endpoint set by the caller, private debates. New
rag_search_url (full search endpoint, used as is) next to rag_base_url,
and optional rag_collection; the wrapper no longer injects
collection: "stt-calls" — it's sent only when you pass it. New
private parameter on mycouncil_debate(_start) and --private server
flag: the API refuses public share links for private debates. A debate
with RAG call memory is always private, and return_as="link" for any
private debate is refused before it starts (no rounds spent). See
RAG modes and Private debates.
0.6.1 — debate settings reach the server. max_rounds /
initial_budget at the top level of config are moved into
config["adf_settings"] before the request — the server only reads them
there and silently ignored the top-level copy (a type-2 debate asked for 3
rounds ran, and reserved, 5). New optional max_rounds and
initial_budget parameters on mycouncil_debate(_start); a setting that
doesn't fit the config's session type returns {"error": "invalid_params"}
instead of being dropped. Quota docs now cover competitive debates
(session_type 3), which are priced by per-debater budget. See
Quotas and debate settings.
0.6.0 — in-debate RAG (--rag-mode debate). The wrapper stops
searching on its own and instead forwards per-call RAG parameters
(pooled rag_access_token or per-expert rag_access_tokens, plus
rag_max_requests / rag_expires_in) to the debate API as a retrieval
object — the council's agents then query the corpus themselves,
mid-debate. --rag-prelude becomes a deprecated alias for
--rag-mode prelude; defaults are unchanged. See
RAG modes.
0.5.0 — optional RAG prelude (--rag-prelude). The debate tools
gain two optional per-call parameters — rag_access_token + rag_base_url.
When a call carries both, the wrapper runs one hybrid search over an
external stakeholder-call RAG corpus and prepends the found excerpts to the
debate content, so the council grounds its takes in what stakeholders
actually said. Off by default; without the flag the tool schemas are
unchanged. See RAG modes.
0.4.0 — optional per-request auth for streamable-http
(--auth per-request). Each HTTP call carries its own myCouncil API key in
the X-MyCouncil-Key (or Authorization: Bearer) header, so one hosted
wrapper can serve many myCouncil accounts — each caller spends their own
rounds. Default is unchanged (--auth shared, single env key); stdio is
unchanged. See Per-request auth.
0.3.0 — optional streamable-http transport
(--transport streamable-http). Run the server as one long-lived HTTP
service instead of a per-client stdio process — the async server handles
concurrent debates natively (no stdio→HTTP bridge in front). Identity is
unchanged: a single MYCOUNCIL_API_KEY from the environment. stdio stays
the default. See Running over streamable HTTP.
0.2.0 — added mycouncil_info (orientation guide agents can call once
per session) and mycouncil_list_roles (browse the curated expert-role
catalogue when composing a custom council). 8 tools total now.
0.1.0 — initial release: 6 tools, tier abstraction (fast / balanced
/ deep), model IDs hidden from the agent.
Setup
- Sign up at https://app.mycouncil.xyz (10 free rounds).
- Pick your auto-config mode under Account → Auto-config Settings
(
standardis free;advancedcosts 1 round per call). - Create an API key under Account → API. The key is shown once — save it.
- Register the MCP server in your client (see below).
Nothing to install — uvx fetches the package on first use. Requires
uv (curl -LsSf https://astral.sh/uv/install.sh | sh).
Claude Code
claude mcp add mycouncil \
--env MYCOUNCIL_API_KEY=mc_your_key_here \
-- uvx mycouncil
Claude Desktop / Cursor
In claude_desktop_config.json or ~/.cursor/mcp.json:
{
"mcpServers": {
"mycouncil": {
"command": "uvx",
"args": ["mycouncil"],
"env": { "MYCOUNCIL_API_KEY": "mc_your_key_here" }
}
}
}
Running over streamable HTTP
By default the server runs over stdio — one process per client, spawned by the MCP client. You can instead run it as a single long-lived streamable-http service:
MYCOUNCIL_API_KEY=mc_your_key_here \
uvx mycouncil --transport streamable-http --host 127.0.0.1 --port 8000
The endpoint is then http://<host>:<port>/mcp. Point any streamable-http
MCP client at it:
{
"mcpServers": {
"mycouncil": {
"type": "streamable-http",
"url": "http://127.0.0.1:8000/mcp"
}
}
}
Or, with the Claude Code CLI (no env var on the client — the key lives with the running service):
claude mcp add --transport http mycouncil http://127.0.0.1:8000/mcp
By default this is single-identity (--auth shared): every request uses
the one MYCOUNCIL_API_KEY the process was started with — all callers share
that account's rounds and balance. The transport runs in stateless mode (a
fresh transport per request), so there is no session affinity to manage. For
one wrapper serving many accounts, see
Per-request auth.
Per-request auth (multi-account)
--auth per-request makes each HTTP call authenticate itself: the caller
passes their own myCouncil key on every request, and the wrapper uses it for
exactly that call. No key is read from the environment; different callers
spend their own rounds.
uvx mycouncil --transport streamable-http --host 0.0.0.0 --port 8000 \
--auth per-request
The key is taken from (first match wins):
X-MyCouncil-Key: mc_...— dedicated header; use it when a proxy in front of the wrapper already occupiesAuthorizationfor its own auth.Authorization: Bearer mc_...— the standard form.
Client config example (any streamable-http MCP client that supports custom headers):
{
"mcpServers": {
"mycouncil": {
"type": "streamable-http",
"url": "https://your-host.example/mcp",
"headers": { "Authorization": "Bearer mc_your_key_here" }
}
}
}
Or with the Claude Code CLI:
claude mcp add --transport http mycouncil https://your-host.example/mcp \
--header "Authorization: Bearer mc_your_key_here"
Calls without a key are rejected with a structured
{"error": "api_error", "status_code": 401, ...} payload — there is no
silent fallback to an environment key, so a misconfigured client can never
spend someone else's rounds. --auth per-request requires
--transport streamable-http (stdio has no request headers; startup fails
otherwise).
Always put TLS in front of this mode. API keys travel in request
headers on every call; terminate HTTPS at your reverse proxy. Plain
http:// is acceptable only on 127.0.0.1.
Notes:
- Binding beyond localhost. The default bind is
127.0.0.1. If you set--host 0.0.0.0(e.g. behind a reverse proxy), the localhost-only DNS-rebinding guard is relaxed automatically — put the service behind your own proxy / network controls, since anyone who can reach the port spends the configured key's quota. - Long-running debates. Blocking
mycouncil_debateholds the HTTP response open while it polls (up totimeout_minutes, default 20) with no bytes flowing. Raise idle timeouts on any intermediary proxy, or prefer the async pairmycouncil_debate_start+mycouncil_debate_statusover HTTP.
All flags have environment-variable equivalents (MYCOUNCIL_TRANSPORT,
MYCOUNCIL_HTTP_HOST, MYCOUNCIL_HTTP_PORT, MYCOUNCIL_HTTP_PATH,
MYCOUNCIL_HTTP_AUTH) — see
Environment variables.
RAG modes
--rag-mode {prelude|debate} (env: MYCOUNCIL_RAG_MODE) connects debates
to an external RAG corpus of stakeholder call transcripts. Off by default:
without the flag the debate tool schemas contain no rag parameters at all,
so agents can't pass them by accident. Both modes work on both transports
(parameters travel in the tool call, not in HTTP headers, so stdio works
too) and compose with --auth per-request. Tokens are never logged,
stored, or echoed into results in either mode.
The search endpoint is given per call, the same way in both modes:
rag_search_url— the full endpoint, used as is, e.g.https://rag.example.com/v1/stt/search. Preferred: if the RAG service moves its endpoint, only the caller changes;rag_base_url— the service root;/v1/searchis appended (the pre-0.7.0 layout). If both are given,rag_search_urlwins;rag_collection— optional; goes into the search body only when given. Services that take the collection from the token reject unknown body fields, so since 0.7.0 nothing is sent by default.
A debate with RAG call memory is always private (see Private debates).
Prelude mode (--rag-mode prelude)
The wrapper searches once, before the debate. Adds optional per-call
parameters to mycouncil_debate and mycouncil_debate_start:
| Parameter | What it is |
|---|---|
rag_access_token |
Short-lived bearer token for the RAG service, issued per debate. |
rag_search_url / rag_base_url |
The search endpoint, see above. |
rag_collection |
Optional collection name for the search body. |
When a call carries a token and an endpoint, the wrapper runs one hybrid
search (top_k 5) and sends the debate content to the server as:
### Question
<original content>
### Useful info
1. <excerpt from a stakeholder call> (score 0.91; source: ...)
2. ...
Behaviour details:
- No token, or no endpoint → plain debate, exactly as without the flag. RAG is an enrichment, not a dependency.
- Calls that would fail anyway (
return_as="link", a debate setting that doesn't fit the session type) are refused before the search, so they don't spend the token's requests. - The result carries
rag_prelude: "applied"orrag_prelude: "skipped (...)"so integrators can verify the prelude ran. - Failure policy:
401from RAG → no retry (token is dead), run the debate plain; network errors /5xx→ one retry with backoff, then run plain. A RAG outage never fails a debate.
--rag-prelude (env: MYCOUNCIL_RAG_PRELUDE=1) is a deprecated alias
for this mode; combining it with --rag-mode debate fails at startup.
Debate mode (--rag-mode debate)
The wrapper performs no searching at all — it forwards RAG parameters
to the debate API as a retrieval object, and the council's agents query
the corpus themselves, mid-debate (requires a myCouncil server with
in-debate retrieval support). Parameters added to the debate tools:
| Parameter | What it is |
|---|---|
rag_access_token |
Pooled bearer token — every council agent gets the memory-search tool. |
rag_access_tokens |
Map of expert index to token, e.g. {"0": "...", "2": "..."} — only those experts get the tool. Requires an explicit config.experts list. |
rag_search_url / rag_base_url |
The search endpoint, see above. One of them is required with any token. |
rag_collection |
Optional collection name, forwarded only when given. |
rag_max_requests |
Optional search budget per token (server default: 10). |
rag_expires_in |
Optional token TTL in seconds; the server turns memory off when it runs out — pass it. |
Validation happens in the wrapper before any API call: rag_access_token
and rag_access_tokens are mutually exclusive; per-agent tokens require
explicit experts (auto-config doesn't know the lineup in advance) and
string-number keys within the expert index range; a token without
rag_search_url or rag_base_url is an error. Endpoints on private or
reserved networks are rejected by the server with a 422 before any
rounds are charged. Violations return
{"error": "invalid_rag_params", "detail": ...}.
The result carries rag: the server's no-secrets retrieval summary (e.g.
{"agents": ["agent_0"], "max_requests": 25, "used": 7}), or
rag: "skipped (API does not support in-debate retrieval)" when the
server predates the feature — there is no silent fallback to prelude.
Private debates
A private debate can't get a public share link: the server refuses it (403) for the whole life of the conversation. The owner, their team and myCouncil admins can still view it; only an admin can lift the flag.
private=trueonmycouncil_debate/mycouncil_debate_startstarts a private debate;- a debate with RAG call memory (any
rag_*token) is private automatically; --private(env:MYCOUNCIL_PRIVATE=1) makes every debate on this server private and refusesmycouncil_share(format="link")outright.
For private debates return_as="link" returns
{"error": "invalid_params"} before the debate starts, so no rounds
are spent; use pdf or transcript. Results carry the server's
private: true|false.
Tools
| Tool | What it does |
|---|---|
mycouncil_info |
One-call agent orientation: flows, tier semantics, quota notes. Call once at the start of a session. |
mycouncil_balance |
Remaining rounds + current auto-config mode. |
mycouncil_list_roles |
List curated system + own + team expert roles. Use their id as role_preset when composing a custom council. |
mycouncil_auto_config |
Generate a session config from a query. Returns roles + temperatures + tier. Concrete model IDs are not exposed — the planner and the server pick them based on the tier. |
mycouncil_debate_start |
Start a debate, return job_id. Accepts a config returned by mycouncil_auto_config (with or without edits). Optional max_rounds / initial_budget — see Quotas and debate settings. |
mycouncil_debate_status |
Poll a debate by job_id. |
mycouncil_debate |
Blocking: start, poll, return the finished result. return_as: pdf (default) / transcript / link. Same optional max_rounds / initial_budget. |
mycouncil_share |
Share or export an existing conversation: format=link (public URL) or format=pdf (file on disk). |
How tiers work
The planner LLM picks one of three operating tiers based on the question:
- fast — quick + cheap models, for simple / casual questions.
- balanced — sweet spot. Default for typical analytical questions.
- deep — slow + reasoning-heavy models, for high-stakes / complex / irreversible decisions. Only available in advanced auto-config mode.
Tiers are an operating mode, not a quality rank — deep does not always
beat balanced in absolute terms, it just takes more time and money. The
planner is instructed to lean toward balanced and not escalate by default.
After mycouncil_auto_config, you may edit config["tier"] and roles
before passing it to mycouncil_debate(_start). MCP fills concrete models
from the tier locally; the agent never sees specific provider names.
Quotas and debate settings
What a debate reserves at start (the unused part is refunded when it finishes):
session_type |
Kind | Reserve | Setting |
|---|---|---|---|
| 1 | three-stage council | 1 round | — |
| 2 | moderated debate | max_rounds (default 5), charged per iteration run |
adf_settings.max_rounds |
| 3 | competitive debate | initial_budget × experts (default budget 5), charged by the points the debaters burn |
adf_settings.initial_budget (1-10) |
For type 3 max_rounds is only a hidden iteration ceiling, not a price —
don't set it. A 403 "Not enough rounds. Need 20, have 5" on type 3 means
e.g. 4 debaters × budget 5: lower the budget or the expert count.
Both settings live in config["adf_settings"]:
{"session_type": 2, "tier": "balanced", "experts": [...], "chairman": {...},
"adf_settings": {"max_rounds": 3}}
The server reads them only there; max_rounds / initial_budget at
the top level of config are ignored (and echoed back in config_used, so
the mistake is invisible). Since 0.6.1 the wrapper normalizes before
sending:
- top-level
max_rounds/initial_budgetare moved intoadf_settings(an explicitadf_settingsvalue wins) and removed from the top level; - the tools'
max_rounds/initial_budgetparameters override both; - a setting that doesn't fit the session type —
max_roundson type 1,initial_budgeton anything but type 3, either without aconfig— returns{"error": "invalid_params", "detail": ...}before any API call; - values are forwarded as-is (no client-side bounds); the server clamps
initial_budgetto 1-10.
Other quota notes:
mycouncil_auto_configis free instandardmode, 1 round inadvancedmode (refunded if the planner LLM fails).mycouncil_debateauto-configures internally if you don't pass a config — callingmycouncil_auto_configfirst only makes sense to preview / tweak the config. Inadvancedmode that bills you twice.
Environment variables
| Variable | Required | Default | Notes |
|---|---|---|---|
MYCOUNCIL_API_KEY |
yes* | — | Your mc_* key from Account → API. *Not read in per-request auth mode — keys arrive in request headers instead. |
MYCOUNCIL_BASE_URL |
no | https://app.mycouncil.xyz |
Override for staging / self-hosted. |
MYCOUNCIL_TRANSPORT |
no | stdio |
stdio or streamable-http. Overridden by --transport. |
MYCOUNCIL_HTTP_HOST |
no | 127.0.0.1 |
Bind host for streamable-http. Overridden by --host. |
MYCOUNCIL_HTTP_PORT |
no | 8000 |
Bind port for streamable-http. Overridden by --port. |
MYCOUNCIL_HTTP_PATH |
no | /mcp |
Endpoint path for streamable-http. Overridden by --path. |
MYCOUNCIL_HTTP_AUTH |
no | shared |
shared or per-request (streamable-http only). Overridden by --auth. |
MYCOUNCIL_RAG_MODE |
no | off | prelude or debate — see RAG modes. Overridden by --rag-mode. |
MYCOUNCIL_RAG_PRELUDE |
no | off | Deprecated alias: 1/true equals MYCOUNCIL_RAG_MODE=prelude. |
MYCOUNCIL_PRIVATE |
no | off | 1/true: every debate is private — see Private debates. Overridden by --private. |
Examples
Simplest path — let myCouncil pick everything:
Run
mycouncil_debateon "Should we migrate our backend from FastAPI to Go?" and return the result as a PDF in./review.pdf.
Preview and escalate the tier before running (advanced mode):
Use
mycouncil_auto_configfor "Replace our entire ML infra with a custom RAG system, $3M budget". If the planner returnstier: balanced, change it todeepand runmycouncil_debatewith the edited config.
Async polling:
Start the debate with
mycouncil_debate_start, then pollmycouncil_debate_statusevery minute until status iscompleteorfailed.
License
Metadata
Release files for mycouncil 0.7.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| mycouncil-0.7.1.tar.gz | 42.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mycouncil-0.7.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 79.9 kB
Release files / mycouncil-0.7.1.tar.gz
| Download URL | mycouncil-0.7.1.tar.gz |
|---|---|
| Size | 42.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a0eee26891ec9d97104ef703fe2f04168c7f6607bd201fb89f0c59dad992bff8
|
|
BLAKE2b-256 checksum How to use checksums |
8062d350dc793a9704788ca74eedb61af787ce827cd44c19b6ab83e61ed70170
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.5.26
|
Release files / mycouncil-0.7.1-py3-none-any.whl
| Download URL | mycouncil-0.7.1-py3-none-any.whl |
|---|---|
| Size | 37.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6767967326582c97a83a607bf2381f03ba1c00a2a454c6950eeb78f40b4b0531
|
|
BLAKE2b-256 checksum How to use checksums |
c96d71596659f3a4e206364e314997cf2738a90df72c6f67fe8100912e0a50d1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.5.26
|
Security for mycouncil 0.7.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.