@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.
- package/.agents/plugins/marketplace.json +20 -0
- package/.claude-plugin/marketplace.json +26 -0
- package/.claude-plugin/plugin.json +45 -0
- package/.codex-plugin/mcp.json +16 -0
- package/.codex-plugin/plugin.json +27 -0
- package/.mcp.json +17 -0
- package/CHANGELOG.md +510 -0
- package/README.es.md +294 -55
- package/README.md +293 -55
- package/dist/cli/init.d.ts +101 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +481 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/client.d.ts +32 -4
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +106 -32
- package/dist/client.js.map +1 -1
- package/dist/config/policy.d.ts +82 -0
- package/dist/config/policy.d.ts.map +1 -0
- package/dist/config/policy.js +226 -0
- package/dist/config/policy.js.map +1 -0
- package/dist/errors.d.ts +5 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +62 -5
- package/dist/errors.js.map +1 -1
- package/dist/identity.generated.d.ts +2 -1
- package/dist/identity.generated.d.ts.map +1 -1
- package/dist/identity.generated.js +2 -1
- package/dist/identity.generated.js.map +1 -1
- package/dist/index.js +85 -10
- package/dist/index.js.map +1 -1
- package/dist/instructions.d.ts +21 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +37 -0
- package/dist/instructions.js.map +1 -0
- package/dist/operations/actions.d.ts +654 -0
- package/dist/operations/actions.d.ts.map +1 -0
- package/dist/operations/actions.js +154 -0
- package/dist/operations/actions.js.map +1 -0
- package/dist/operations/archive.d.ts +28 -0
- package/dist/operations/archive.d.ts.map +1 -0
- package/dist/operations/archive.js +74 -0
- package/dist/operations/archive.js.map +1 -0
- package/dist/operations/attachments.d.ts +1 -1
- package/dist/operations/attachments.d.ts.map +1 -1
- package/dist/operations/attachments.js +3 -1
- package/dist/operations/attachments.js.map +1 -1
- package/dist/operations/board-id.d.ts +1 -1
- package/dist/operations/board-id.d.ts.map +1 -1
- package/dist/operations/board-id.js +13 -7
- package/dist/operations/board-id.js.map +1 -1
- package/dist/operations/boards.d.ts +96 -19
- package/dist/operations/boards.d.ts.map +1 -1
- package/dist/operations/boards.js +377 -93
- package/dist/operations/boards.js.map +1 -1
- package/dist/operations/card-brief.d.ts +91 -0
- package/dist/operations/card-brief.d.ts.map +1 -0
- package/dist/operations/card-brief.js +79 -0
- package/dist/operations/card-brief.js.map +1 -0
- package/dist/operations/cards.d.ts +34 -9
- package/dist/operations/cards.d.ts.map +1 -1
- package/dist/operations/cards.js +60 -14
- package/dist/operations/cards.js.map +1 -1
- package/dist/operations/comments.d.ts +61 -4
- package/dist/operations/comments.d.ts.map +1 -1
- package/dist/operations/comments.js +91 -8
- package/dist/operations/comments.js.map +1 -1
- package/dist/operations/duplicate.d.ts +16 -0
- package/dist/operations/duplicate.d.ts.map +1 -0
- package/dist/operations/duplicate.js +43 -0
- package/dist/operations/duplicate.js.map +1 -0
- package/dist/operations/labels.d.ts +1 -1
- package/dist/operations/labels.d.ts.map +1 -1
- package/dist/operations/labels.js +7 -4
- package/dist/operations/labels.js.map +1 -1
- package/dist/operations/lists.d.ts +63 -1
- package/dist/operations/lists.d.ts.map +1 -1
- package/dist/operations/lists.js +97 -2
- package/dist/operations/lists.js.map +1 -1
- package/dist/operations/members.d.ts +39 -0
- package/dist/operations/members.d.ts.map +1 -0
- package/dist/operations/members.js +107 -0
- package/dist/operations/members.js.map +1 -0
- package/dist/operations/projects.d.ts +16 -0
- package/dist/operations/projects.d.ts.map +1 -1
- package/dist/operations/projects.js +54 -9
- package/dist/operations/projects.js.map +1 -1
- package/dist/operations/tasks.d.ts +1 -1
- package/dist/operations/tasks.d.ts.map +1 -1
- package/dist/operations/tasks.js +5 -3
- package/dist/operations/tasks.js.map +1 -1
- package/dist/operations/users.d.ts +123 -0
- package/dist/operations/users.d.ts.map +1 -0
- package/dist/operations/users.js +180 -0
- package/dist/operations/users.js.map +1 -0
- package/dist/operations/verify.d.ts +84 -0
- package/dist/operations/verify.d.ts.map +1 -0
- package/dist/operations/verify.js +124 -0
- package/dist/operations/verify.js.map +1 -0
- package/dist/prompts.d.ts +48 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +155 -0
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +38 -0
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +127 -0
- package/dist/resources.js.map +1 -0
- package/dist/schemas/entities.d.ts +115 -24
- package/dist/schemas/entities.d.ts.map +1 -1
- package/dist/schemas/entities.js +48 -0
- package/dist/schemas/entities.js.map +1 -1
- package/dist/schemas/requests.d.ts +121 -46
- package/dist/schemas/requests.d.ts.map +1 -1
- package/dist/schemas/requests.js +57 -12
- package/dist/schemas/requests.js.map +1 -1
- package/dist/schemas/responses.d.ts +541 -186
- package/dist/schemas/responses.d.ts.map +1 -1
- package/dist/schemas/responses.js +13 -2
- package/dist/schemas/responses.js.map +1 -1
- package/dist/tools/activity.d.ts +150 -0
- package/dist/tools/activity.d.ts.map +1 -0
- package/dist/tools/activity.js +198 -0
- package/dist/tools/activity.js.map +1 -0
- package/dist/tools/annotations.d.ts +52 -0
- package/dist/tools/annotations.d.ts.map +1 -0
- package/dist/tools/annotations.js +214 -0
- package/dist/tools/annotations.js.map +1 -0
- package/dist/tools/attachments.d.ts +28 -4
- package/dist/tools/attachments.d.ts.map +1 -1
- package/dist/tools/attachments.js +53 -34
- package/dist/tools/attachments.js.map +1 -1
- package/dist/tools/card-ops.d.ts +232 -0
- package/dist/tools/card-ops.d.ts.map +1 -0
- package/dist/tools/card-ops.js +333 -0
- package/dist/tools/card-ops.js.map +1 -0
- package/dist/tools/cards.d.ts +90 -8
- package/dist/tools/cards.d.ts.map +1 -1
- package/dist/tools/cards.js +411 -128
- package/dist/tools/cards.js.map +1 -1
- package/dist/tools/comments.d.ts +226 -22
- package/dist/tools/comments.d.ts.map +1 -1
- package/dist/tools/comments.js +163 -134
- package/dist/tools/comments.js.map +1 -1
- package/dist/tools/dispatch.d.ts +47 -0
- package/dist/tools/dispatch.d.ts.map +1 -0
- package/dist/tools/dispatch.js +63 -0
- package/dist/tools/dispatch.js.map +1 -0
- package/dist/tools/guard.d.ts +9 -0
- package/dist/tools/guard.d.ts.map +1 -0
- package/dist/tools/guard.js +20 -0
- package/dist/tools/guard.js.map +1 -0
- package/dist/tools/index.d.ts +748 -450
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +136 -17
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/labels.d.ts +213 -18
- package/dist/tools/labels.d.ts.map +1 -1
- package/dist/tools/labels.js +218 -203
- package/dist/tools/labels.js.map +1 -1
- package/dist/tools/lists.d.ts +222 -15
- package/dist/tools/lists.d.ts.map +1 -1
- package/dist/tools/lists.js +175 -156
- package/dist/tools/lists.js.map +1 -1
- package/dist/tools/members.d.ts +128 -0
- package/dist/tools/members.d.ts.map +1 -0
- package/dist/tools/members.js +150 -0
- package/dist/tools/members.js.map +1 -0
- package/dist/tools/navigation.d.ts +22 -2
- package/dist/tools/navigation.d.ts.map +1 -1
- package/dist/tools/navigation.js +60 -15
- package/dist/tools/navigation.js.map +1 -1
- package/dist/tools/queries.d.ts +196 -166
- package/dist/tools/queries.d.ts.map +1 -1
- package/dist/tools/queries.js +125 -155
- package/dist/tools/queries.js.map +1 -1
- package/dist/tools/tasks.d.ts +26 -6
- package/dist/tools/tasks.d.ts.map +1 -1
- package/dist/tools/tasks.js +110 -55
- package/dist/tools/tasks.js.map +1 -1
- package/dist/tools/users.d.ts +130 -0
- package/dist/tools/users.d.ts.map +1 -0
- package/dist/tools/users.js +165 -0
- package/dist/tools/users.js.map +1 -0
- package/docs/planka-2x-gotchas.md +121 -5
- package/docs/tools.md +771 -187
- package/docs/troubleshooting.md +137 -5
- package/hooks/hooks.json +15 -0
- package/hooks/preflight.mjs +100 -0
- package/package.json +6 -1
- package/scripts/setup.sh +8 -26
- package/scripts/sync-identity.mjs +55 -1
- package/server.json +87 -6
- package/tests/smoke/planka-smoke.mjs +512 -72
- package/workflow/skills/planka-close-card/SKILL.md +18 -5
- 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
|
|
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 `
|
|
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
|
|
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
|
|
41
|
+
## Instalación
|
|
42
42
|
|
|
43
|
-
|
|
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** | [](cursor://anysphere.cursor-deeplink/mcp/install?name=planka&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBvbW5pY29yZW9zL3BsYW5rYS1tY3AiXSwiZW52Ijp7IlBMQU5LQV9CQVNFX1VSTCI6Imh0dHBzOi8vcGxhbmthLmV4YW1wbGUuY29tIiwiUExBTktBX0FQSV9LRVkiOiI8eW91ci1wbGFua2EtYXBpLWtleT4ifX0=) |
|
|
52
|
+
| **VS Code** | [](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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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
|
|
91
|
-
|
|
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
|
-
##
|
|
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
|
|
112
|
-
| `
|
|
113
|
-
| `
|
|
114
|
-
| `
|
|
115
|
-
| `
|
|
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` |
|
|
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
|
-
| `
|
|
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
|
-
| `
|
|
127
|
-
| `planka_get_comments` |
|
|
128
|
-
| `
|
|
129
|
-
| `
|
|
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
|
-
`
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
`
|
|
139
|
-
`
|
|
140
|
-
|
|
141
|
-
una
|
|
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
|
|
191
|
-
cruda de Planka:
|
|
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"
|