# Herramientas MCP de tickets

Las herramientas MCP de soporte interno: tickets para consultar la bandeja y crear tickets, my_tickets para los tuyos y tickets_workflow para gestionarlos.

Con estas herramientas, un asistente reporta incidencias, consulta la bandeja de soporte interno de tu organización y la gestiona, siempre con los permisos y el alcance de la persona que autorizó el token. Necesitas un token OAuth o una clave personal de uno de los servidores MCP; cuál, depende de la herramienta.

No hay que confundirlas con el servicio `sdk.tickets` de los SDK, que usa una clave de API y sirve para que los usuarios finales de **tu** producto abran tickets a tu equipo. Estas herramientas crean tickets en la bandeja de soporte de tu organización en nombre de quien autorizó el token.

## Qué herramienta usar

**Tres herramientas para los tickets**

### tickets (CRM y Gestión de proyectos)

Consulta la bandeja del equipo y crea tickets. Para leer pide el permiso de ver todos los tickets.

### my_tickets (Todos los servidores)

Reporta un ticket y sigue los tuyos: verlos, responder, cerrarlos o reabrirlos. Basta con ver los propios.

### tickets_workflow (Gestión de proyectos)

Gestiona la bandeja: comenta, cambia el estado, asigna, resuelve, cierra y convierte en elemento de trabajo.

## `tickets`

| Operación | Qué hace | Permiso | Scope |
| - | - | - | - |
| `list` | Lista los tickets de la bandeja, con filtros y paginación. | `SUPPORT_TICKETS_VIEW_ALL` | `support:tickets:view` |
| `get` | Detalle de un ticket por su `id` o su `ticketKey`. | `SUPPORT_TICKETS_VIEW_ALL` | `support:tickets:view` |
| `get_comments` | Comentarios del ticket. Con `includeInternal` (por defecto `true`) incluye los internos del equipo. | `SUPPORT_TICKETS_VIEW_ALL` | `support:tickets:view` |
| `get_attachments` | Adjuntos del ticket original, sin los de los comentarios. | `SUPPORT_TICKETS_VIEW_ALL` | `support:tickets:view` |
| `create` | Crea un ticket. Quien lo reporta es siempre la persona que autorizó el token. | `SUPPORT_TICKETS_CREATE` | `support:tickets:create` |

Las lecturas aceptan también los conjuntos `support:read`, `support:full`, `crm:read`, `crm:full` y `dev:full`, y la creación, `support:write`, `support:full`, `crm:write`, `crm:full` y `dev:full`. Si tu usuario solo puede ver sus propios tickets, usa `my_tickets`.

La herramienta recibe siempre el parámetro `operation` y, después, los de cada operación:

```json
{
  "name": "tickets",
  "arguments": {
    "operation": "create",
    "title": "No puedo exportar el informe mensual",
    "description": "Al pulsar «Exportar» no pasa nada y aparece un error de red.",
    "category": "BUG"
  }
}
```

### Crear un ticket

| Campo | Tipo | Obligatorio | Validación |
| - | - | - | - |
| `title` | `string` | Sí | Entre 5 y 500 caracteres. |
| `description` | `string` | Sí | Al menos 10 caracteres. Si es un error, incluye los pasos para reproducirlo. |
| `category` | `enum` | Sí | `BUG` (algo no funciona), `CONSULTA` (una duda) o `SUGERENCIA` (una mejora). |
| `priority` | `enum` | No | `BAJA`, `MEDIA` (por defecto), `ALTA` o `CRITICA`. |
| `attachmentIds` | `string[]` | No | Hasta 5 identificadores de archivos del gestor de archivos que haya subido la misma persona. |
| `currentUrl` | `string` | No | Dirección de la pantalla donde apareció el problema, hasta 2048 caracteres. |
| `userAgent` | `string` | No | Navegador de la persona. |
| `screenResolution` | `string` | No | Resolución de pantalla, por ejemplo `1920x1080`, hasta 50 caracteres. |

Los tres últimos campos dan contexto de diagnóstico y los rellena el botón de reportar incidencias de la propia aplicación. Un asistente normalmente no los conoce, y puede dejarlos vacíos.

Reglas que conviene saber:

- **Quien reporta es siempre quien autorizó el token.** Si la llamada incluye `reporterId`, se ignora, para que nadie abra tickets en nombre de otro.
- **Hasta 10 tickets por hora y persona** por MCP, además del límite general de 60 peticiones por minuto. Así un asistente en bucle no llena la bandeja.
- **Los adjuntos se suben antes** al gestor de archivos (con `fm_files_manage` en el servidor `utilia-crm`, por ejemplo) y se pasan por su identificador. Si alguno no existe o lo subió otra persona, no se crea el ticket y el error dice cuál.
- **Un `BUG` no se convierte solo en un elemento de trabajo.** La conversión la decide el equipo de soporte, desde la aplicación o con `tickets_workflow` (`convert_to_issue`).

