@omnicoreos/planka-mcp 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/.agents/plugins/marketplace.json +20 -0
  2. package/.claude-plugin/marketplace.json +26 -0
  3. package/.claude-plugin/plugin.json +45 -0
  4. package/.codex-plugin/mcp.json +16 -0
  5. package/.codex-plugin/plugin.json +27 -0
  6. package/.mcp.json +17 -0
  7. package/CHANGELOG.md +510 -0
  8. package/README.es.md +294 -55
  9. package/README.md +293 -55
  10. package/dist/cli/init.d.ts +101 -0
  11. package/dist/cli/init.d.ts.map +1 -0
  12. package/dist/cli/init.js +481 -0
  13. package/dist/cli/init.js.map +1 -0
  14. package/dist/client.d.ts +32 -4
  15. package/dist/client.d.ts.map +1 -1
  16. package/dist/client.js +106 -32
  17. package/dist/client.js.map +1 -1
  18. package/dist/config/policy.d.ts +82 -0
  19. package/dist/config/policy.d.ts.map +1 -0
  20. package/dist/config/policy.js +226 -0
  21. package/dist/config/policy.js.map +1 -0
  22. package/dist/errors.d.ts +5 -1
  23. package/dist/errors.d.ts.map +1 -1
  24. package/dist/errors.js +62 -5
  25. package/dist/errors.js.map +1 -1
  26. package/dist/identity.generated.d.ts +2 -1
  27. package/dist/identity.generated.d.ts.map +1 -1
  28. package/dist/identity.generated.js +2 -1
  29. package/dist/identity.generated.js.map +1 -1
  30. package/dist/index.js +85 -10
  31. package/dist/index.js.map +1 -1
  32. package/dist/instructions.d.ts +21 -0
  33. package/dist/instructions.d.ts.map +1 -0
  34. package/dist/instructions.js +37 -0
  35. package/dist/instructions.js.map +1 -0
  36. package/dist/operations/actions.d.ts +654 -0
  37. package/dist/operations/actions.d.ts.map +1 -0
  38. package/dist/operations/actions.js +154 -0
  39. package/dist/operations/actions.js.map +1 -0
  40. package/dist/operations/archive.d.ts +28 -0
  41. package/dist/operations/archive.d.ts.map +1 -0
  42. package/dist/operations/archive.js +74 -0
  43. package/dist/operations/archive.js.map +1 -0
  44. package/dist/operations/attachments.d.ts +1 -1
  45. package/dist/operations/attachments.d.ts.map +1 -1
  46. package/dist/operations/attachments.js +3 -1
  47. package/dist/operations/attachments.js.map +1 -1
  48. package/dist/operations/board-id.d.ts +1 -1
  49. package/dist/operations/board-id.d.ts.map +1 -1
  50. package/dist/operations/board-id.js +13 -7
  51. package/dist/operations/board-id.js.map +1 -1
  52. package/dist/operations/boards.d.ts +96 -19
  53. package/dist/operations/boards.d.ts.map +1 -1
  54. package/dist/operations/boards.js +377 -93
  55. package/dist/operations/boards.js.map +1 -1
  56. package/dist/operations/card-brief.d.ts +91 -0
  57. package/dist/operations/card-brief.d.ts.map +1 -0
  58. package/dist/operations/card-brief.js +79 -0
  59. package/dist/operations/card-brief.js.map +1 -0
  60. package/dist/operations/cards.d.ts +34 -9
  61. package/dist/operations/cards.d.ts.map +1 -1
  62. package/dist/operations/cards.js +60 -14
  63. package/dist/operations/cards.js.map +1 -1
  64. package/dist/operations/comments.d.ts +61 -4
  65. package/dist/operations/comments.d.ts.map +1 -1
  66. package/dist/operations/comments.js +91 -8
  67. package/dist/operations/comments.js.map +1 -1
  68. package/dist/operations/duplicate.d.ts +16 -0
  69. package/dist/operations/duplicate.d.ts.map +1 -0
  70. package/dist/operations/duplicate.js +43 -0
  71. package/dist/operations/duplicate.js.map +1 -0
  72. package/dist/operations/labels.d.ts +1 -1
  73. package/dist/operations/labels.d.ts.map +1 -1
  74. package/dist/operations/labels.js +7 -4
  75. package/dist/operations/labels.js.map +1 -1
  76. package/dist/operations/lists.d.ts +63 -1
  77. package/dist/operations/lists.d.ts.map +1 -1
  78. package/dist/operations/lists.js +97 -2
  79. package/dist/operations/lists.js.map +1 -1
  80. package/dist/operations/members.d.ts +39 -0
  81. package/dist/operations/members.d.ts.map +1 -0
  82. package/dist/operations/members.js +107 -0
  83. package/dist/operations/members.js.map +1 -0
  84. package/dist/operations/projects.d.ts +16 -0
  85. package/dist/operations/projects.d.ts.map +1 -1
  86. package/dist/operations/projects.js +54 -9
  87. package/dist/operations/projects.js.map +1 -1
  88. package/dist/operations/tasks.d.ts +1 -1
  89. package/dist/operations/tasks.d.ts.map +1 -1
  90. package/dist/operations/tasks.js +5 -3
  91. package/dist/operations/tasks.js.map +1 -1
  92. package/dist/operations/users.d.ts +123 -0
  93. package/dist/operations/users.d.ts.map +1 -0
  94. package/dist/operations/users.js +180 -0
  95. package/dist/operations/users.js.map +1 -0
  96. package/dist/operations/verify.d.ts +84 -0
  97. package/dist/operations/verify.d.ts.map +1 -0
  98. package/dist/operations/verify.js +124 -0
  99. package/dist/operations/verify.js.map +1 -0
  100. package/dist/prompts.d.ts +48 -0
  101. package/dist/prompts.d.ts.map +1 -0
  102. package/dist/prompts.js +155 -0
  103. package/dist/prompts.js.map +1 -0
  104. package/dist/resources.d.ts +38 -0
  105. package/dist/resources.d.ts.map +1 -0
  106. package/dist/resources.js +127 -0
  107. package/dist/resources.js.map +1 -0
  108. package/dist/schemas/entities.d.ts +115 -24
  109. package/dist/schemas/entities.d.ts.map +1 -1
  110. package/dist/schemas/entities.js +48 -0
  111. package/dist/schemas/entities.js.map +1 -1
  112. package/dist/schemas/requests.d.ts +121 -46
  113. package/dist/schemas/requests.d.ts.map +1 -1
  114. package/dist/schemas/requests.js +57 -12
  115. package/dist/schemas/requests.js.map +1 -1
  116. package/dist/schemas/responses.d.ts +541 -186
  117. package/dist/schemas/responses.d.ts.map +1 -1
  118. package/dist/schemas/responses.js +13 -2
  119. package/dist/schemas/responses.js.map +1 -1
  120. package/dist/tools/activity.d.ts +150 -0
  121. package/dist/tools/activity.d.ts.map +1 -0
  122. package/dist/tools/activity.js +198 -0
  123. package/dist/tools/activity.js.map +1 -0
  124. package/dist/tools/annotations.d.ts +52 -0
  125. package/dist/tools/annotations.d.ts.map +1 -0
  126. package/dist/tools/annotations.js +214 -0
  127. package/dist/tools/annotations.js.map +1 -0
  128. package/dist/tools/attachments.d.ts +28 -4
  129. package/dist/tools/attachments.d.ts.map +1 -1
  130. package/dist/tools/attachments.js +53 -34
  131. package/dist/tools/attachments.js.map +1 -1
  132. package/dist/tools/card-ops.d.ts +232 -0
  133. package/dist/tools/card-ops.d.ts.map +1 -0
  134. package/dist/tools/card-ops.js +333 -0
  135. package/dist/tools/card-ops.js.map +1 -0
  136. package/dist/tools/cards.d.ts +90 -8
  137. package/dist/tools/cards.d.ts.map +1 -1
  138. package/dist/tools/cards.js +411 -128
  139. package/dist/tools/cards.js.map +1 -1
  140. package/dist/tools/comments.d.ts +226 -22
  141. package/dist/tools/comments.d.ts.map +1 -1
  142. package/dist/tools/comments.js +163 -134
  143. package/dist/tools/comments.js.map +1 -1
  144. package/dist/tools/dispatch.d.ts +47 -0
  145. package/dist/tools/dispatch.d.ts.map +1 -0
  146. package/dist/tools/dispatch.js +63 -0
  147. package/dist/tools/dispatch.js.map +1 -0
  148. package/dist/tools/guard.d.ts +9 -0
  149. package/dist/tools/guard.d.ts.map +1 -0
  150. package/dist/tools/guard.js +20 -0
  151. package/dist/tools/guard.js.map +1 -0
  152. package/dist/tools/index.d.ts +748 -450
  153. package/dist/tools/index.d.ts.map +1 -1
  154. package/dist/tools/index.js +136 -17
  155. package/dist/tools/index.js.map +1 -1
  156. package/dist/tools/labels.d.ts +213 -18
  157. package/dist/tools/labels.d.ts.map +1 -1
  158. package/dist/tools/labels.js +218 -203
  159. package/dist/tools/labels.js.map +1 -1
  160. package/dist/tools/lists.d.ts +222 -15
  161. package/dist/tools/lists.d.ts.map +1 -1
  162. package/dist/tools/lists.js +175 -156
  163. package/dist/tools/lists.js.map +1 -1
  164. package/dist/tools/members.d.ts +128 -0
  165. package/dist/tools/members.d.ts.map +1 -0
  166. package/dist/tools/members.js +150 -0
  167. package/dist/tools/members.js.map +1 -0
  168. package/dist/tools/navigation.d.ts +22 -2
  169. package/dist/tools/navigation.d.ts.map +1 -1
  170. package/dist/tools/navigation.js +60 -15
  171. package/dist/tools/navigation.js.map +1 -1
  172. package/dist/tools/queries.d.ts +196 -166
  173. package/dist/tools/queries.d.ts.map +1 -1
  174. package/dist/tools/queries.js +125 -155
  175. package/dist/tools/queries.js.map +1 -1
  176. package/dist/tools/tasks.d.ts +26 -6
  177. package/dist/tools/tasks.d.ts.map +1 -1
  178. package/dist/tools/tasks.js +110 -55
  179. package/dist/tools/tasks.js.map +1 -1
  180. package/dist/tools/users.d.ts +130 -0
  181. package/dist/tools/users.d.ts.map +1 -0
  182. package/dist/tools/users.js +165 -0
  183. package/dist/tools/users.js.map +1 -0
  184. package/docs/planka-2x-gotchas.md +121 -5
  185. package/docs/tools.md +771 -187
  186. package/docs/troubleshooting.md +137 -5
  187. package/hooks/hooks.json +15 -0
  188. package/hooks/preflight.mjs +100 -0
  189. package/package.json +6 -1
  190. package/scripts/setup.sh +8 -26
  191. package/scripts/sync-identity.mjs +55 -1
  192. package/server.json +87 -6
  193. package/tests/smoke/planka-smoke.mjs +512 -72
  194. package/workflow/skills/planka-close-card/SKILL.md +18 -5
  195. package/workflow/skills/planka-orchestrator/SKILL.md +36 -7
