Desarrolladores
Servidor MCP
El servidor del Model Context Protocol (MCP) permite a los agentes de IA leer y editar tus formularios de formbase con herramientas enriquecidas basadas en esquemas.
Qué es
MCP es un estándar abierto para que las herramientas de IA se conecten a servicios externos. formbase expone un endpoint MCP alojado al que cualquier cliente compatible con MCP puede conectarse, incluyendo Claude Code, Claude desktop y Cursor.
Conexión
URL: https://api.formbase.so/api/mcp (POST, streamable HTTP)
Auth: Bearer <token>Funcionan dos tipos de token Bearer:
Token de API (
fb_…) — se crea desde tokens de API. Ideal para uso personal y configuración rápida.Token de acceso OAuth (
fbo_…) — emitido por el flujo OAuth. Ideal para aplicaciones de terceros que se conectan en nombre de un usuario.
Ambos quedan vinculados a exactamente un espacio de trabajo y llegan a las mismas herramientas. Una llamada a una herramienta que nombre
otro espacio de trabajo, o un formulario de otro, falla con FORBIDDEN. Los tokens OAuth también llevan ámbitos
mcp:read, mcp:write y offline_access, pero hoy ninguna herramienta está restringida por ellos —
trata cualquier token como acceso completo dentro de su espacio de trabajo.
Las llamadas a herramientas están limitadas a 120 por minuto por token, compartidas con la API: solo
tools/call gasta el presupuesto, mientras que initialize, tools/list, prompts/ y
son gratis. Al superar el límite, la llamada igualmente devuelve HTTP 200 con un resultado de herramienta fallido
que lleva resources/RATE_LIMITED y un retryAfterMs — sondea con un temporizador, nunca en un bucle.
Dónde obtener un token
Abre OAuth y claves de API en la barra lateral de tu espacio de trabajo para crear tokens de API y ver las apps OAuth conectadas. Consulta tokens de API para una guía paso a paso.
Herramientas principales
Todas las herramientas se anuncian en tools/list cuando un cliente se conecta. Los clientes que cargan esquemas bajo demanda,
como Claude Code, obtienen el esquema completo de una herramienta cuando una tarea lo necesita. La tabla a continuación cubre las
herramientas principales con las que empiezan la mayoría de las tareas; load_tools (catálogos) y load_skill
(guías de dominio) documentan el resto.
| Herramienta | Qué hace |
|---|---|
| form_list | Lista los formularios de un espacio de trabajo. Admite filtro por carpeta, búsqueda aproximada por nombre y paginación con cursor. |
| form_get | Obtiene todos los detalles de un formulario: preguntas, portada, logotipo y una URL de vista previa en vivo. |
| form_create | Crea un nuevo formulario vacío en un espacio de trabajo. Devuelve una URL de vista previa para edición en vivo. |
| form_update | Actualiza los metadatos del formulario: nombre, carpeta, emoji, portada o logotipo. |
| form_delete | Elimina un formulario de forma suave (lo mueve a la papelera y revoca los enlaces de compartición). |
| form_publish | Publica un formulario para que pueda recibir respuestas. Idempotente. |
| workspace_list | Lista todos los espacios de trabajo accesibles con tu token. |
| workspaceFolder_list | Lista las carpetas de un espacio de trabajo. |
| formSubmission_list | Lista los envíos de un formulario con paginación. Incluye borradores en Pro y Business; Free lista solo las respuestas completadas. |
| fields_list | Lista las claves de campo que una solicitud puede dirigir en un formulario publicado, cada una con su tipo de valor, las claves de las opciones y una línea de uso. Llámalo antes de request_create. |
| request_create | Asigna un formulario publicado a un destinatario con nombre: precompletado, campos bloqueados, contexto, entrega, caducidad y un callback opcional. |
| request_get | Lee una solicitud: estado, cronología y — una vez completada — las respuestas indexadas por clave de campo, más display. |
| request_list | Lista solicitudes de un formulario o de todo un espacio de trabajo, filtradas por estado, resultado o tu propio id externo. |
| editor_getDocument | Obtiene la estructura completa del documento de un formulario: todos los elementos, sus tipos y propiedades. |
| editor_updateElement | Edita un elemento existente del formulario: reemplaza su texto, cambia propiedades (título, obligatorio, etc.) o lo reposiciona. |
| editor_deleteElement | Elimina un elemento del formulario. |
| editor_insertTextQuestion | Inserta una pregunta de texto corto o largo. Cada tipo de pregunta tiene su propia herramienta de inserción con un esquema preciso. |
| editor_insertContactQuestion | Inserta una pregunta de correo electrónico, número de teléfono o URL de sitio web. |
| editor_insertNumberQuestion · editor_insertDateQuestion | Inserta una pregunta de número o fecha. |
| editor_insertRadioQuestion · editor_insertCheckboxQuestion · editor_insertSelectQuestion | Inserta preguntas de opción única (radio), opción múltiple (casillas de verificación) o lista desplegable. |
| editor_insertDecisionQuestion | Inserta la pregunta de decisión: la opción aprobar / rechazar / cambios cuya respuesta se convierte en el resultado de una solicitud. Un radio creado a mano nunca produce uno. |
| editor_insertRatingQuestion · editor_insertLinearScaleQuestion | Inserta una pregunta de valoración por estrellas o escala lineal. |
| editor_insertHeader · editor_insertParagraph · editor_insertImage · editor_insertPageDivider | Inserta contenido que no es pregunta: encabezados, párrafos, imágenes y saltos de página. |
| load_tools | Carga la documentación de un catálogo de herramientas. Devuelve esquemas y patrones de uso para herramientas agrupadas. |
| load_skill | Carga una guía de conocimiento de dominio (temas, tipos de pregunta, reglas de lógica, etc.). |
Más variantes de inserción — hora, carga de archivos, firma, pago, matriz/cuadrícula, clasificación, selección de imagen, interruptor,
tabla, lista, fila, campo calculado, campo oculto, variable inline, contenido incrustado (editor_insertEmbedded para YouTube,
Google Maps o iframes) y un bloque de lógica condicional (editor_insertLogic) — también están en tools/list.
Carga load_skill(“question-types”) para el conjunto completo, cada uno con su nombre de herramienta y campos. La lógica
condicional se crea con editor_setLogic en el catálogo editor-actions.
Catálogos de herramientas
Estas herramientas también están en tools/list. Ejecuta load_tools con un nombre de catálogo para obtener
documentación enriquecida (introducción, esquemas completos, patrones de uso, casos límite) para las herramientas agrupadas, y luego
llámalas directamente.
| Catálogo | Herramientas incluidas |
|---|---|
| form-data | formAnalytics_get — métricas agregadas (vistas, envíos, tasa de completación, desglose por dispositivo, país, navegador y fuente). Requiere el plan Pro o Business; sin él, la llamada falla con un mensaje que indica el plan. |
| form-appearance | formTheme_get, formTheme_set, form_update — temas por modo (claro y oscuro), además de la portada y el logotipo en form_update. |
| form-behavior | formSettings_get, formSettings_update — notificaciones por correo, redirección al completar, contraseña, retención, idioma, pago. |
| form-sharing | formShareLink_list, formShareLink_create, formShareLink_update — CRUD de enlaces de compartición con soporte de dominio personalizado. |
| form-translations | translationLanguage_list, translationDraft_get, translationDraft_update, translationDraft_publish, translationLanguage_delete — flujo de trabajo multilingüe de borrador y publicación. Publicar un borrador vacío es la única forma de despublicar un idioma. |
| form-lifecycle | form_unpublish, form_restore — operaciones de ciclo de vida más allá de publicar y eliminar. |
| workspace-management | workspaceFolder_create, workspaceFolder_update, workspaceFolder_delete — CRUD de carpetas más allá de los verbos de lista principales. |
| editor-actions | editor_formatText, editor_setLogic, editor_testLogic — formato de texto, creación de lógica condicional y simulación de lógica. |
| request-lifecycle | fields_list, request_create, request_get, request_list, request_cancel, request_remind, request_replayCallback, document_create — toda la superficie de solicitudes: retira una solicitud pendiente, envía un recordatorio al destinatario, repite un callback que nunca llegó, reserva la subida de un documento para una solicitud. |
| editor-inserts | La larga cola de herramientas editor_insert*: hora, interruptor, archivo, firma, bloque de documentos, matriz, clasificación, pago, cita, selección de imagen, contenido incrustado, tabla, lista, fila, campo calculado, campo oculto, grupo repetible, lógica y variable. |
El catálogo request-lifecycle lista las ocho herramientas de solicitudes porque el chat integrado anuncia un conjunto
principal más reducido. En este endpoint las ocho ya están en tools/list, así que lo que el catálogo añade es documentación.
Skills (conocimiento de dominio)
Los skills son guías integradas que el agente puede cargar mediante load_skill. Ofrecen conocimiento de dominio que ayuda al
agente a tomar mejores decisiones — no son esquemas de herramientas, sino consejos de diseño y semántica de campos.
| Skill | Qué cubre |
|---|---|
| question-types | Cada tipo de pregunta, sus campos y cuándo usar cada uno. |
| logic-rules | Lógica condicional: operadores, acciones, combinadores y casos límite. |
| editing-flows | Patrones para construir formularios: orden, saltos de página, piping. |
| form-best-practices | Directrices de UX para un diseño de formularios efectivo. |
| form-themes | Estructura de temas, referencia de tokens y directrices de estilo. |
| form-settings | Referencia de ajustes: notificaciones, plantillas de correo electrónico, variables. |
| analytics | Definiciones de métricas y cómo interpretar los análisis de formularios. |
| toon-format | Formato de salida compacto para la visualización de datos estructurados. |
| requests | Solicitudes de principio a fin: claves de campo, formas de valor de precompletado, entrega, documentos por solicitud, callbacks, sondeo y recuperación de errores. |
Solicitudes
Una solicitud asigna un formulario publicado a una persona con nombre, con su propio enlace, sus propias respuestas precompletadas y su propio resultado. Así es como un agente pide algo a una persona real y descubre qué ha respondido.
Crear una solicitud
Empieza siempre con fields_list(formId). Devuelve las claves direccionables de la versión publicada actual del formulario,
cada una con una línea usage que indica en qué argumento va la clave — las preguntas visibles van en prefill,
los campos ocultos en context. Nunca derives una clave a partir del título de una pregunta, y vuelve a leer después de
form_publish.
{
"formId": "j57...",
"recipient": { "email": "ada@acme.com", "name": "Ada" },
"prefill": { "company_name": "Acme", "plan": "pro" },
"readonly": ["company_name"],
"context": { "crm_id": "A-42" },
"metadata": { "run_id": "exec_918" },
"delivery": "email",
"expiresAt": 1780000000000,
"callbackUrl": "https://hooks.acme.com/formbase",
"idempotencyKey": "po-42"
}El resultado incluye el enlace y el reloj:
{
"id": "kd7...",
"status": "pending",
"url": "https://form.formbase.so/r/rq_...",
"deliveryStatus": "queued",
"expiresAt": 1780000000000,
"createdAt": 1747000000000,
"deduplicated": false,
"next": "..."
}delivery vale “none” por defecto, lo que te da url para que la entregues tú mismo;
“email” envía la invitación y necesita recipient.email en un plan Pro o Business, o con una de las 10
invitaciones gratis de una cuenta Free. readonly bloquea campos que el destinatario no puede editar, y toda clave bloqueada
también debe estar precompletada. context solo acepta claves de campos ocultos, mientras que metadata es
contabilidad opaca que se refleja en request_get y en el callback. expiresAt son milisegundos epoch, con 30 días
por defecto y un máximo de 365 días. idempotencyKey tiene alcance de espacio de trabajo durante 30 días: la misma clave con
el mismo cuerpo devuelve la solicitud original con deduplicated: true, y un cuerpo distinto es un conflicto. Cada respuesta
también incluye una línea next que le dice al agente qué hacer a continuación.
deliveryStatus es not_requested hasta que se pone en cola una invitación, luego queued →
sent o failed, y bounced en cuanto el proveedor de correo reporta un rebote definitivo o una queja.
Crear una solicitud gasta una unidad de la asignación mensual del espacio de trabajo, responda o no el destinatario; una vez agotada,
request_create falla con MONTHLY_ALLOWANCE_REACHED.
Callbacks o sondeo
Con una callbackUrl, formbase envía un POST una vez por cada evento terminal — finalización, expiración, cancelación —
firmado con el secreto de firma de solicitudes del espacio de trabajo. Consulta Callbacks y firma
para conocer el payload y la receta de verificación.
Los agentes autónomos deberían sondear
El secreto de firma de solicitudes solo aparece en la página de Credenciales de tu espacio de trabajo — nunca se devuelve por MCP ni por
la API. Un agente que se ejecuta por su cuenta, sin ninguna persona que levante y configure un receptor, no puede por tanto verificar un
callback. Omite callbackUrl y en su lugar consulta request_get(requestId) periódicamente, del orden de minutos
y no de segundos, hasta que status deje de ser “pending”. expiresAt acota cuánto tiempo merece la
pena hacerlo.
Modo de prueba
Pasa test: true para ensayar todo el circuito antes de una ejecución real. El enlace se sigue abriendo y se puede completar,
y el callback se dispara con “test”: true — pero no se envía nada por correo sea lo que sea lo que diga delivery
y la solicitud queda oculta de la página de Solicitudes y del embudo de analíticas, y su envío no cuenta en ningún sitio: ni cuota, ni
exportaciones, ni integraciones. Las solicitudes de prueba solo aparecen en request_list cuando pasas
includeTest: true. El enlace se cierra en un plazo de 24 horas, y en Free un workspace puede crear 10 solicitudes de prueba
al día.
Documentos por solicitud
Para entregarle a un destinatario un archivo — un borrador de contrato, su propio presupuesto — el formulario necesita un
bloque de Documentos, que un autor o un agente inserta con editor_insertDocumentsBlock.
fields_list lo reporta como type: “documents”. Los bytes nunca pasan por una herramienta:
Llama a
document_createconformId,name,contentTypey elsizeexacto en bytes. Recibes{ id, name, contentType, size, uploadUrl, expiresAt }. Solo PDF e imágenes (sin documentos de Office), 25 MB por archivo, y 100 MB de documentos por solicitud.Haz un
PUTde los bytes en bruto auploadUrlantes de que pase una hora, conContent-Typeajustado al tipo que declaraste.Referéncialo desde
request_create:documents: [{ documentId, field?, name? }].fieldes la clave de campo del bloque de Documentos, opcional solo cuando el formulario tiene exactamente un bloque de ese tipo.namesobrescribe el nombre visible para esta solicitud.
Los documentos de autor del bloque permanecen intactos y los tuyos aparecen debajo, solo para este destinatario.
request_create verifica la subida antes de que la solicitud exista, así que DOCUMENT_NOT_UPLOADED significa que
el paso 2 se saltó. Una subida puede ser referenciada por cualquier número de solicitudes, y sus bytes cuentan contra el almacenamiento de
tu espacio de trabajo.
Dominios personalizados
Pasa domainId a request_create para generar el enlace en uno de los
dominios personalizados del espacio de trabajo. Los ids vienen de
formShareLink_list, que los devuelve como availableCustomDomains. Si lo omites, el enlace usa el dominio en el
que el formulario ya está publicado.
Leer los resultados
request_get devuelve la solicitud completa. Una vez completada, answers contiene los valores del destinatario
indexados por clave de campo, display las mismas claves como texto legible, y outcome — aprobación, rechazo o
cambios — es su veredicto cuando el formulario tiene una pregunta de decisión. Una marca de tiempo
callbackFailedAt significa que la entrega se quedó sin reintentos y nada llegó a tu endpoint; arregla el receptor y luego
llama a request_replayCallback, que reenvía el id del evento original para que tu receptor deduplique en lugar de volver a
ejecutarse. Tras que la política de retención del formulario elimine una solicitud, se establece dataPurgedAt y las
respuestas se pierden para siempre.
request_list recorre muchas a la vez, filtradas por status, outcome, externalId e
includeTest. Pagina con nextCursor: una página puede, en raras ocasiones, volver con items vacío y
hasMore: true, lo cual no es el final de la lista — pasa el cursor de vuelta y sigue.
Recursos y prompts
Cada skill y catálogo de herramientas es también un recurso MCP en skill://<name> — skill://requests,
skill://editor-inserts. Un cliente que admite resources/list puede explorarlos y leerlos sin necesidad de llamar
a load_skill o load_tools. El servidor también sirve cuatro prompts en prompts/list:
identity, capabilities, data_tools y editor_tools.
Herramientas que piden confirmación
Cada herramienta lleva las pistas MCP readOnlyHint y destructiveHint, derivadas de su verbo. Estas herramientas
están marcadas como destructivas, porque deshacerlas exige otra llamada o no es posible: form_delete,
form_unpublish, workspaceFolder_delete, editor_deleteElement,
translationLanguage_delete y request_cancel. La mayoría de los clientes piden confirmación al usuario antes de
ejecutarlas, pero ese aviso es decisión del cliente, así que revisa sus ajustes de aprobación si necesitas un bloqueo estricto.
Limitaciones
Sin subidas binarias mediante una llamada a herramienta. Las imágenes se establecen por URL: portadas, logotipos y bloques de imagen aceptan URIs
http(s)://odata:image. Un documento por solicitud es la excepción:document_createdevuelve una URL de subida que un cliente con acceso HTTP puede subir conPUT(consulta Documentos por solicitud). Para convertir un PDF o una captura de pantalla en un formulario, usa el chat de IA integrado.Sin AI Skills del espacio de trabajo. Las habilidades escritas en formbase solo están disponibles en el chat de IA integrado. Las skills propias del servidor (
load_skill) sí están disponibles por MCP.
Conectar con un token de API
La mayoría de los clientes inician sesión con OAuth: pega la URL sin ninguna cabecera y sigue Conecta un agente de IA. Un cliente que no puede abrir un navegador, como un script, un job de CI o un agente sin interfaz, envía en su lugar un token de API como cabecera.
Claude Code, desde la línea de comandos:
claude mcp add --transport http formbase https://api.formbase.so/api/mcp \
--header "Authorization: Bearer fb_YOUR_TOKEN"O en el .mcp.json de un proyecto:
{
"mcpServers": {
"formbase": {
"type": "http",
"url": "https://api.formbase.so/api/mcp",
"headers": {
"Authorization": "Bearer fb_YOUR_TOKEN"
}
}
}
}Cursor, en .cursor/mcp.json o ~/.cursor/mcp.json:
{
"mcpServers": {
"formbase": {
"url": "https://api.formbase.so/api/mcp",
"headers": {
"Authorization": "Bearer fb_YOUR_TOKEN"
}
}
}
}Para iniciar sesión con OAuth en lugar de un token, usa Añadir a Cursor . Añade la URL del servidor sin cabecera, y Cursor te pide que inicies sesión en formbase.
Otros clientes usan la misma URL y cabecera; consulta su documentación para saber dónde.
Usar OAuth en lugar de tokens de API
Una aplicación de terceros que se conecta en nombre de un usuario debería usar OAuth en lugar de pedir un token pegado. formbase es un
servidor de autorización OAuth 2.1 con PKCE obligatorio (S256) y tokens opacos — sin JWT, sin implicit grant. Claude desktop, Claude Code
y el conector web de Claude.ai descubren todo esto desde el endpoint MCP, así que pegar la URL sin ninguna cabecera es suficiente: el 401
apunta a /.well-known/oauth-protected-resource, y el cliente se encarga del resto.
El flujo, para un cliente que escribas tú mismo:
GET /.well-known/oauth-protected-resource, luegoGET /.well-known/oauth-authorization-serverpara las URLs de los endpoints, los ámbitos y los métodos de autenticación admitidos.POST /oauth/registercon tusredirect_uris(registro dinámico de cliente, sin necesidad de credenciales). Recibes unclient_id, más unclient_secretsi pediste algo distinto detoken_endpoint_auth_method: “none”. Las redirect URIs deben ser HTTPS, o HTTP enlocalhost. El registro está limitado a 20 por hora por IP.Envía al usuario a
/oauth/authorizeconresponse_type=code, tuclient_id, elredirect_uriregistrado,scope=mcp:read mcp:write offline_access,state, y uncode_challengeconcode_challenge_method=S256. Inicia sesión, elige un espacio de trabajo y autoriza.Intercambia el código en
POST /oauth/tokencongrant_type=authorization_codey tucode_verifier, en un plazo de 60 segundos. Los códigos son de un solo uso.Llama al endpoint MCP con
Authorization: Bearer fbo_…. Los tokens de acceso duran 1 hora; los tokens de actualización duran 30 días y rotan en cada uso. Reutilizar un token de actualización ya gastado quema toda la cadena, así que guarda el más reciente.
POST /oauth/revoke (RFC 7009) revoca un token de acceso o de actualización. Un usuario también puede desconectar toda la
aplicación desde Aplicaciones conectadas en la página OAuth y claves de API, lo que elimina todos los tokens que tiene
para ese espacio de trabajo.
Si ya tienes un token en la mano, va en el mismo lugar que una clave de API:
{
"mcpServers": {
"formbase": {
"type": "http",
"url": "https://api.formbase.so/api/mcp",
"headers": {
"Authorization": "Bearer fbo_USER_OAUTH_TOKEN"
}
}
}
}Aplicaciones conectadas
Cada conexión OAuth aparece listada en Aplicaciones conectadas, en la página OAuth y claves de API, con la fecha en que se conectó y su último uso. Las conexiones son personales: solo el usuario que la autorizó puede verla, y los administradores del espacio de trabajo no pueden ver ni revocar la de otro miembro. Desconectar surte efecto de inmediato. Un usuario que abandona el espacio de trabajo, o es eliminado de él, pierde todas sus conexiones a ese espacio.