Al crearse el ticket, las personas con `SUPPORT_TICKETS_VIEW_ALL` reciben un aviso en la aplicación y por correo, y las aplicaciones externas suscritas reciben el webhook `TICKET_CREATED`.

La respuesta:

```json
{
  "id": "ckxyz0001abcdfilexample",
  "ticketKey": "TKT-AURORA-ASESORES-128",
  "title": "No puedo exportar el informe mensual",
  "category": "BUG",
  "priority": "MEDIA",
  "status": "OPEN",
  "createdAt": "2026-10-08T14:35:21.000Z",
  "attachments": [],
  "message": "Ticket TKT-AURORA-ASESORES-128 creado. Los agentes de soporte han sido notificados. Consulta su estado con tickets(operation: 'get', id: 'TKT-AURORA-ASESORES-128')."
}
```

`ticketKey` es la clave legible del ticket y sirve como `id` en las operaciones siguientes. `status` empieza siempre en `OPEN`, y `message` resume el resultado en castellano con el siguiente paso.

> **Atención: No construyas la clave del ticket**
>
> La clave lleva el identificador de tu organización para que no choque con la de otra: tiene la forma `TKT-<ORGANIZACIÓN>-<número>`, por ejemplo `TKT-AURORA-ASESORES-128`. Usa siempre el `ticketKey` que devuelve el servidor y no supongas que detrás de `TKT-` solo hay dígitos.