package/README.es.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # planka-mcp
2
2
 
3
- Manejá un tablero Planka 2.x desde Claude Code mediante 24 tools MCP.
3
+ Manejá un tablero Planka 2.x desde Claude Code mediante 41 tools MCP.
4
4
  Las escrituras se releen y verifican: un éxito informado coincide con el board.
5
5
  Un flujo opcional convierte ese board en memoria durable para el trabajo con agentes.
6
6
 
@@ -30,53 +30,87 @@ Claude Code carga los servidores MCP al iniciar una sesión, así que reinicialo
30
30
 
31
31
  ## Requisitos
32
32
 
33
- - Node.js 18 o posterior y `npm`
33
+ - Node.js 18 o posterior y `npx`
34
34
  - Una instancia Planka 2.x accesible
35
35
  - Un usuario dedicado de Planka que pueda ver el proyecto y board elegidos
36
- - Claude Code instalado y disponible como `claude`
36
+ - Un cliente MCP: Claude Code, Codex CLI, Cursor o VS Code
37
37
  - Rol de project manager sólo si el setup debe crear un board
38
38
 
39
39
  Funciona en Linux y macOS. Bun no es un requisito.
40
40
 
41
- ## Instalación en 5 minutos
41
+ ## Instalación
42
42
 
43
- Cloná el repo y ejecutá el instalador guiado:
43
+ Un comando por cliente. Todos corren el paquete publicado con `npx`: no hay que
44
+ clonar nada ni compilar nada.
45
+
46
+ | Cliente | One-liner |
47
+ |---|---|
48
+ | **Claude Code** (plugin: MCP + skills + preflight) | `/plugin marketplace add omnicoreos/planka-mcp` y después `/plugin install planka@planka-mcp` |
49
+ | **Claude Code** (sólo el server) | `claude mcp add --scope user --transport stdio planka --env PLANKA_BASE_URL=https://planka.example.com --env PLANKA_API_KEY=<key> -- npx -y @omnicoreos/planka-mcp` |
50
+ | **Codex CLI** | `codex mcp add planka -- npx -y @omnicoreos/planka-mcp` (después el bloque `env`, más abajo) |
51
+ | **Cursor** | [![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](cursor://anysphere.cursor-deeplink/mcp/install?name=planka&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBvbW5pY29yZW9zL3BsYW5rYS1tY3AiXSwiZW52Ijp7IlBMQU5LQV9CQVNFX1VSTCI6Imh0dHBzOi8vcGxhbmthLmV4YW1wbGUuY29tIiwiUExBTktBX0FQSV9LRVkiOiI8eW91ci1wbGFua2EtYXBpLWtleT4ifX0=) |
52
+ | **VS Code** | [![Add to VS Code](https://img.shields.io/badge/VS_Code-Add_planka-0098FF)](https://insiders.vscode.dev/redirect/mcp/install?name=planka&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40omnicoreos%2Fplanka-mcp%22%5D%2C%22env%22%3A%7B%22PLANKA_BASE_URL%22%3A%22https%3A%2F%2Fplanka.example.com%22%2C%22PLANKA_API_KEY%22%3A%22%3Cyour-planka-api-key%3E%22%7D%2C%22type%22%3A%22stdio%22%7D) |
53
+ | **Cualquiera, guiado** | `npx @omnicoreos/planka-mcp init --client claude\|codex\|cursor\|vscode\|print` |
54
+
55
+ Los links de Cursor y VS Code llevan una key de ejemplo, nunca una real: un
56
+ deeplink queda en el historial del navegador. Los dos clientes piden la
57
+ credencial ellos mismos — VS Code con un input `promptString`, así el
58
+ `.vscode/mcp.json` commiteado no tiene ningún secreto.
59
+
60
+ ### `planka-mcp init`
44
61
 
45
62
  ```bash
46
- git clone https://github.com/omnicoreos/planka-mcp.git
47
- cd planka-mcp
48
- ./scripts/setup.sh
63
+ npx @omnicoreos/planka-mcp init --client claude # claude mcp add, scope user
64
+ npx @omnicoreos/planka-mcp init --client codex # agrega el bloque a ~/.codex/config.toml
65
+ npx @omnicoreos/planka-mcp init --client cursor # mergea ~/.cursor/mcp.json (0600) + imprime el deeplink
66
+ npx @omnicoreos/planka-mcp init --client vscode # escribe .vscode/mcp.json pidiendo el secreto
67
+ npx @omnicoreos/planka-mcp init --client print # imprime todos los snippets, no escribe nada
49
68
  ```
50
69
 
51
- El setup pide la URL de Planka, email o usuario del agente y password. Valida esas
52
- credenciales antes de escribir configuración, permite elegir o crear un board y
53
- ejecuta un smoke real de crear, etiquetar, comentar y borrar una card.
70
+ Con terminal pregunta lo que le falta; `--base-url`, `--api-key`, `--board`,
71
+ `--email` y `--password` lo vuelven no interactivo. Antes de escribir nada hace
72
+ un `GET /api/users/me` autenticado: eso es lo que detecta una base URL que
73
+ apunta al SPA en vez de a la API, o una credencial que nunca funcionó.
74
+ `--dry-run` imprime el cambio exacto y no toca nada.
54
75
 
55
- Ofrece dos formas de configurar Claude Code:
76
+ Todos los emisores mergean: si ya existe una entrada `planka` la reporta y la
77
+ deja como está, y conserva los demás servers del mismo archivo. Lo que escribe
78
+ en tu home queda con modo `0600`.
56
79
 
57
- | Opción | Cuándo usarla | Dónde vive |
58
- |---|---|---|
59
- | `claude mcp add` | Querés la instalación personal más simple | Configuración de usuario de Claude Code |
60
- | `.mcp.json` de proyecto | Un equipo debe compartir la entrada del servidor | `<TU_PROYECTO>/.mcp.json`, se puede commitear |
80
+ `./scripts/setup.sh` es un alias de `init --client claude`. El instalador
81
+ guiado viejo — el que además elige o crea un board y corre el smoke completo de
82
+ create/label/comment/delete sigue siendo `node scripts/setup.mjs`.
61
83
 
62
- En ambos casos, el setup guarda las credenciales fuera de Git en
63
- `~/.config/planka-mcp/config.json` con modo `0600` y crea el launcher privado
64
- `~/.local/bin/planka-mcp`. La configuración compartida no contiene el password:
84
+ ### El plugin de Claude Code
65
85
 
66
- ```json
67
- {
68
- "mcpServers": {
69
- "planka": {
70
- "type": "stdio",
71
- "command": "${HOME}/.local/bin/planka-mcp",
72
- "args": []
73
- }
74
- }
75
- }
86
+ El plugin instala el MCP server, las dos skills del workflow y un preflight de
87
+ `SessionStart` que detecta una credencial faltante o una base URL `http://`
88
+ antes de la primera tool call:
89
+
90
+ ```text
91
+ /plugin marketplace add omnicoreos/planka-mcp
92
+ /plugin install planka@planka-mcp
76
93
  ```
77
94
 
78
- Podés volver a ejecutar el instalador. Actualiza la entrada `planka`, reutiliza las
79
- listas y labels existentes del método y borra la card temporal del smoke test.
95
+ Claude Code pide la base URL y la API key al habilitar el plugin y guarda la
96
+ key en el keychain del sistema, no en `settings.json`. Codex lee el mismo
97
+ repositorio vía `.codex-plugin/plugin.json` y `.agents/plugins/marketplace.json`.
98
+
99
+ Los plugins no pueden traer reglas de permisos, y eso es lo único que no viaja:
100
+ usá `PLANKA_READ_ONLY` y `PLANKA_DISABLED_TOOLS` (más abajo) en vez de una deny
101
+ list del cliente — funcionan en todos los runtimes y no cuestan contexto.
102
+
103
+ ### Bloque `env` de Codex
104
+
105
+ `codex mcp add` no toma credenciales, así que van en `~/.codex/config.toml` (o
106
+ dejá que lo haga `init --client codex`):
107
+
108
+ ```toml
109
+ [mcp_servers.planka]
110
+ command = "npx"
111
+ args = ["-y", "@omnicoreos/planka-mcp"]
112
+ env = { PLANKA_BASE_URL = "https://planka.example.com", PLANKA_API_KEY = "<key>" }
113
+ ```
80
114
 
81
115
  ## Verificá que funciona
82
116
 
@@ -87,8 +121,11 @@ claude mcp list
87
121
  claude mcp get planka
88
122
  ```
89
123
 
90
- Después cerrá y volvé a abrir Claude Code por completo. Si elegiste `.mcp.json`,
91
- abrí Claude Code dentro de ese proyecto y aprobá el servidor de proyecto cuando lo pida.
124
+ Después cerrá y volvé a abrir Claude Code por completo los MCP se cargan al
125
+ arrancar la sesión. Si configuraste el server en el `.mcp.json` de un proyecto,
126
+ abrí Claude Code dentro de ese proyecto y aprobá el servidor cuando lo pida. Si
127
+ instalaste el plugin, `/plugin` lo muestra, y su pestaña **Errors** muestra un
128
+ server que no arrancó.
92
129
 
93
130
  Pedile a Claude Code:
94
131
 
@@ -100,7 +137,145 @@ Mostrame mis proyectos y boards de Planka. En la lista Pending, creá una card l
100
137
  Si Claude no ve las tools, reinicialo primero y después seguí
101
138
  [Solución de problemas](docs/troubleshooting.md).
102
139
 
103
- ## Las 24 tools
140
+ ## Autenticación
141
+
142
+ Hay dos formas de autenticarse, y son mutuamente excluyentes: setear las dos es
143
+ un error de configuración, porque Planka lee `Authorization` primero e ignora en
144
+ silencio el `x-api-key` cuando llegan juntos.
145
+
146
+ | | `PLANKA_API_KEY` (recomendada) | `PLANKA_AGENT_EMAIL` + `PLANKA_AGENT_PASSWORD` |
147
+ |---|---|---|
148
+ | Se manda como | `X-Api-Key: <prefijo>_<secreto>` en cada request | `POST /api/access-tokens` y después `Authorization: Bearer` |
149
+ | Vuelta de login | ninguna | una por sesión, refrescada cada 25 minutos |
150
+ | Rate limit de sign-in | no le aplica | 10 logins por identidad cada 60 s — se toca rápido cuando arrancan varios agentes a la vez |
151
+ | Password en disco | no hay | sí, en el archivo de configuración del cliente |
152
+ | Descarga de adjuntos | funciona | funciona |
153
+
154
+ ### Receta: usuario agente acotado con API key
155
+
156
+ Cuatro pasos, los corre un **admin** de Planka. El resultado es un usuario que
157
+ sólo puede ver los boards que vos nombres — lo enforcea Planka, no este server.
158
+
159
+ 1. **Crear el usuario con el rol global más bajo.** En la UI de Planka:
160
+ *Administración → Usuarios → Agregar usuario*, rol **`boardUser`**. Un
161
+ `boardUser` no puede crear proyectos ni darse membresías a sí mismo.
162
+
163
+ 2. **Darle membresía en los boards donde debe trabajar**, y sólo en esos:
164
+
165
+ ```bash
166
+ curl -X POST "$PLANKA_URL/api/boards/<BOARD_ID>/board-memberships" \
167
+ -H "Authorization: Bearer $ADMIN_TOKEN" \
168
+ -H "Content-Type: application/json" \
169
+ -d '{"userId":"<USER_ID>","role":"editor"}'
170
+ ```
171
+
172
+ Usá `"role":"viewer"` para un agente que tiene que leer y comentar pero nunca
173
+ crear ni mover cards. Después de eso, `GET /api/projects` devuelve sólo los
174
+ proyectos derivados de esas membresías; cualquier otro board responde 404.
175
+
176
+ 3. **Emitir la API key** (endpoint admin-only). La key se muestra **una sola
177
+ vez**, en `included.apiKey`; Planka guarda sólo su hash y su prefijo:
178
+
179
+ ```bash
180
+ curl -X POST "$PLANKA_URL/api/users/<USER_ID>/api-key" \
181
+ -H "Authorization: Bearer $ADMIN_TOKEN"
182
+ ```
183
+
184
+ 4. **Configurar el server con la key y nada más.** Sacar `PLANKA_AGENT_EMAIL` y
185
+ `PLANKA_AGENT_PASSWORD`:
186
+
187
+ ```json
188
+ {
189
+ "mcpServers": {
190
+ "planka": {
191
+ "command": "npx",
192
+ "args": ["-y", "@omnicoreos/planka-mcp"],
193
+ "env": {
194
+ "PLANKA_BASE_URL": "https://planka.example.com",
195
+ "PLANKA_API_KEY": "abcd1234_0123456789abcdef0123456789abcdef"
196
+ }
197
+ }
198
+ }
199
+ }
200
+ ```
201
+
202
+ Rotar una key es repetir el paso 3: emitir una nueva invalida la anterior.
203
+
204
+ ## Acotar el server
205
+
206
+ Una API key hereda los permisos de su usuario — no tiene scopes propios. Por eso
207
+ el acotado se hace en tres capas, y cada una cubre algo que las otras no.
208
+
209
+ ```
210
+ Capa 1 · PLANKA la única que el agente no puede esquivar hablando
211
+ boardUser + board memberships + la API key de arriba
212
+ ⇒ todo lo que esté fuera de los boards permitidos es 404/403 en la API
213
+
214
+ Capa 2 · ESTE SERVER ergonomía, y defensa contra el propio agente
215
+ las variables de entorno de abajo; viajan con el paquete,
216
+ así que Claude Code, Codex y Cursor reciben las mismas reglas
217
+
218
+ Capa 3 · EL CLIENTE sobrevive a un downgrade de este paquete
219
+ reglas deny/ask de .claude/settings.json, enabled_tools/disabled_tools de Codex
220
+ ```
221
+
222
+ La capa 1 es la única con enforcement real, pero no puede expresar "no llames a
223
+ `planka_get_board`, cuesta cuarenta veces más contexto". La capa 2 sí, y llega a
224
+ todos los runtimes. La capa 3 sigue valiendo aunque este paquete quede pineado en
225
+ una versión vieja.
226
+
227
+ ### Capa 2: las variables de entorno
228
+
229
+ | Variable | Valor | Qué hace |
230
+ |---|---|---|
231
+ | `PLANKA_DEFAULT_BOARD_ID` | un board id | `boardId` pasa a ser opcional en todas las tools que lo toman, y cae en este board. Ahorra el `planka_get_structure` que el agente hace sólo para recuperar un id que nunca cambia |
232
+ | `PLANKA_ALLOWED_BOARD_IDS` | board ids separados por coma | Los boards fuera de la lista se filtran de `planka_get_structure`, y cualquier llamada que nombre uno se rechaza antes de que el request salga del proceso |
233
+ | `PLANKA_ALLOWED_PROJECT_IDS` | project ids separados por coma | Lo mismo, un nivel más arriba |
234
+ | `PLANKA_READ_ONLY` | `true` o `1` | Las veintisiete tools de escritura desaparecen del `tools/list` y se rechazan si igual las llaman |
235
+ | `PLANKA_HIDE_DEPRECATED` | `true` o `1` | Saca del `tools/list` las siete tools deprecadas (`manage_labels`, `manage_lists`, `manage_comment`, `add_comment`, `get_board`, `list_cards`, `list_lists`) y ahorra ~6,8 kB de contexto. Siguen siendo llamables, así que un tool list cacheado sigue funcionando |
236
+ | `PLANKA_DISABLED_TOOLS` | nombres de tool separados por coma | Apaga tools puntuales. El prefijo `planka_` es opcional: `get_board` y `planka_get_board` son lo mismo |
237
+ | `PLANKA_PROTECTED_LIST_IDS` | list ids separados por coma | Rechaza crear o mover cards **hacia** esas listas, editar o borrar las listas mismas, y mover, archivar o borrar las cards **hacia afuera** de ellas |
238
+ | `PLANKA_SUMMARY_DECISION_LISTS` | nombres o ids de columna separados por coma | De qué columnas trae cards `planka_board_summary` cuando la llamada no lo dice. Sin setear: no devuelve cards, sólo la forma del board |
239
+ | `PLANKA_SUMMARY_HIGHLIGHT_LABEL` | un nombre de label | El label que marca una card como desbloqueada en `planka_board_summary`. Sin setear: no se resalta nada |
240
+ | `PLANKA_MCP_PREFLIGHT` | `full` | La lee sólo el hook SessionStart del plugin: además levanta el binario del server y espera su banner de stdio. Apagado por default, porque con caché fría cuesta una descarga de `npx` |
241
+ | `PLANKA_MCP_COMMAND` | un comando | La lee el mismo hook: el comando a levantar en lugar de `npx -y @omnicoreos/planka-mcp` (un checkout local, un binario pineado) |
242
+
243
+ **El server no trae vocabulario de board propio.** Los nombres de columna y el
244
+ label de "listo" son tuyos, en tu idioma: esas dos variables son cómo un deploy
245
+ le cuenta al resumen cómo es su board. Un hint que no matchea nada vuelve en
246
+ `warnings`, nunca como respuesta vacía.
247
+
248
+ ```json
249
+ "PLANKA_SUMMARY_DECISION_LISTS": "decision,probalo,miralo",
250
+ "PLANKA_SUMMARY_HIGHLIGHT_LABEL": "decidido"
251
+ ```
252
+
253
+ **Las allowlists son por id, nunca por nombre.** Cualquier editor del board puede
254
+ renombrarlo, así que una allowlist por nombre se saltea con una edición. Los ids
255
+ de Planka son estables.
256
+
257
+ Dos aclaraciones sobre qué son y qué no:
258
+
259
+ - `PLANKA_READ_ONLY` esconde tools; no vuelve read-only a la cuenta. Combinalo con
260
+ `"role":"viewer"` en el paso 2 de arriba si eso es lo que realmente necesitás.
261
+ - `PLANKA_PROTECTED_LIST_IDS` es un guard-rail, no un permiso: Planka no tiene
262
+ derechos por lista. Es la herramienta correcta para "no muevas cards a
263
+ *Mergeado* sola", y la equivocada para cualquier cosa crítica de seguridad.
264
+
265
+ Ejemplo — un agente que lee un board y comenta, y nada más:
266
+
267
+ ```json
268
+ "env": {
269
+ "PLANKA_BASE_URL": "https://planka.example.com",
270
+ "PLANKA_API_KEY": "abcd1234_0123456789abcdef0123456789abcdef",
271
+ "PLANKA_DEFAULT_BOARD_ID": "1234567890123456789",
272
+ "PLANKA_ALLOWED_BOARD_IDS": "1234567890123456789",
273
+ "PLANKA_DISABLED_TOOLS": "get_board",
274
+ "PLANKA_PROTECTED_LIST_IDS": "9876543210987654321"
275
+ }
276
+ ```
277
+
278
+ ## Las 41 tools
104
279
 
105
280
  Los IDs son strings. Empezá con `planka_get_structure` y usá los IDs que devuelve
106
281
  Planka; no los adivines.
@@ -108,41 +283,105 @@ Planka; no los adivines.
108
283
  | Tool | Qué hace |
109
284
  |---|---|
110
285
  | `planka_get_structure` | Lista proyectos, boards y listas visibles |
111
- | `planka_get_board` | Lee un board con listas, cards, labels y conteos opcionales de tareas |
112
- | `planka_list_lists` | Lista las columnas de un board con sus conteos de cards, sin las cards |
113
- | `planka_board_summary` | Resumen en una llamada: columnas, labels y las cards que esperan una decisión |
114
- | `planka_list_cards` | Lee una sola columna, paginada y sin descripciones por defecto |
115
- | `planka_find_cards` | Busca cards de un board por label, texto o columna |
286
+ | `planka_get_board` | **Deprecada.** Lee el board entero, hasta `limit` cards |
287
+ | `planka_board_summary` | Resumen en una llamada: columnas con conteos, labels con ids y, si se piden, las cards de las columnas nombradas |
288
+ | `planka_find_cards` | La única lectura de cards: una columna (`listId`), o un board buscado por texto, label o miembro |
289
+ | `planka_list_lists` | **Deprecada.** Alias de `planka_board_summary` con `cardsFrom: []` |
290
+ | `planka_list_cards` | **Deprecada.** Alias de `planka_find_cards` con `listId` |
116
291
  | `planka_create_card` | Crea una card y puede agregar tareas y labels |
117
- | `planka_get_card` | Lee el detalle completo de una card |
292
+ | `planka_get_card` | Digest de una card; `detail: "full"` y `withComments` a pedido |
118
293
  | `planka_update_card` | Actualiza título, descripción, vencimiento o estado de completado |
119
294
  | `planka_move_card` | Mueve una card a otra lista o posición |
120
295
  | `planka_delete_card` | Borra una card permanentemente |
121
296
  | `planka_create_tasks` | Agrega tareas de checklist a una card |
122
297
  | `planka_update_task` | Renombra o completa una tarea |
123
298
  | `planka_delete_task` | Borra una tarea |
124
- | `planka_manage_labels` | Crea, actualiza o borra labels del board |
299
+ | `planka_create_label` | Crea un label en el board |
300
+ | `planka_update_label` | Renombra un label o le cambia el color |
301
+ | `planka_delete_label` | Borra un label del board y de todas las cards que lo tenían |
302
+ | `planka_manage_labels` | **Deprecada.** Alias que rutea `action` a las tres de arriba |
125
303
  | `planka_set_card_labels` | Agrega o quita labels y verifica el estado final |
126
- | `planka_add_comment` | Agrega un comentario por el endpoint dedicado de Planka 2.x |
127
- | `planka_get_comments` | Lee comentarios por el endpoint dedicado |
128
- | `planka_manage_comment` | Edita o borra un comentario existente |
129
- | `planka_manage_lists` | Crea, actualiza o borra listas del board |
304
+ | `planka_create_comment` | Agrega un comentario por el endpoint dedicado de Planka 2.x |
305
+ | `planka_get_comments` | Comentarios paginados (`limit`, `beforeId`, `all`) |
306
+ | `planka_update_comment` | Reescribe un comentario existente |
307
+ | `planka_delete_comment` | Borra un comentario |
308
+ | `planka_add_comment` | **Deprecada.** Alias de `planka_create_comment` |
309
+ | `planka_manage_comment` | **Deprecada.** Alias que rutea `action` a update/delete |
310
+ | `planka_create_list` | Crea una lista (columna) en un board |
311
+ | `planka_update_list` | Renombra, reubica o reclasifica una columna |
312
+ | `planka_delete_list` | Borra una columna **y todas sus cards** |
313
+ | `planka_manage_lists` | **Deprecada.** Alias que rutea `action` a las tres de arriba |
130
314
  | `planka_add_attachment` | Sube un archivo local a una card y verifica que quedó |
131
315
  | `planka_get_attachments` | Lista los adjuntos de una card con tipo, tamaño y URL de descarga |
132
316
  | `planka_view_attachment` | Devuelve el contenido de un adjunto; las imágenes vuelven visibles |
133
317
  | `planka_delete_attachment` | Borra un adjunto |
134
-
135
- `planka_get_board` devuelve el board entero, que suele ser más de lo que la
136
- pregunta necesita. Las cuatro lecturas acotadas responden preguntas más chicas y
137
- devuelven mucho menos texto: medido sobre un board de 100 cards,
138
- `planka_list_lists` devolvió 39 veces menos que `planka_get_board`,
139
- `planka_board_summary` 16 veces menos, y `planka_find_cards` entre 18 y 56 veces
140
- menos según el filtro. Además informan `total`, `returned` y `hasMore`, así que
141
- una respuesta recortada nunca parece completa.
318
+ | `planka_card_history` | El historial de una card en líneas legibles: creada, movida, asignada, tareas completadas |
319
+ | `planka_board_activity` | Qué se movió en un board desde una fecha, agrupado por card |
320
+ | `planka_set_card_members` | Asigna o desasigna personas y verifica la membresía final |
321
+ | `planka_list_users` | Quiénes pueden ser asignados, con fallback al board |
322
+ | `planka_whoami` | La cuenta del servidor, su rol en el board, la versión de Planka y la policy |
323
+ | `planka_duplicate_card` | Copia una card con sus tareas, labels y miembros |
324
+ | `planka_archive_card` | Archiva una card en el archivo oculto del board, o la restaura |
325
+ | `planka_move_list_cards` | Mueve todas las cards de una columna a otra, con conteos |
326
+
327
+ Las lecturas siguen un principio: **digest chico por defecto, el detalle por
328
+ parámetros**. `planka_board_summary` abre una sesión en una llamada;
329
+ `planka_find_cards` con `listId` lee una columna entera en UNA request contra
330
+ `GET /api/lists/:id`, así que `total` es el tamaño real de la columna y la
331
+ respuesta trae `truncated: false`; la misma tool sin `listId` busca en el board.
332
+ Medido sobre el board de referencia de 185 cards, la deprecada
333
+ `planka_get_board` pasó de 56.112 a 17.215 caracteres y `planka_get_structure`
334
+ (`withLists: false`) de 696 a 203, y una columna de 159 cards pasó de cinco
335
+ requests a dos. Toda lectura informa `total`, `returned` y `hasMore`, así que
336
+ una respuesta recortada nunca parece completa, y toda lectura derivada del board
337
+ trae `excludesArchived: true` porque Planka deja archive y trash afuera del
338
+ board show.
142
339
 
143
340
  Todos los campos y un payload completo por tool están en la
144
341
  [referencia de tools](docs/tools.md).
145
342
 
343
+ ## Qué aprende el cliente al conectarse
344
+
345
+ El handshake `initialize` devuelve un texto corto de instrucciones del servidor:
346
+ cómo abrir una sesión, de dónde salen los IDs, por qué importan todos los
347
+ comentarios de una card, y que Planka contesta `404` donde quiere decir `403`.
348
+ Claude Code las mete en el system prompt de la sesión y Codex CLI las lee junto
349
+ con la lista de tools, así que la guía compartida se dice una vez en vez de
350
+ repetirse en 41 descripciones. Cada tool además publica un título de display y
351
+ los cuatro hints de comportamiento del MCP escritos explícitamente, en lugar de
352
+ heredar los defaults pesimistas de la spec, más las dos claves `_meta` que
353
+ Claude Code sí usa: confirmación forzada en las tools que borran datos y un tope
354
+ de salida más alto en `planka_view_attachment`. Detalle en
355
+ [Server instructions and annotations](docs/tools.md#server-instructions-and-annotations).
356
+
357
+ ## Resources y prompts
358
+
359
+ Dos superficies más, y las dos cuestan cero hasta que alguien las pide: por eso
360
+ la guía larga vive acá y no en las instructions, que se pagan en cada sesión.
361
+ Claude Code lee las dos; Cursor también; Codex no soporta ninguna, así que nada
362
+ de esto es imprescindible.
363
+
364
+ **Resources** sirven las guías que viajan dentro del paquete. En Claude Code se
365
+ traen con `@`, en Cursor desde el selector de resources:
366
+
367
+ | URI | Qué es |
368
+ |---|---|
369
+ | `planka://workflow/readme` | El workflow opcional del board: columnas, labels, quién mueve qué |
370
+ | `planka://workflow/board-template` | Las columnas y labels para crear en un board nuevo |
371
+ | `planka://workflow/skills/orchestrator` | La skill de director, tal cual |
372
+ | `planka://workflow/skills/close-card` | La skill de cierre, tal cual |
373
+ | `planka://gotchas/planka-2x` | Cómo se comporta Planka 2.x cuando una llamada contesta algo sin sentido |
374
+ | `planka://labels/colors` | Todos los colores que aceptan `planka_create_label` y `planka_update_label`, generados del schema |
375
+
376
+ **Prompts** son tres formas de arrancar; Claude Code las expone como
377
+ `/mcp__planka__<nombre>`:
378
+
379
+ | Prompt | Argumentos | Qué hace |
380
+ |---|---|---|
381
+ | `planka-open-session` | `boardId?`, `since?` | Summary, después lo que se movió, después las columnas que importan — en ese orden |
382
+ | `planka-close-card` | `cardId`, `listId?` | Leer el hilo entero, escribir un cierre honesto, mover la card y chequear `verified` |
383
+ | `planka-board-triage` | `boardId?`, `lists?` | Recorrer las columnas que esperan a una persona y volver cada card una pregunta |
384
+
146
385
  ## Flujo de agentes opcional
147
386
 
148
387
  El servidor MCP funciona por sí solo. El método opcional resuelve otro problema:
@@ -187,8 +426,8 @@ npm test
187
426
  ```
188
427
 
189
428
  El smoke real es opt-in porque modifica un board escribible y después limpia lo
190
- creado. Ejercita las 24 tools por stdio y contrasta cada escritura contra la API
191
- cruda de Planka: 64 checks con nombre.
429
+ creado. Ejercita las 41 tools por stdio y contrasta cada escritura contra la API
430
+ cruda de Planka: más de 90 checks con nombre.
192
431
 
193
432
  ```bash
194
433
  export PLANKA_BASE_URL="https://planka.example.com"