foundry-boards-mcp 0.1.0__tar.gz

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.
@@ -0,0 +1,18 @@
1
+ # ── MCP de tableros de Foundry — SOLO configuración (sin credenciales) ────────────
2
+ # La autenticación NO va aquí: te la pide Claude Code (diálogo interactivo) la primera vez.
3
+ # Este archivo es opcional; sin él se usan los valores por defecto de abajo.
4
+
5
+ # API de Foundry (el MISMO backend que consume el frontend). Incluye el prefijo /api/v1.
6
+ # Sin esta variable se habla con el Foundry público: https://foundry.collie.digital/api/v1
7
+ # Descoméntala solo para apuntar a otro backend — el tuyo en local, o uno propio.
8
+ # FOUNDRY_API_BASE=http://localhost:8000/api/v1
9
+
10
+ # Segundos de espera a la API.
11
+ FOUNDRY_TIMEOUT=30
12
+
13
+ # ── Override opcional (solo automatización/CI, SIN diálogo) ────────────────────────
14
+ # Si defines esto, el MCP se autentica sin pedir nada. Para uso normal, déjalo comentado
15
+ # y deja que Claude Code te pida las credenciales.
16
+ # FOUNDRY_TOKEN=
17
+ # FOUNDRY_EMAIL=
18
+ # FOUNDRY_PASSWORD=
@@ -0,0 +1,4 @@
1
+ .venv/
2
+ .env
3
+ __pycache__/
4
+ *.pyc
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Collie Valley
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,378 @@
1
+ Metadata-Version: 2.5
2
+ Name: foundry-boards-mcp
3
+ Version: 0.1.0
4
+ Summary: MCP para administrar los tableros Kanban de Foundry desde agentes de IA.
5
+ Project-URL: Homepage, https://foundry.collie.digital
6
+ Author: Collie Valley
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Keywords: agents,agile,claude,foundry,kanban,mcp,model-context-protocol
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Framework :: AsyncIO
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Natural Language :: Spanish
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Bug Tracking
21
+ Classifier: Topic :: Utilities
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: httpx>=0.27
24
+ Requires-Dist: keyring>=25
25
+ Requires-Dist: mcp<2,>=1.12
26
+ Requires-Dist: python-dotenv>=1.0
27
+ Description-Content-Type: text/markdown
28
+
29
+ # Foundry Boards — MCP para agentes de IA
30
+
31
+ Servidor **MCP** (Model Context Protocol) que le da a **Claude Code, Codex, OpenCode, Gemini**
32
+ y otros clientes compatibles herramientas para
33
+ **administrar los tableros de Foundry** con un **modelo de gestión de trabajo ágil** (ADR-0014):
34
+ listar/crear tableros, crear/mover/editar tarjetas (work items), **transicionar por estados**,
35
+ gestionar **jerarquía** (épicas/historias/subtareas), **etiquetas**, **vínculos**, **sprints**
36
+ (Scrum), leer el **historial estructurado** y **gestionar adjuntos** (leer imágenes, subir y
37
+ borrar) — además de mover un tablero personal a un Área.
38
+
39
+ No forma parte del backend ni lo modifica: habla el **mismo contrato REST público** que el
40
+ frontend, autenticándose como una **cuenta de usuario** (JWT). Corre en **tu máquina**, igual que
41
+ `integrations/discord-porrin` y `integrations/porrin-meet`.
42
+
43
+ ```
44
+ Agente IA ──stdio──► foundry-boards (este MCP) ──HTTP + JWT──► Foundry API (/api/v1)
45
+ tools de tablero (permisos por rol)
46
+ ```
47
+
48
+ ## Por qué un MCP (y no llamar al backend a mano)
49
+ Con el MCP conectado, le pides al agente cosas en lenguaje natural y opera el tablero:
50
+ - *"Lista mis tableros y muéstrame las tarjetas del de Marketing."*
51
+ - *"Crea la tarea 'Migrar auth a OAuth' en En progreso, prioridad alta, asígnala a Ana."*
52
+ - *"Mueve todas las tarjetas de En revisión que ya estén aprobadas a Hecho."*
53
+
54
+ Los **permisos los valida el backend por rol**: dueño/colaborador pueden mutar; **observador solo
55
+ lee** (una mutación devuelve 403 y Claude te lo dice).
56
+
57
+ ## Requisitos
58
+ - **Python 3.10+** (lo gestiona `uv`; no necesitas python/pip en el PATH).
59
+ - **Un backend con el que hablar.** Por omisión, el Foundry público
60
+ (`https://foundry.collie.digital`): no hace falta levantar nada. Para apuntar a otro —el tuyo en
61
+ local, o uno propio— se dice con `FOUNDRY_API_BASE` (ver *Configuración*).
62
+ - Una **cuenta de Foundry**; para administrar tableros de un Área, esa cuenta debe ser
63
+ **miembro colaborador/dueño** de ese Área. Puede ser tu propia cuenta o una dedicada.
64
+
65
+ ## Instalación (con `uv`)
66
+ ```powershell
67
+ cd integrations/foundry-mcp
68
+ .\run.ps1 # crea .venv, instala deps y hace un chequeo de humo (no pide credenciales)
69
+ ```
70
+ Manual (o en Linux/Mac):
71
+ ```bash
72
+ cd integrations/foundry-mcp
73
+ uv sync # crea .venv e instala deps desde pyproject.toml/uv.lock (portable)
74
+ uv run python -m foundry_mcp # (opcional) arranque manual por stdio; normalmente lo lanza Claude Code
75
+ ```
76
+ No necesitas `.env`: **no se configuran credenciales**. Como mucho, un `.env` con
77
+ `FOUNDRY_API_BASE` para apuntar a otro backend, `FOUNDRY_AGENT_SOURCE` para corregir la identidad
78
+ de un cliente que no se anuncie bien o `FOUNDRY_AGENT_MODEL` para declarar el modelo usado (tu
79
+ `.env` local **gana** sobre el default del `.mcp.json`).
80
+
81
+ ## Identidad del agente en tareas, comentarios e historial
82
+
83
+ Cada descripción, comentario, avance, bloqueo, verificación o cambio de historial creado por el
84
+ MCP conserva la procedencia `ia` y guarda dos datos separados:
85
+
86
+ - `agent_source`: `claude_code`, `codex`, `opencode`, `gemini`, `other` o `unknown`.
87
+ - `agent_model`: nombre abierto del modelo concreto, por ejemplo `gpt-5`, `claude-sonnet-*` o
88
+ `gemini-2.5-pro`.
89
+
90
+ Foundry los muestra como `IA · Codex · gpt-*` junto a la descripción, en cada aporte y en cada
91
+ evento del historial; no hace falta firmar el texto. El MCP adjunta la identidad como cabeceras a
92
+ sus peticiones, de modo que también cubre movimientos, etiquetas, vínculos, sprints y otras
93
+ mutaciones que dejan `ChangeLog`.
94
+
95
+ - `FOUNDRY_AGENT_SOURCE=auto` (recomendado) usa `clientInfo` de la sesión MCP.
96
+ - Si el cliente se identifica con un nombre genérico, fija un override, por ejemplo
97
+ `FOUNDRY_AGENT_SOURCE=codex`.
98
+ - MCP no estandariza el modelo en `clientInfo`: fija `FOUNDRY_AGENT_MODEL` si el proceso usa uno
99
+ estable, o pasa `agent_model` en la herramienta si cambia dentro de la misma sesión. El servidor
100
+ también reconoce, si existen, `CODEX_MODEL`, `CLAUDE_MODEL`, `ANTHROPIC_MODEL`,
101
+ `OPENCODE_MODEL` y `GEMINI_MODEL`.
102
+ - Los clientes antiguos siguen funcionando: sus descripciones y eventos de agente aparecen como
103
+ `IA · Desconocido · Modelo desconocido`.
104
+ - La identidad es **declarada por el cliente o por la configuración local**, no una certificación
105
+ criptográfica. No debe usarse como control de autorización; los permisos siguen dependiendo del
106
+ JWT y del rol de la cuenta.
107
+
108
+ ## Autenticación (interactiva — te la pide Claude Code)
109
+ No hay credenciales en archivos ni en variables de entorno. La **primera acción** sobre un tablero
110
+ (o la tool `login`) abre un **diálogo de Claude Code** que te pide **email + contraseña** (o pegar un
111
+ **token**). Con eso el MCP obtiene un JWT y lo usa durante la sesión.
112
+
113
+ - El **token** se cachea en **memoria** para la sesión. Si marcas **"recordar"** en el diálogo, se
114
+ guarda en el **gestor de credenciales del sistema operativo** (Windows Credential Manager / macOS
115
+ Keychain / Secret Service) — **nunca** en `.env`, en un archivo ni en el repo. La **contraseña no
116
+ se guarda en ningún lado**: solo se usa para obtener el token.
117
+ - El token de Foundry dura **24 h** y no hay renovación automática (la contraseña no se guarda).
118
+ Al caducar, el MCP **relee primero el gestor de credenciales**: si otra sesión ya renovó (y
119
+ guardó con "recordar"), lo reutiliza **sin preguntar** — con varias ventanas de Claude Code
120
+ abiertas, solo la primera pide login. Si el guardado también caducó, vuelve el diálogo (una vez
121
+ al día como mucho).
122
+ - `login` inicia sesión por adelantado o cambia de cuenta; `logout` olvida el token (memoria + SO).
123
+ - **Automatización/CI (sin diálogo):** define `FOUNDRY_TOKEN`, o `FOUNDRY_EMAIL` + `FOUNDRY_PASSWORD`
124
+ en el entorno y el MCP se autentica sin preguntar. Es opcional; para uso normal, no lo definas.
125
+
126
+ > Se usa *elicitation* en **modo formulario**. Para un backend local que tú controlas es lo práctico;
127
+ > como solo se persiste el JWT (nunca la contraseña), el riesgo es acotado.
128
+
129
+ ## Registrar en Claude Code
130
+ El registro solo necesita **cómo lanzar** el server (sin secretos: la sesión se pide en runtime).
131
+ El repo ya trae un **`.mcp.json` en la raíz** (scope de **proyecto**, compartido con el equipo) que
132
+ lanza el server con `uv run` — **portable** (Windows/Linux/Mac, sin rutas de `.venv`) — y fija el
133
+ backend de prod:
134
+
135
+ ```json
136
+ {
137
+ "mcpServers": {
138
+ "foundry-boards": {
139
+ "type": "stdio",
140
+ "command": "uv",
141
+ "args": ["run", "--directory", "${CLAUDE_PROJECT_DIR:-.}/integrations/foundry-mcp", "python", "run_server.py"],
142
+ "env": {
143
+ "FOUNDRY_API_BASE": "https://foundry.collie.digital/api/v1",
144
+ "FOUNDRY_AGENT_SOURCE": "auto"
145
+ }
146
+ }
147
+ }
148
+ }
149
+ ```
150
+ Si ese proceso usa un modelo fijo, añade
151
+ `"FOUNDRY_AGENT_MODEL": "<modelo-actual>"` dentro de `env`. Si permite cambiar de modelo durante
152
+ la sesión, deja la configuración sin ese valor y usa el argumento `agent_model` de cada aporte,
153
+ creación o edición de tarea.
154
+ Claude Code **pide aprobarlo** la primera vez (`claude mcp reset-project-choices` para reelegir).
155
+ Requiere `uv` en el PATH (el mismo que usa el backend).
156
+
157
+ **Alternativa personal** (sin `.mcp.json`, solo para ti): `claude mcp add-json foundry-boards '<json>'`
158
+ con la misma estructura pero **ruta absoluta** en `--directory` (en `~/.claude.json` no se expande
159
+ `${CLAUDE_PROJECT_DIR}`). Si no vas a tocar el código del MCP, es más simple instalarlo: el
160
+ [`mcp.json.example`](./mcp.json.example) trae esa forma, la de *[Instalarlo sin el
161
+ repo](#instalarlo-sin-el-repo-desde-pypi)*, que no depende de dónde tengas el proyecto.
162
+
163
+ Verifica: `/mcp` debe mostrar `foundry-boards` con sus tools. Prueba *"usa whoami de foundry-boards"*.
164
+
165
+ ## Compartir con el equipo (onboarding)
166
+ Todo lo necesario ya vive en el repo (el `.mcp.json` y el código); **no hay secretos que repartir** —
167
+ cada quien entra con **su** cuenta. Cada compañero, **una vez**:
168
+ 1. Tener **`uv`** en el PATH y el repo al día (`git pull`).
169
+ 2. **Preparar el entorno** para que el primer arranque no expire:
170
+ `cd integrations/foundry-mcp && uv sync` (o `.\run.ps1` en Windows).
171
+ 3. Abrir **Claude Code** en la carpeta del repo → **aprobar** el `.mcp.json` cuando lo pregunte.
172
+ 4. `/mcp` → `foundry-boards` (74 tools: tablero + notas). La primera acción abre el **diálogo de login**: entra con
173
+ **tu** cuenta (debes ser miembro colaborador/dueño de los espacios que quieras editar).
174
+
175
+ - **Backend:** por defecto prod (fijado en el `.mcp.json`). Para apuntar a otro (p. ej. local), crea
176
+ un `.env` en `integrations/foundry-mcp/` con `FOUNDRY_API_BASE=...` — tu `.env` local **gana** sobre
177
+ el default del `.mcp.json`.
178
+ - **Si ya lo tenías registrado a mano** (scope local/usuario), quítalo para que mande el de proyecto:
179
+ `claude mcp remove foundry-boards -s local`.
180
+
181
+ ## Instalarlo sin el repo (desde PyPI)
182
+ Quien **no** tiene el monorepo —ni tiene por qué tenerlo— instala el paquete publicado. Basta con
183
+ `uv` en el PATH: `uvx` lo baja, lo cachea y lo ejecuta sin dejar nada en tu carpeta de trabajo.
184
+
185
+ ```powershell
186
+ claude mcp add-json foundry-boards '{ "type":"stdio", "command":"uvx", "args":["foundry-boards-mcp"] }'
187
+ ```
188
+
189
+ Y ya está: **no hay que configurar ningún backend**, porque por omisión habla con el Foundry
190
+ público (`https://foundry.collie.digital`). Lo demás es igual que para el equipo — la primera
191
+ herramienta abre el **diálogo de login** y entras con **tu** cuenta.
192
+
193
+ > **Aún no publicado.** La primera versión todavía no está en PyPI; hasta entonces, usa la vía
194
+ > desde git de aquí abajo. El nombre ya está decidido: `foundry-mcp` está cogido por un proyecto
195
+ > ajeno, así que el paquete se llama **`foundry-boards-mcp`** y el comando, igual.
196
+
197
+ - Para clavar una versión concreta: `"args": ["foundry-boards-mcp@0.1.0"]`.
198
+ - Para apuntar a otro backend —el tuyo en local, o uno propio—, añade
199
+ `"env": {"FOUNDRY_API_BASE": "http://localhost:8000/api/v1"}`.
200
+ - Necesitas una **cuenta de Foundry**; para editar los tableros de un Área, ser miembro
201
+ colaborador o dueño de ella. No hay nada más que repartir: ni claves, ni tokens.
202
+
203
+ **Desde git, sin esperar a una publicación** (requiere acceso al repositorio, que es privado):
204
+
205
+ ```powershell
206
+ claude mcp add-json foundry-boards '{ "type":"stdio", "command":"uvx", "args":["--from","git+https://github.com/gepres/foundry.git#subdirectory=integrations/foundry-mcp","foundry-boards-mcp"] }'
207
+ ```
208
+
209
+ - Se registra en tu scope **local/usuario** (ruta git absoluta; `${CLAUDE_PROJECT_DIR}` no aplica
210
+ sin repo).
211
+ - Para fijar rama o etiqueta: `...foundry.git@<tag-o-rama>#subdirectory=integrations/foundry-mcp`
212
+ (sin `@`, usa la rama por defecto = último `main`).
213
+ - La primera vez uv **clona el repo una vez** (en su caché, no en tu workspace) para construir el
214
+ paquete; los arranques siguientes son rápidos.
215
+
216
+ ## Herramientas
217
+ **Sesión**
218
+ - `login` — inicia sesión (abre el diálogo de credenciales) o cambia de cuenta.
219
+ - `logout` — olvida el token (memoria + gestor de credenciales del SO).
220
+
221
+ **Lectura**
222
+ - `whoami` — la cuenta con la que actúa el MCP (verifica la sesión).
223
+ - `list_spaces` — Áreas de trabajo visibles.
224
+ - `list_boards(space_id?)` — tableros personales (sin `space_id`) o los de un Área.
225
+ - `list_all_boards` — panorama: personales + los de cada Área.
226
+ - `get_board(initiative_id)` — tablero completo: columnas con sus tarjetas.
227
+
228
+ **Tableros**
229
+ - `list_board_presets()` — las **plantillas de columnas** para el alta: `key`, `label`, `template`
230
+ (`kanban`/`scrum`), `is_default` y las columnas de cada una con su categoría. Léelas antes de
231
+ crear: las claves no se adivinan.
232
+ - `create_board(title, space_id?, preset?, columns?)` — tablero nuevo (personal o de un Área).
233
+ `preset` es la clave de una plantilla; `columns` monta las columnas **a medida**
234
+ (`[{"name": "Ideas", "category": "todo", "color": "#5b9bd5"}, …]`, entre 2 y 10, con al menos una
235
+ `todo` y una `done`). Con columnas a medida manda también el `preset` del que partes: es quien
236
+ fija la plantilla del tablero y, con ella, si hay sprints.
237
+ - `move_board_to_space(initiative_id, space_id)` — comparte un tablero personal con un Área.
238
+
239
+ **Columnas del tablero** (FNV-267; el servidor aplica el tope de 10, los nombres sin repetir y el
240
+ invariante de ≥1 de entrada y ≥1 de cierre)
241
+ - `add_column(initiative_id, name, category="in_progress", color?)` — al final del tablero.
242
+ - `rename_column(initiative_id, column, name)` — el id no cambia: **el historial no se toca**.
243
+ - `set_column_category(initiative_id, column, category)` — `todo` · `in_progress` · `done`; es lo
244
+ que decide dónde entra una tarjeta y qué la da por cerrada.
245
+ - `set_column_color(initiative_id, column, color?)` — `#rrggbb`, o vacío para heredar el de su tipo.
246
+ - `reorder_columns(initiative_id, columns)` — **todas** las columnas en el orden que quedan.
247
+ - `delete_column(initiative_id, column, reassign_to?)` — si tiene tarjetas, `reassign_to` dice a
248
+ dónde se mudan (se avisa antes con cuántas son). Marcada como **destructiva** para el cliente.
249
+
250
+ **Tarjetas (work items)**
251
+ - `create_task(initiative_id, title, column="Backlog", description?, priority?, kind?, assignee?, issue_type?, parent_id?, story_points?)`
252
+ - `move_task(initiative_id, task_id, column, order=0)`
253
+ - `transition_task(initiative_id, task_id, status, order=0)` — mueve por **estado** del workflow
254
+ (la columna destino); deja rastro (status_id/column_id).
255
+ - `edit_task(initiative_id, task_id, title?, description?, priority?, kind?, assignee?)` — **parcial**.
256
+ - `set_task_hierarchy(initiative_id, task_id, issue_type, parent_id?)` — tipo + padre (jerarquía).
257
+ - `set_task_details(initiative_id, task_id, story_points?, start_date?, due_date?, flagged?)` —
258
+ **PATCH**: solo cambia lo que pases, conserva lo demás. Fechas ISO; `""` borra una fecha.
259
+ - `set_task_labels(initiative_id, task_id, labels[])` — etiquetas por **nombre** (crea las que falten).
260
+ - `link_tasks(initiative_id, task_id, target_id, link_type)` — `blocks|is_blocked_by|relates_to`.
261
+ - `get_task_links(initiative_id, task_id)` — vínculos de la tarjeta (id, tipo, la otra tarjeta).
262
+ - `unlink_tasks(initiative_id, task_id, target_id, link_type?)` — quita el/los vínculo(s) hacia esa
263
+ tarjeta (resuelve el id del vínculo solo; sin `link_type` borra todos los que apunten a ella).
264
+ - `link_repo(initiative_id, task_id, url, kind?, title?)` — vincula la tarjeta a un recurso de
265
+ repositorio (`kind`: `repository|branch|pull_request|commit`); ideal para asociar el repo/branch/PR
266
+ donde trabajas (ADR-0016, Opción A).
267
+ - `get_repo_links(initiative_id, task_id)` — recursos de repo de la tarjeta (id, url, kind, title).
268
+ - `unlink_repo(initiative_id, task_id, url)` — quita el vínculo de repo cuya URL coincide.
269
+ - `delete_task(initiative_id, task_id)`
270
+ - `create_tasks_from_seeds(initiative_id, seed_ids)` — Vivero→Tablero (tarjetas propuestas).
271
+
272
+ **Épicas**
273
+ - `create_epic(initiative_id, title, column?, description?, priority?)` — work item raíz.
274
+ - `list_epics(initiative_id)` — tarjetas con `issue_type=epic`.
275
+
276
+ **Sprints (Scrum)**
277
+ - `list_sprints(initiative_id)` — sprints del tablero (state future|active|closed).
278
+ - `create_sprint(initiative_id, name, goal?, start_date?, end_date?)` — sprint planificado.
279
+ - `start_sprint(initiative_id, sprint_id)` — future → active (uno activo por tablero).
280
+ - `complete_sprint(initiative_id, sprint_id)` — active → closed (incompletas vuelven al backlog).
281
+ - `assign_task_to_sprint(initiative_id, task_id, sprint_id?)` — a un sprint, o al backlog si se omite.
282
+
283
+ **Historial de la tarjeta**
284
+ - `add_task_entry(initiative_id, task_id, content, kind="avance", meta=None)` — registra un **aporte
285
+ tipado** (ADR-0017) en el hilo, para que el output quede estructurado, no aplanado en texto.
286
+ `kind`: `avance` · `criterio` · `decision` · `bloqueo` (marca la bandera de impedimento) ·
287
+ `verificacion` · `pregunta` · `nota`. `meta` es JSON libre (p. ej. `{"commit": "abc123"}`). Queda
288
+ como aporte del agente (procedencia `ia`).
289
+ - `add_task_comment(initiative_id, task_id, content)` — deja una **nota** libre (equivale a
290
+ `add_task_entry` con `kind="nota"`). Queda marcada como nota del agente.
291
+ - `get_task_comments(initiative_id, task_id)` — lee el hilo (autor, tipo human|agent, procedencia,
292
+ tipo de aporte, meta, texto y fecha).
293
+ - `get_task_history(initiative_id, task_id)` — historial **estructurado** (ADR-0014, E4): qué campo
294
+ cambió, de qué valor a qué valor, quién y cuándo (created·status·priority·assignee·sprint·label·…).
295
+
296
+ **Datos con gate humano (ADR-0017 F2)** — la IA **propone**, una persona acepta/descarta. El gate es
297
+ **simétrico**: se ve y se completa por los **dos lados** (el tablero y estas tools). Si la sesión de
298
+ Claude se cierra, la decisión se toma en la app; al reabrir, `list_*` te muestra qué se decidió.
299
+ - `propose_criterion(initiative_id, task_id, text)` — propone un **criterio de aceptación** ("para
300
+ darla por hecha debe cumplirse Z"). Nace `proposed` (procedencia `ia`).
301
+ - `record_decision(initiative_id, task_id, title, rationale="", repo_url="")` — registra una
302
+ **decisión** de diseño (ADR-lite). Nace `proposed`. `repo_url` la ata a un commit/PR (ADR-0016)
303
+ para la trazabilidad IA→código.
304
+ - `list_criteria(initiative_id, task_id)` / `list_decisions(initiative_id, task_id)` — **leen** los
305
+ criterios/decisiones con su `status` (`proposed|accepted|rejected`), procedencia y datos del gate.
306
+ Úsalas para confirmar qué se aceptó/descartó (incluso si se decidió desde la app).
307
+ - `review_criterion(initiative_id, task_id, criterion_id, accept)` /
308
+ `review_decision(initiative_id, task_id, decision_id, accept)` — **gate humano** desde Claude:
309
+ `accept=True` lo vuelve oficial, `False` lo descarta. Es la decisión de una persona (igual que el
310
+ botón del tablero): úsalas solo a pedido explícito; **no auto-apruebes** lo que tú propusiste.
311
+ - `review_task_proposal(initiative_id, task_id, accept)` — gate humano de una **tarjeta** propuesta
312
+ por la IA (`status_agent="proposed"`): la acepta (tarjeta oficial) o la rechaza (queda `rejected`,
313
+ oculta pero con traza). Su estado se lee en `get_board`.
314
+
315
+ **Adjuntos**
316
+ - `list_task_attachments(initiative_id, task_id)` — metadatos de los adjuntos de una tarjeta
317
+ (id, filename, content_type, size, quién los subió y cuándo).
318
+ - `read_task_attachment(initiative_id, task_id, attachment_id)` — lee un adjunto: las **imágenes** se
319
+ devuelven como imagen (Claude las **ve**: mockups, capturas, diagramas), el **texto** (text/*, json,
320
+ csv, yaml…) como texto, y otros binarios como metadatos + aviso. Límite 10 MB (backend).
321
+ - `upload_task_attachment(initiative_id, task_id, path? | text? | base64_data?, filename?)` — **sube**
322
+ un archivo: desde una ruta del disco, desde un texto que el agente tiene en la mano o desde bytes
323
+ en base64 (estas dos exigen `filename`, del que sale el tipo de contenido). Una sola de las tres.
324
+ - `delete_task_attachment(initiative_id, task_id, attachment_id)` — **borra** un adjunto.
325
+ Irreversible; devuelve qué se borró (nombre y tamaño) en vez de un OK mudo.
326
+
327
+ **Prompts** (los invoca el usuario desde su cliente, como un comando; FNV-270)
328
+ - `trabajar_tarjeta(initiative_id, task_id)` — el ciclo completo: enterarse, hacer visible el plan,
329
+ construir, dejar avance con su commit, dejar evidencia y entregar. Con lo que no se negocia:
330
+ **«Hecho» lo decide una persona**.
331
+ - `dejar_avance(initiative_id, task_id)` — qué se construyó, qué quedó fuera, el commit y la
332
+ evidencia con números (`parcial` si falta la prueba de fuego).
333
+ - `repasar_tablero(initiative_id)` — qué espera a una persona, qué dice estar hecho sin
334
+ demostrarlo, qué lleva parado. Solo lee: no mueve nada.
335
+
336
+ **Qué declara cada herramienta** (FNV-268): todas dicen si **solo leen** (23), si **crean o
337
+ actualizan** sin destruir (45) o si **borran** (6). El cliente lo usa para saber qué puede aprobar
338
+ solo y dónde pararse — y ojo, lo no declarado se asume destructivo, así que callar no era neutral.
339
+ Las que borran (tarjeta, nota, adjunto, columna) además **preguntan antes**, diciendo qué se pierde
340
+ (FNV-269); si el cliente no sabe preguntar, siguen adelante en vez de bloquearse.
341
+
342
+ **Ergonomía pensada para el agente:**
343
+ - **Columnas/estados por nombre** (tolerante a acentos/mayúsculas) o por id. Cada tablero tiene las
344
+ suyas —siete plantillas o las que haya montado el equipo (FNV-256)—, así que se leen con
345
+ `get_board` y **nunca se suponen**: mira la `category` de cada columna (`todo`/`in_progress`/
346
+ `done`), que es lo único estable. Al crear una tarjeta sin `column`, nace en la primera `todo`.
347
+ - **Responsable por nombre/email**: en tableros de Área resuelve el `assignee` contra los miembros;
348
+ en tableros personales acepta un id. Para quitarlo: `edit_task(..., assignee="")`.
349
+ - `priority` ∈ `highest|high|medium|low|lowest` (también `baja|media|alta`); `kind` ∈
350
+ `tarea|bug|incidencia`; `issue_type` ∈ `epic|historia|tarea|bug|subtarea`.
351
+
352
+ ## Límites (a propósito, para no inventar API)
353
+ Este MCP envuelve lo que necesita del backend. **No** hace (aún):
354
+ - crear/renombrar/borrar/reordenar **columnas** y editar el **workflow** (el backend ya lo soporta,
355
+ ADR-0014 E2; el MCP aún no lo envuelve — al crear un tablero toma el preset por defecto);
356
+ - **borrar** un tablero/iniciativa.
357
+
358
+ Si hace falta, se agregan como tools nuevas aquí.
359
+
360
+ ## Seguridad
361
+ - Las mutaciones las autoriza el **servidor** por rol; el MCP no elude permisos.
362
+ - **No hay credenciales en archivos ni en variables de entorno** (salvo que actives el override de
363
+ automatización). La sesión se pide en runtime; solo el **token** se cachea (memoria, u opcional
364
+ gestor de credenciales del SO). La **contraseña no se persiste**.
365
+ - Da a la cuenta usada **solo** el acceso que necesite (invítala como colaborador únicamente a los
366
+ espacios que deba administrar).
367
+
368
+ ## Solución de problemas
369
+ - **Las tools piden login cada vez** → marca **"recordar"** en el diálogo para guardar el token en el
370
+ gestor de credenciales del SO (si no, la sesión es solo en memoria y se pide en cada arranque).
371
+ - **`/mcp` muestra el server pero las tools fallan** → confirma que el backend esté arriba en
372
+ `FOUNDRY_API_BASE`. Prueba `whoami`; si pide login, complétalo.
373
+ - **403 al mutar** → la cuenta es observador, o no es miembro de ese Área.
374
+ - **404 en un tablero/tarea** → id equivocado, o la cuenta no ve ese tablero.
375
+ - **El server no conecta** → verifica la ruta de `command` (Windows `.venv/Scripts/python.exe` vs
376
+ POSIX `.venv/bin/python`) y que `run.ps1` haya creado el `.venv`.
377
+ - **"Este cliente no puede pedir credenciales…"** → el cliente MCP no soporta *elicitation*; usa el
378
+ override de entorno (`FOUNDRY_TOKEN` o `FOUNDRY_EMAIL`/`FOUNDRY_PASSWORD`).