{
  "markdown": "# weeek-mcp\n\nLocal, read-only-by-default MCP server for [Weeek](https://weeek.net/) — with opt-in write tools.\n\n[![npm version](https://img.shields.io/npm/v/weeek-mcp.svg)](https://www.npmjs.com/package/weeek-mcp)\n[![npm downloads](https://img.shields.io/npm/dm/weeek-mcp.svg)](https://www.npmjs.com/package/weeek-mcp)\n[![CI](https://github.com/YOLKINS/weeek-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/YOLKINS/weeek-mcp/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n[![MCP](https://img.shields.io/badge/MCP-2025--06--18-blue.svg)](https://modelcontextprotocol.io/)\n\n[Русская версия README](README.ru.md)\n\n`weeek-mcp` connects AI clients (Claude Desktop, Claude Code, Cursor, MCP\nInspector) to your Weeek workspace over stdio. It is **read-only by default** —\na default install can list projects, tasks, boards, members and tags but change\nnothing — and exposes five write tools only when you opt in with `READ_ONLY=false`.\nRuns on Node ≥ 20; install with `npx`, no clone or build required.\n\n## Why this one\n\n- **On npm.** `npx -y weeek-mcp` works today — no clone, no build, no absolute paths.\n- **Read-only by default, with composable gates.** Writes are simply not\n  registered unless you opt in; `ENABLED_TOOLS` whitelists a subset and\n  `MAX_RESPONSE_CHARS` caps every payload. Server-side, not client convention.\n- **Bilingual.** Full EN ↔ RU documentation parity.\n- **Granular error model.** Nine distinct error codes with agent-readable\n  messages, so a model knows when to retry versus give up without parsing prose.\n\n## Quickstart\n\nThe recommended install path is `npx` — no clone, no build. Drop\n[examples/claude_desktop.mcp.json](examples/claude_desktop.mcp.json) into your\nMCP client config, replace `YOUR_WEEEK_TOKEN_HERE` with a real token from\n<https://app.weeek.net/ws/_/settings/apps/api>, and restart the client:\n\n```json\n{\n  \"mcpServers\": {\n    \"weeek\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"weeek-mcp\"],\n      \"env\": {\n        \"WEEEK_ACCESS_TOKEN\": \"YOUR_WEEEK_TOKEN_HERE\"\n      }\n    }\n  }\n}\n```\n\n`npx` downloads `weeek-mcp` on first launch and caches it. Cursor and Cline use\nthe same `mcpServers` shape — see [examples/cursor.mcp.json](examples/cursor.mcp.json)\nand [examples/cline.mcp.json](examples/cline.mcp.json). Other env vars have safe\ndefaults; override only what you need (see [Configuration](#configuration)). If\n`npx` cannot find `node` (typical with `nvm`), see\n[Troubleshooting](#troubleshooting); for a zero-dependency smoke test see\n[docs/smoke.md](docs/smoke.md).\n\n> `examples/` lives on GitHub only — the npm tarball ships `dist/` +\n> `README.md` + `README.ru.md` + `LICENSE`.\n\n## Tools\n\nTen **read** tools are exposed by default. All fifteen appear only under\n`READ_ONLY=false` (see [Enabling write tools](#enabling-write-tools)).\n\n| Read tool | Returns |\n|---|---|\n| `ping` | `pong: <msg>` — transport health check, no API call, no token |\n| `weeek_get_me` | the authenticated user (`id`, `email`, `name`) — confirms the token |\n| `weeek_list_projects` | every project visible to the token |\n| `weeek_get_project` | a single project by id, including its `description` |\n| `weeek_list_tasks` | one page of tasks (filters + offset/`per_page` pagination) |\n| `weeek_get_task` | a single task by id, with multi-assignee fields |\n| `weeek_list_members` | every workspace member |\n| `weeek_list_tags` | every tag |\n| `weeek_list_boards` | every board in a project |\n| `weeek_list_board_columns` | every column of a board, in sort order |\n\n| Write tool (`READ_ONLY=false`) | Does |\n|---|---|\n| `weeek_complete_task` | flips the completion flag; `completed: false` re-opens |\n| `weeek_move_task` | moves a task to a board column (a column *is* a status) |\n| `weeek_create_task` | files a new task and returns it with its new id |\n| `weeek_update_task` | edits title / priority / type / due date |\n| `weeek_set_task_mr_link` | records a merge/pull-request URL in a custom field |\n\nFull field-level reference (inputs, outputs, edge cases, truncation,\nmulti-assignee) → [docs/tools.md](docs/tools.md).\n\n## Enabling write tools\n\n**The default install cannot change anything in your workspace.** All five\nmutating tools are hidden behind `READ_ONLY` (default `true`) — not registered,\nso they never appear in `tools/list`. Setting `READ_ONLY=false` takes\n`tools/list` from ten tools to fifteen and lets the agent **create, edit, move\nand complete tasks in the workspace the token can reach**. There is no\nserver-side confirmation step — `annotations` are a hint an MCP client is free\nto ignore. Point the token at a workspace whose contents you are willing to see\nchanged.\n\n```json\n\"env\": {\n  \"WEEEK_ACCESS_TOKEN\": \"YOUR_WEEEK_TOKEN_HERE\",\n  \"READ_ONLY\": \"false\"\n}\n```\n\n**Start with one tool, not five.** `READ_ONLY=false` intersected with\n`ENABLED_TOOLS` gives you writes on, but only the one you asked for:\n\n```json\n\"env\": {\n  \"WEEEK_ACCESS_TOKEN\": \"YOUR_WEEEK_TOKEN_HERE\",\n  \"READ_ONLY\": \"false\",\n  \"ENABLED_TOOLS\": \"weeek_complete_task\"\n}\n```\n\n`READ_ONLY` is the **outer** gate: naming a write tool in `ENABLED_TOOLS` does\nnot by itself opt into writes. The allowlist is not additive, so list the read\ntools you need alongside it —\n[examples/claude_desktop.write.mcp.json](examples/claude_desktop.write.mcp.json)\nis a ready-to-edit config that does exactly that.\n\n### What each write tool can and cannot do\n\n| Tool | Changes | Undone by | `destructiveHint` | `idempotentHint` |\n|---|---|---|---|---|\n| `weeek_complete_task` | one completion flag | re-firing with `completed: false` | `false` | `true` |\n| `weeek_set_task_mr_link` | one custom field's value | re-setting it | `false` | `true` |\n| `weeek_move_task` | the task's board column (and board) | moving it back — if you know where it was | `true` | `false` |\n| `weeek_update_task` | title / priority / type / due date | re-setting each field — if you know the old value | `true` | `false` |\n| `weeek_create_task` | files a **new** task | deleting it, which this server cannot do | `true` | `false` |\n\nThe three `true` rows are marked \"worth a human confirm\" because **the agent\nnever saw the old value** and cannot put it back; `weeek_create_task` is the one\nto watch — its effect cannot be undone through this server, and a retried create\nfiles a **second** task. `weeek_set_task_mr_link` resolves its custom field by\nname unless you pass `custom_field_id` / `custom_field_name` — the matched names\nand ambiguity rules are in\n[docs/tools.md](docs/tools.md#the-mr-link-field-naming-convention).\n\n## Configuration\n\nRead from the environment at startup and validated with zod; invalid values\nabort startup on stderr with a non-zero exit code. The server never reads a\n`.env` file itself — pass variables through your MCP client's `env` block or\nyour shell.\n\n| Variable | Required | Default | Purpose |\n|---|---|---|---|\n| `WEEEK_ACCESS_TOKEN` | yes | — | Personal Weeek API token (≥ 20 chars; placeholders and whitespace-padded values are rejected). |\n| `WEEEK_BASE_URL` | no | `https://api.weeek.net/public/v1` | Base URL for the Weeek HTTP client. Override for self-hosted proxies. |\n| `WEEEK_TIMEOUT_MS` | no | `30000` | Per-request timeout (ms). Positive integer. |\n| `READ_ONLY` | no | `true` | Hide write tools. When `true`, any tool whose `readOnlyHint !== true` is not registered. Accepts `true`/`false`/`1`/`0`. |\n| `ENABLED_TOOLS` | no | (unset = all) | Comma-separated allowlist of tool names, still intersected with `READ_ONLY`. Unknown names WARN; an empty result aborts startup. |\n| `MAX_RESPONSE_CHARS` | no | `65536` | Byte budget per response; over-budget payloads are clipped and flagged `truncated: true`. Min `1024`, max `1000000`. |\n| `LOG_LEVEL` | no | `info` | Logger threshold: `debug`, `info`, `warn`, `error`. Unknown values fall back to `info`. |\n\nBoth gates run **server-side**: a hidden tool is not registered, so an agent\ncannot call it. `READ_ONLY` is load-bearing — leave it at the default unless you\nintend an agent to change your workspace. See [.env.example](.env.example) for a\ncopy-pasteable template.\n\n## Troubleshooting\n\n| Symptom | Likely cause | Fix |\n|---|---|---|\n| Server doesn't appear in the client | `command` points at a `node` the client cannot find, or `dist/index.js` is missing/non-executable | Run `npm run build`; confirm `ls -la dist/index.js` shows `0755`. Use the absolute path from `which node` (see NVM note below). |\n| `MCP server failed to start` immediately | Same as above, plus `node_modules` missing | Run `npm install && npm run build` from the repo root. |\n| `invalid env: WEEEK_ACCESS_TOKEN: ...` on stderr | Token contains whitespace/control chars, or is the placeholder | Generate a real token at <https://app.weeek.net/ws/_/settings/apps/api> and paste it without surrounding spaces or newlines. |\n| `invalid env: WEEEK_BASE_URL: ...` | URL uses a non-`http(s)` scheme or contains `user:pass@` | Use plain `https://api.weeek.net/public/v1`; route credentials through `WEEEK_ACCESS_TOKEN`. |\n| `EACCES` launching `dist/index.js` | `postbuild` chmod skipped | `chmod +x dist/index.js`. |\n| `npm start` works but the client fails | The client launches under a different `PATH` than your shell | See the NVM workaround below. |\n\n<details>\n<summary><b>NVM workaround</b> — <code>spawn npx ENOENT</code> / <code>spawn node ENOENT</code></summary>\n\nClaude Desktop and Cursor launch their MCP subprocess under a non-interactive\nshell that does **not** source `~/.nvm/nvm.sh`, so a bare `\"command\": \"npx\"`\nsilently fails when Node is installed via nvm. Either hard-code an absolute path\n— run `which npx` and paste the result as `command` (update it whenever you\nswitch nvm version); the package is still downloaded and cached on first run:\n\n```json\n{ \"command\": \"/Users/<you>/.nvm/versions/node/v20.18.0/bin/npx\", \"args\": [\"-y\", \"weeek-mcp\"] }\n```\n\n— or point `command` at a small wrapper script that sources `~/.nvm/nvm.sh`\nbefore `exec npx \"$@\"`, which survives nvm version changes.\n\n</details>\n\n## Errors\n\nEvery Weeek tool fails the same way: `isError: true` with a single-line\n`<tool> failed (<weeek_code>): <one English sentence>`. The `weeek_<code>` token\nis the stable, machine-greppable contract; the sentence guides self-correction.\nNine codes cover unauthorized / forbidden / not-found / validation / rate-limit\n/ server / network / timeout / invalid-response, each with retry guidance.\n\n```\nweeek_get_task failed (weeek_not_found): Weeek returned 404 for this resource. Verify the id exists in the configured workspace and was not deleted.\nweeek_list_tasks failed (weeek_rate_limited): Weeek rate-limited the request (HTTP 429). Retry after a brief delay or reduce the call frequency.\n```\n\nFull table with retry semantics → [docs/errors.md](docs/errors.md).\n\n## Contributing · Security · License\n\n- **Contributing** — issues and feature requests are welcome; **pull requests\n  are by prior agreement** (this repo runs a strictly linear increment process).\n  See [CONTRIBUTING.md](CONTRIBUTING.md).\n- **Security** — found a way to leak the token or a byte on stdout? Do not open\n  a public issue; see [SECURITY.md](SECURITY.md) for the private channel and\n  threat model.\n- **License** — [MIT](LICENSE).\n- **For AI coding agents** — the entry-point contract (invariants, pinned deps,\n  pre-merge checklist) lives in [CLAUDE.md](CLAUDE.md).\n\n`CONTRIBUTING.md`, `SECURITY.md` and `CLAUDE.md` live on GitHub only — like\n`examples/`, they are not in the npm tarball. `LICENSE` is the exception: it\nships inside the package.\n",
  "bytes": 11567,
  "sha": "b763e57ff2f028ffd4859d308e853c3d8549d412921cfed9f97da6a9caf0e423",
  "repo_slug": "yolkins/weeek-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_yolkins_weeek_mcp_4e056d60/readme"
}