Buscador global y avisos por MCP
Dos herramientas MCP transversales: search encuentra fichas de todo el producto en una llamada y notifications gestiona los avisos de quien autorizó el token.
Actualizado el . 9 min de lectura
Dos herramientas MCP no pertenecen a ningún módulo y se publican en varios servidores con el mismo contrato: search, el buscador global, y notifications, los avisos de la persona que autorizó el token. Se comportan igual en todos los servidores; lo único que cambia es lo que alcanzan, porque depende del token con el que llames.
search: encontrar sin saber dónde está
Es el mismo buscador de la paleta de búsqueda de la aplicación. En una sola llamada encuentra clientes, contactos, leads, proyectos, oportunidades, presupuestos, facturas, cobros recurrentes, tareas, interacciones, tickets, archivos, carpetas, elementos de trabajo, proveedores y su actividad, gastos, documentos digitalizados, y personas de tu organización. Para cada resultado indica la herramienta con la que pedir la ficha completa.
Está en los servidores de Gestión de proyectos (/api/mcp/work), CRM (/api/mcp/crm) y Finanzas (/api/mcp/finance). No está en Marketing, Chat ni RRHH, porque ningún scope de esos servidores abre un tipo de ficha del buscador: siempre devolvería cero resultados.
Operación global
| Parámetro | Tipo | Obligatorio | Notas |
|---|---|---|---|
q | texto | Sí | Entre 2 y 200 caracteres. |
types | lista de tipos | No | Acota la búsqueda. Sin él, busca en todos los tipos que alcanzas. |
limit | número | No | Entre 1 y 50; 10 por defecto. |
La respuesta trae groups (las coincidencias agrupadas por tipo, con su rótulo en castellano y su recuento), meta y una pista del siguiente paso:
{
"groups": [
{
"type": "client",
"label": "Clientes",
"count": 1,
"items": [
{
"id": "cli-1",
"type": "client",
"title": "Acme SL",
"subtitle": "Cliente activo",
"followUpTool": "crm_clients"
}
]
}
],
"meta": {
"query": "acme",
"returned": 1,
"totalMatches": 4,
"limit": 10,
"truncated": true,
"hasMoreByType": { "client": false, "contact": true },
"moreTypes": ["task"],
"searchedTypes": ["client", "contact", "task"],
"excludedByToken": ["supplier"]
}
}Cada resultado lleva lo justo: identificador, tipo, título, subtítulo si la ficha tiene descripción, la marca de baja si la tiene (inactive e inactiveLabel, como «Anulada» o «Perdido»), un estado vivo que cambia la lectura si lo hay (stateLabel, como «Borrador» o «Vencida») y followUpTool. El detalle se pide a la herramienta que nombra followUpTool con ese mismo identificador.
El buscador no pagina. Devuelve una tanda ordenada por relevancia y repartida entre tipos; limit fija su tamaño. meta.hasMoreByType dice, tipo a tipo, si hay más coincidencias de las servidas, y meta.moreTypes nombra los tipos con coincidencias que no han cabido. meta.truncated es true cuando se da cualquiera de las dos cosas. Si necesitas recorrerlo todo, acota con types y afina el texto.
meta.totalMatches se conserva por compatibilidad, pero cuenta las filas que el servidor ha leído (cada tipo con su tope), no las coincidencias que existen: para saber si hay más, usa hasMoreByType.
Cómo interpreta el texto
El buscador ordena por parecido con el nombre de cada ficha: primero la coincidencia exacta, luego lo que empieza por lo que has escrito, después las palabras en cualquier orden y, al final, lo que solo coincide en otro campo (el correo, las notas o el cliente de un presupuesto). Además:
- Las palabras vacías no se exigen. «facturas de acme» busca «acme» entre las facturas, y «clínica la paz» encuentra también «Clínica Paz», por detrás de «Clínica La Paz». Lo mismo con las formas societarias («SL», «S.A.»).
- Una o dos letras solo cuentan como principio de palabra. «ma» encuentra «María» o «Grupo Marea», no «hotmail».
- Los códigos se encuentran como se recuerdan. «45» o «00045» encuentran «FAC-2026-00045», y «fac202600045» encuentra «FAC-202600045».
- Los teléfonos se encuentran con cualquier formato. «+34 612 34 56 78» y «612345678» encuentran la misma ficha.
- El NIF, solo donde puedes leerlo. «B-12.345.678», «b12345678» y «ESB12345678» encuentran la misma ficha. En facturas y gastos basta el permiso del documento, porque el NIF va impreso en él. En clientes, proveedores y documentos digitalizados hace falta además el permiso de ver sus datos fiscales y, en el token,
crm:clients:view-sensitiveofinance:suppliers:view-sensitive. El NIF nunca viaja en el resultado. - Nombrar un tipo centra la búsqueda. «presupuesto domingo» busca «domingo» entre los presupuestos y el resto de tipos con el texto entero. «mis tareas» se queda con las tuyas, y «rectificativas», «abonos», «llamadas» o «visitas» con esa clase. «Incidencias» nombra los tickets de soporte, e «issues», los elementos de trabajo. La respuesta lo cuenta en
meta.intentTypesymeta.appliedFilters; estos filtros solo estrechan, nunca abren filas fuera de tu alcance. - Erratas. Si la búsqueda exacta no encuentra nada, se repite tolerando erratas en las palabras de cuatro letras o más («rodriges» encuentra «Rodríguez»), y la respuesta lo marca con
meta.approximate: true. Trata esos resultados como sugerencias.
Los tipos que pides y el token no alcanza se descartan y se nombran en meta.excludedByToken. Si no alcanzas ninguno de los pedidos, la llamada falla con INSUFFICIENT_SCOPE y dice qué scopes harían falta.
Operación types
No recibe parámetros y dice qué tipos puedes buscar en esta llamada y por qué no puedes buscar los demás:
{
"available": [
{ "type": "client", "label": "Clientes", "scope": "ALL", "followUpTool": "crm_clients" },
{ "type": "expense", "label": "Gastos", "scope": "OWN", "followUpTool": "expenses" }
],
"unavailable": [
{ "type": "supplier", "label": "Proveedores", "reason": "token" }
],
"meta": {
"availableCount": 2,
"unavailableCount": 24,
"reasons": { "plan": "…", "permission": "…", "token": "…" }
}
}Cada tipo pasa tres filtros, en este orden, y la respuesta da en reason el primero que falla. Dos tipos que no dependen de un aplicativo también pueden salir con reason: plan: los tickets de soporte, si tu plan no incluye los tickets propios de la organización, y las personas de tu organización, si solo las alcanzas como directorio (sin el permiso de ver usuarios) y tu plan no incluye el chat:
Por qué un tipo no se puede buscar
reason: plan
Plan
Tu organización tiene contratado el aplicativo de ese tipo.Si no: el tipo sale en unavailable con reason plan.
reason: permission
Permiso
Tienes el permiso de ver ese tipo.Si no: sale con reason permission.
reason: token
Token
El token lleva el scope de lectura de ese tipo.Si no: sale con reason token, aunque tengas el permiso.
available
Disponible
Se busca, recortado a tu alcance de filas.
En los tipos disponibles, scope es tu alcance de filas sobre ese tipo (ALL, DEPARTMENT, ASSIGNED u OWN), que decide cuántas filas verás de verdad: con alcance OWN sobre gastos encuentras los tuyos y ninguno más.
Llama a types antes de acotar una búsqueda: te ahorra llamadas fallidas y evita que tu asistente concluya que algo «no existe» cuando lo que pasa es que no lo puede ver.
Scopes que hacen falta
El buscador no tiene scope propio: cada tipo de ficha exige el de lectura de su módulo. Con uno basta para llamar a la herramienta, y el recorte se aplica tipo a tipo.
| Tipo de ficha | Scope de lectura |
|---|---|
| Clientes, contactos, leads, proyectos y oportunidades | crm:clients:view, crm:contacts:view, crm:leads:view, crm:projects:view, crm:opportunities:view |
| Presupuestos, facturas y cobros recurrentes, tareas e interacciones | crm:budgets:view, crm:invoices:view, crm:tasks:view, crm:interactions:view |
| Elementos de trabajo, ciclos y proyectos de trabajo | dev:issues:view, dev:sprints:view, dev:projects:view |
| Archivos y carpetas | dev:files:view o crm:files:view |
| Tickets de soporte | support:tickets:view |
| Proveedores, sus personas de contacto y su actividad | finance:suppliers:view, finance:supplier-contacts:view, finance:suppliers:interactions:read |
| Gastos y documentos digitalizados | finance:expenses:view, finance:payments:view |
| Personas de tu organización | org:team:view |
Los formularios de captación no se buscan por MCP: el tipo lead_form existe en el buscador, pero aún no tiene un scope conectado, así que siempre sale como no disponible por el token.
notifications: los avisos de quien autorizó el token
Gestiona tus avisos y tus preferencias de aviso. Está en todos los servidores y nunca da acceso a los de otra persona: el dueño sale del token, no de un parámetro. Un aviso ajeno responde «no encontrado», igual que uno inexistente.
| Operación | Qué hace | Scope |
|---|---|---|
list | Lista tus avisos, paginados por cursor. | org:notifications:view |
get | Detalle de un aviso tuyo, siempre en vista detallada. | org:notifications:view |
unread_count | Cuántos tienes y cuántos sin leer, por tipo y por estado. | org:notifications:view |
get_preferences | Tus preferencias, con las categorías que puedes configurar y los canales. | org:notifications:view |
mark_read | Marca como leídos los avisos indicados, hasta 200 por llamada. | org:notifications:manage |
mark_all_read | Marca todos como leídos. Exige confirm: true. | org:notifications:manage |
dismiss | Descarta los avisos indicados. Exige confirm: true. | org:notifications:manage |
dismiss_all | Descarta todos. Exige confirm: true. | org:notifications:manage |
update_preferences | Cambia solo los ajustes que envíes y devuelve en changes cada uno que ha cambiado. | org:notifications:manage |
Descartar no es borrar: dismiss marca el aviso y conserva la fila para que el mismo aviso no vuelva a emitirse.
Filtros del listado
| Parámetro | Notas |
|---|---|
unread_only | No se puede combinar con status. |
status | PENDING, SENT, DELIVERED, FAILED, READ o CLICKED. |
type | Tipo concreto de aviso. Se combina con category si van los dos. |
category | Una de las categorías de preferencia, las mismas que devuelve get_preferences. |
created_from y created_to | Fecha ISO 8601. Se toma el día completo en la zona horaria de tu organización. |
response_format | concise (por defecto) o detailed. |
cursor y limit | Cursor opaco; 20 por defecto y 100 como máximo. |
Cada aviso trae identificador, tipo, título, cuerpo (recortado a 200 caracteres en la vista compacta), estado, si está leído, fecha, categoría y entity: la ficha a la que apunta, con la herramienta para consultarla. La vista detallada añade la clave de preferencia y las fechas de lectura, pulsación y envío. El contenido interno del aviso y sus errores de entrega no viajan nunca.
Permisos y preferencias
Ninguna operación exige permiso, igual que en la aplicación: tus avisos son tuyos. Lo que sí se recorta por plan y por permiso son las categorías de preferencia, y get_preferences devuelve solo las que puedes configurar de verdad.
update_preferences recibe solo lo que cambia, como categoría, tipo de aviso y canal (inApp, push o email) con true o false. Por ejemplo, { "issues": { "overdue": { "email": false } } } deja de enviarte por correo los avisos de vencidos y no toca nada más: ni los otros canales de ese aviso ni los demás avisos de la categoría. La respuesta trae en changes cada ajuste con su valor anterior y el nuevo; si ya estaba así, no se guarda nada y lo dice en unchanged.
Se rechaza entera, sin guardar nada, si nombra una categoría, un tipo o un canal que no existe, o si un valor no es true o false (el texto "false" también se rechaza). Si toca una categoría que no puedes configurar, el mensaje no dice cuál ni por qué, para que no sirva para averiguar el plan de tu organización.
Scopes y autorización
Los dos scopes son org:notifications:view y org:notifications:manage. Viajan con el token de cualquier servidor, igual que org:plan:view, y los incluyen los conjuntos de cada área: <área>:read incluye el de ver, y <área>:write y <área>:full, los dos.
Un token solo de avisos no abre ningún servidor
Estos scopes acompañan a otros, no van solos: un token que solo los lleve se emite, pero se rechaza al usarlo. Pide siempre al menos un scope del área con la que vas a trabajar.
Ejemplo con el SDK
Los SDK exponen las dos herramientas como sdk.mcp.search y sdk.mcp.notifications. Llaman al servidor de Gestión de proyectos, así que buscan con los scopes de ese servidor; para buscar fichas del CRM o de Finanzas, llama a search por JSON-RPC en esos servidores. Necesitan una sesión OAuth, como se explica en Primeros pasos con MCP.
// Qué puedo buscar con este token
const { data: tipos } = await sdk.mcp.search.types();
const alcanzables = tipos.available.map((t) => t.type);
// Buscar y profundizar
const { data } = await sdk.mcp.search.global({ q: 'migración', limit: 20 });
for (const grupo of data.groups) {
for (const item of grupo.items) {
console.log(grupo.label, item.title, item.followUpTool);
}
}
// Marcar como leídos los avisos pendientes
const { data: cuenta } = await sdk.mcp.notifications.unreadCount();
if (cuenta.unread > 0) {
const { data: avisos } = await sdk.mcp.notifications.list({ unread_only: true });
await sdk.mcp.notifications.markRead(avisos.items.map((n) => n.id));
}Los métodos de las operaciones que exigen confirmación (markAllRead, dismiss y dismissAll) la envían por ti.
Siguiente paso
Con el identificador que devuelve search, pide la ficha completa a la herramienta de followUpTool. Las herramientas de cada área y sus scopes están en la referencia de herramientas MCP, y cómo se configuran los avisos desde la aplicación, en la guía de Avisos.