Ejemplo con los SDK. Necesitan una sesión OAuth autorizada para el servidor al que llamas, como se explica en [Primeros pasos con MCP](https://os.utilia.ai/docs/developers/mcp/quickstart#llamar-a-las-herramientas-desde-tu-código):

#### JavaScript / TypeScript

```typescript
const { data } = await sdk.mcp.tickets.create({
  title: 'No puedo exportar el informe mensual',
  description: 'Al pulsar «Exportar» no pasa nada y aparece un error de red.',
  category: 'BUG',
  priority: 'ALTA',
});

console.log(data.ticketKey);
console.log(data.message);
```

#### Python

```python
from utilia_sdk.mcp.types import CreateMcpTicketInput

result = await sdk.mcp.tickets.create(
    CreateMcpTicketInput(
        title="No puedo exportar el informe mensual",
        description="Al pulsar «Exportar» no pasa nada y aparece un error de red.",
        category="BUG",
        priority="ALTA",
    )
)

print(result.ticket_key)
print(result.message)
```

### Filtros de `list`

| Parámetro | Tipo | Qué filtra |
| - | - | - |
| `status` | `enum` | Estado: `OPEN`, `IN_REVIEW`, `CONVERTED`, `RESOLVED` o `CLOSED`. |
| `priority` | `enum` | Prioridad. |
| `category` | `enum` | Categoría. |
| `search` | `string` | Texto en el título, la descripción o la clave. |
| `assignedToId` | `string` | Persona asignada. |
| `projectId` | `string` | Proyecto asociado. |
| `portalUserId` | `string` | Usuario del portal del cliente que lo abrió. |
| `externalAppId` | `string` | Aplicación externa que lo originó. |
| `reporterId` | `string` | Persona que lo reportó. |
| `hasIssue` | `boolean` | Solo los vinculados a un elemento de trabajo. |
| `limit` | `number` | Tamaño de página: 20 por defecto, 100 como máximo. |
| `cursor` | `string` | El `pageInfo.nextCursor` de la página anterior. Omítelo para empezar. |
| `offset` | `number` | Desplazamiento para paginar; se sigue aceptando, pero `cursor` es la forma recomendada. |
| `response_format` | `enum` | `concise` (por defecto) da los campos clave de cada ticket; `detailed`, la fila completa. |

La respuesta trae los tickets en `items` y la paginación en `pageInfo` (`nextCursor`, `hasMore` y `pageSize`); `nextCursor` es nulo en la última página. Para la ficha entera de un ticket, usa `get`.

## `my_tickets`

Sirve para reportar un ticket y seguir los tuyos desde cualquier servidor, sin necesitar acceso a la bandeja del equipo. Las notas internas del equipo no se sirven nunca por aquí.

| Operación | Qué hace | Permiso | Scope |
| - | - | - | - |
| `create` | Abre un ticket, con las mismas reglas que `tickets: create`. | `SUPPORT_TICKETS_CREATE` | `support:tickets:create` |
| `list` | Lista tus tickets. | `SUPPORT_TICKETS_VIEW_OWN` | `support:tickets:view` |
| `get` | Detalle de uno de tus tickets. | `SUPPORT_TICKETS_VIEW_OWN` | `support:tickets:view` |
| `get_comments` | Respuestas de tu ticket, sin las notas internas. | `SUPPORT_TICKETS_VIEW_OWN` | `support:tickets:view` |
| `add_comment` | Responde en tu ticket. | `SUPPORT_TICKETS_COMMENT` | `support:tickets:update` |
| `close` | Cierra tu ticket. | `SUPPORT_TICKETS_CLOSE_OWN` | `support:tickets:update` |
| `reopen` | Reabre tu ticket. | `SUPPORT_TICKETS_CLOSE_OWN` | `support:tickets:update` |
| `update` | Corrige tu ticket. | `SUPPORT_TICKETS_CREATE` | `support:tickets:update` |

## `tickets_workflow`

Gestiona la bandeja del equipo. Solo está en el servidor `utilia-work`, y todas sus operaciones piden el scope `support:tickets:update` (o `support:write`, `support:full` o `dev:full`).

| Operación | Qué hace | Permiso |
| - | - | - |
| `add_comment` | Comenta en un ticket; con `isInternal` el comentario solo lo ve el equipo. | `SUPPORT_TICKETS_COMMENT` |
| `edit_comment` | Edita un comentario. | `SUPPORT_TICKETS_COMMENT` |
| `delete_comment` | Borra un comentario. Exige `confirm: true`. | `SUPPORT_TICKETS_COMMENT` |
| `update_status` | Cambia el estado del ticket. | `SUPPORT_TICKETS_MANAGE` |
| `assign` | Asigna el ticket a una persona (`assignedToId`; `null` lo deja sin asignar) y, con `projectId`, lo asocia a un proyecto. | `SUPPORT_TICKETS_ASSIGN` |
| `resolve` | Da el ticket por resuelto. | `SUPPORT_TICKETS_CLOSE` |
| `close` | Cierra el ticket. | `SUPPORT_TICKETS_CLOSE` |
| `convert_to_issue` | Crea un elemento de trabajo en el tablero de `projectId` (obligatorio) y deja el ticket como convertido. Devuelve las claves de los dos. Un ticket ya convertido se rechaza. | `SUPPORT_TICKETS_MANAGE` |

El proyecto vive en Gestión de proyectos, así que `convert_to_issue` y `assign` con `projectId` exigen ese aplicativo en el plan. Sin él, la llamada se rechaza con `errorCode: MODULE_NOT_ENABLED` antes de cambiar nada: tampoco se asigna a la persona que venía en la misma llamada.

`convert_to_issue` acepta además los datos del elemento que se crea, con las mismas reglas que en la aplicación: `title`, `description`, `type`, `priority`, `sprintId`, `assigneeIds`, `storyPoints`, `autoAssignToConverter`, `labelIds`, `dueDate`, `originalEstimate` y `parentId`.

Al crear un ticket de categoría `BUG`, la respuesta de `create` sugiere esta conversión en `nextSteps`, siempre que tu plan, tus permisos y el token permitan hacerla.

## Errores frecuentes

| Mensaje | Causa |
| - | - |
| «Se requiere el parámetro 'operation' para tickets...» | Falta `operation`. |
| «Se requiere el parámetro 'title' para tickets(operation: 'create')...» | Falta `title` o no tiene entre 5 y 500 caracteres. |
| «Se requiere el parámetro 'description'...» | Falta `description` o tiene menos de 10 caracteres. |
| «Se requiere el parámetro 'category'...» | Falta `category` o su valor no es válido. |
| «Prioridad no válida. Valores aceptados: BAJA, MEDIA, ALTA, CRITICA...» | `priority` no es válida. |
| «No se pueden adjuntar más de 5 archivos por ticket.» | Más de 5 identificadores en `attachmentIds`. |
| «Solo puedes adjuntar archivos que tú hayas subido...» | Algún adjunto lo subió otra persona. |
| «Tu usuario no tiene el permiso SUPPORT_TICKETS_CREATE...» | El token es válido, pero tu usuario no tiene el permiso. |

Llegan como resultado de la herramienta con `isError: true` y el mensaje en `error`. Los rechazos de plan, de permisos o de la propia regla de negocio traen además el motivo en `error.details.errorCode` (por ejemplo, `MODULE_NOT_ENABLED` o `PERMISSION_DENIED`), para que tu código no dependa del texto.

## Siguiente paso

Cuando un ticket es un error que hay que arreglar, el equipo de soporte lo convierte en un elemento de trabajo desde la bandeja de [Tickets de soporte](https://os.utilia.ai/dashboard/support-tickets) o con `tickets_workflow` (`convert_to_issue`), y su avance se sigue con la herramienta `issues` del servidor `utilia-work`. Si tu aplicación quiere enterarse de los tickets nuevos sin preguntar, suscríbela a `TICKET_CREATED` y al resto de eventos de tickets en [Avisos: contenido y catálogo de eventos](https://os.utilia.ai/docs/developers/webhooks/events).
