API y MCP

Un endpoint, 1008 declaraciones, y 331 de ellas son suyas.

En esta plataforma no hay tabla de rutas. http_module registra exactamente dos manejadores de Express, app.post("*") y app.get("*"), lee el tercer segmento de ruta de /cpi/<method> y busca ese nombre en el objeto cpi_methods dentro de config.json. Ese objeto contiene 1008 entradas. Un nombre que no esté en él vuelve como {code: -1, data: 'method "X" not exist'} antes siquiera de leer ninguna credencial, en cpi/http_module/index.mjs, líneas 634 a 650. Añadir un método a ese objeto es todo el acto de publicar una API: es lo que hace que el método se pueda invocar por HTTP, sea visible como herramienta MCP, esté condicionado por plan, se cachee, se registre en auditoría y se rastree como trabajo. No hay un segundo lugar donde registrarlo ni un cliente generado que haya que regenerar. 677 de las 1008 entradas llevan private: true, que es el muro de operador que ninguna clave de API cruza en ningún modo, así que lo que un llamante externo puede alcanzar realmente son 331 métodos, y tanto el catálogo REST como la lista de herramientas MCP se compilan a partir de ese mismo conjunto. El resto de esta página sigue una llamada a lo largo de las siete etapas que recorre, muestra la transcripción real en ambos sentidos y señala los puntos en los que el código distribuido hace menos de lo que la interfaz que lo rodea sugiere.

POST /cpi/<method>, GET también funciona
Cinco cadenas de autorización, una sola barrera
331 métodos, 331 herramientas MCP
Un endpoint, 1008 declaraciones, y 331 de ellas son suyas.
1008

Métodos declarados en un solo archivo de configuración

331

Alcanzables con una clave de API de desarrollador

5

Cadenas de autorización que usa toda la superficie

2003

Definiciones de campo tipadas, 1102 de ellas obligatorias

El camino de una petición

Toda llamada recorre las mismas siete etapas, y hay cuatro salidas antes de que su manejador se ejecute.

Un método es un nombre, y todos los nombres recorren el mismo camino. El tráfico público llega a router_module, que termina el puerto 443, conmuta según el primer segmento de la ruta y canaliza el caso cpi directamente hacia localhost:3007. A partir de ahí el orden es fijo y conviene memorizarlo, porque una etapa está donde la mayoría de los lectores supone que no está: el contrato de campos tipados se recorre ANTES de la autenticación, no después, así que una petición a la que le falta un argumento obligatorio se rechaza sin que la plataforma llegue a mirar su credencial. La autenticación en sí es una única llamada al broker hacia session_module en lugar de un middleware, las dos barreras de cuenta leen un mismo documento de cuenta entre ambas, y el manejador se alcanza por RabbitMQ y no mediante una importación. Nada aquí se bifurca según el nombre del método salvo la búsqueda de la etapa dos.

Dos cosas que esta imagen deliberadamente no contiene. No hay limitador de frecuencia en la ruta cpi: router_module define tres buckets de express-rate-limit, y están montados en /login y en cuatro rutas de formulario, mientras que el caso cpi es un proxy sin más, sin middleware por delante. Las llamadas autenticadas están acotadas por el alcance de la credencial, no por la frecuencia de peticiones. Y en esta ruta no hay bloqueo por intentos fallidos en ninguna parte; nada en los archivos leídos cuenta ni suspende una clave que se rechaza una y otra vez.
En la línea

La línea, en sus dos mitades.

Aquí hay tres cosas que sorprenden a la gente, así que conviene verlas en lugar de leer sobre ellas. Primera, la clave de desarrollador viaja en el cuerpo JSON como api_key. http_module nunca lee una cabecera Authorization en esta ruta, y session_module extrae api_key directamente de la petición en la línea 116, así que un cliente que ponga la clave en una cabecera se autentica como nadie. Segunda, el éxito y el fallo son el mismo objeto: {code, data}, con code 1 o superior para el éxito y un número negativo para todo lo demás. Tercera, el estado HTTP se deriva del código en lugar de elegirse, mediante get_status en las líneas 204 a 210: solo un código en el rango de -400 a -499 se convierte en su propio estado, así que -401 es un 401 real y -428 es un 428 real, mientras que todo lo demás, incluido el abrumadoramente común -1, se reduce a un 400 sin más. Un cliente que se bifurca solo según el estado no puede distinguir un argumento incorrecto de un manejador que reventó. La única cabecera de respuesta expuesta mediante CORS es xu-region, sellada con XUDA_HOSTNAME.

POST https://xuda.ai/cpi/get_plugins
content-type: application/json (GET también funciona, la cadena de consulta se analiza hacia el cuerpo)
{ "app_id": "app_...", "api_key": "pk_..." }
la clave es un campo del cuerpo. En esta ruta no hay cabecera Authorization
< 200 xu-region: eu1
{ "code": 1, "data": [ ... ] }
un fallo es el mismo objeto, y el estado se deriva del signo
{ "code": -401, "data": "gtp_token expired" } 401
{ "code": -1, "data": "Error: 'App Name' is a mandatory field" } 400
{ "code": -428, "verification_required": true } 428
solo de -400 a -499 se convierten en su propio estado. -1 es el código más común, y siempre es 400.
Lea el código, no el estado. La línea del 400 y la del 401 son ambas fallos ordinarios del mismo endpoint, y solo la segunda lleva un estado que dice algo específico.
El muro

private es el muro, y abarca la mayoría del catálogo.

clearance decide quién tiene que ser el llamante. private decide si una credencial distinta de una sesión de navegador puede siquiera intentar el método, y es la marca que define la forma del producto visto desde fuera. 677 de los 1008 métodos declarados llevan private: true. Ningún modo de clave cruza esa línea: ni una clave de acceso completo, ni una clave personalizada con el método escrito en su lista de permitidos, ni una sesión MCP. Lo que queda son 331 métodos, y esos mismos 331 son los que ve todo consumidor externo, ya llegue como curl o como un agente con una lista de herramientas. Los cuatro puntos de abajo son la mecánica de ese muro, incluida la parte que es una convención y no una garantía.

  • La rama de la clave de API solo se toma cuando el método no es private ni pertenece a api_module: session_module, líneas 121 a 130, lee if (api_key and method.microservice is not api_module and not method.private). Un método privado, por tanto, nunca llega a la validación de la clave. En su lugar cae hacia la ruta de sesión, que un llamante que solo tiene clave no puede satisfacer, de modo que el rechazo se lee como un fallo de autenticación y no como un 404. api_module, líneas 172 a 181, aplica la misma regla una segunda vez con el mensaje literal api method <name> is unauthorized.
  • Los 8 métodos de gestión de claves son privados en sí mismos: create_account_api_key, update_account_api_key, get_account_api_keys, create_app_api_key, update_app_api_key, get_app_api_keys, get_account_system_api_methods y get_app_system_api_methods. Una clave, por tanto, nunca puede crear una clave, rotar una clave ni enumerar las claves de la cuenta a la que pertenece. El ciclo de vida de las claves es una acción exclusiva de una sesión del dashboard, y eso es una propiedad del catálogo más que una regla escrita en alguna parte.
  • mcp_module omite los private al registrar herramientas, así que REST y MCP se compilan a partir del conjunto idéntico. Una clave de acceso completo enumera Object.keys(cpi_methods), descarta los privados y registra los 331 restantes como herramientas con un esquema de entrada zod derivado de los campos declarados de cada método. La lista se resuelve de nuevo en cada sesión en lugar de quedar fijada en la clave, de modo que un método publicado después de emitir la clave se recoge sin editarla.
  • El matiz honesto: private está ausente en lugar de estar en false en 321 de las entradas, y solo 10 declaran private: false de forma explícita. El muro se define por la falsedad del valor, lo que significa que un método recién publicado sin campo private se puede invocar desde fuera de forma predeterminada. Eso es un valor predeterminado, no una decisión, y es lo único de esta página que conviene contrastar con sus propias expectativas antes de confiar en los 331.
El catálogo, en secciones
Declarados en cpi_methods
1008
private: true, solo para operadores
677
Alcanzables con una clave
331
Seleccionados para el agente propio de Xuda (mcp: true)
22
una pista de clasificación dentro de ai_module, no la barrera de MCP
Autorización

Cinco cadenas de autorización, y qué le pide realmente cada una.

Una cadena en la entrada de configuración decide qué credencial tiene que presentar un llamante, y el despachador nunca se bifurca según el nombre del método para averiguarlo: http_module entrega method.clearance a session_module.validate_cpi_access y deja que esa única función responda. Los recuentos y los códigos de éxito de abajo se leen de session_module, líneas 115 a 291, no se parafrasean de un documento. La fila en la que conviene detenerse es admin. La autorización admin es un rol de app, no un rol de plataforma: se resuelve a partir de una fila de la vista user_active_shares de xuda_team, de modo que un administrador de equipo en un proyecto satisface cualquiera de los 252 métodos admin en ese proyecto y nada más allá de él. El superusuario de plataforma es un concepto aparte que ni siquiera es un valor de clearance. Es un array de configuración con exactamente una entrada, comprobado como una excepción que devuelve code 104 y fuerza auth_clearance a admin, lo que significa que nunca puede expresar más que admin. Por tanto, cualquier método que deba ser solo para personal comprueba eso por su cuenta en su propio cuerpo, y 83 puntos repartidos en 25 archivos de módulo hacen exactamente eso devolviendo {code:-403, data:'superuser only'}. Eso es una convención más que un mecanismo: nada en el despachador verifica que un método reservado al personal lleve realmente su comprobación.

Métodos
Qué debe presentar el llamante
Qué devuelve la barrera
public51nada en absoluto, la comprobación regresa antes de leer ninguna credencialcode 100
user680un documento de sesión activo en xuda_sessions, sin app_idcode 106
user_access5app_id más una fila de equipo con user_group, developer o admincode 6
developer15app_id más developer o admincode 6
admin252app_id más admin, o ser el propietario de la appcode 6, coincidencia de propietario 106
superuserno es una autorizaciónun uid en el array superuser_account_ids, que tiene 1 entradacode 104, forzado a admin
vacía o ausente5nada la satisface: una clearance con valor falso lanza un error antes que cualquier otra ramacode -401. SIN VERIFICAR si estas cinco son entradas exclusivas del broker
Dos puertas de entrada

Dos puertas de entrada, una sola barrera.

mcp_module ejecuta su propia app de Express en el puerto 3012 y habla Streamable HTTP en /mcp, lo que hace que parezca un segundo modelo de permisos. No lo es. Cada manejador de herramienta que registra hace un fetch normal a http://localhost:3007/cpi/<method> con la api_key trasladada de la cabecera Authorization al cuerpo JSON, de modo que la autorización se comprueba exactamente una vez, en la misma llamada a validate_cpi_access, tanto si el llamante es curl como si es un agente. Las cinco filas de abajo están emparejadas a propósito: léalas de lado a lado y verá exactamente qué mitad de la historia cambia entre las dos puertas, y qué poco cambia.

El id de sesión se devuelve como mcp-session-id y se cierra a la fuerza tras un TTL estricto de 10 minutos. Algo que la puerta MCP añade y la puerta REST no puede: en initialize, el servidor inyecta la propia guía de creación de Studio de Xuda como instrucciones MCP, de modo que un modelo que se conecta lee el flujo de trabajo antes de su primera llamada a una herramienta.
Por módulo

La clasificación se invierte: los módulos más grandes exponen lo menos.

Treinta y cinco módulos poseen los 1008 métodos declarados, contados a partir del campo microservice de cada entrada de configuración. Ordenada por cuánto declara cada módulo, la lista se lee como un mapa de la plataforma: app_module 110, ai_module 91, account_module 90, drive_module 77, stripe_module 75. Ordenada por lo que una clave externa puede alcanzar realmente, ese orden se invierte, porque la segunda columna es simplemente la primera menos todo lo que lleva private:true. Siete módulos no exponen nada en absoluto, y no son los pequeños: stripe_module declara 75 métodos y no publica ninguno, marketplace_module 25 y ninguno, shipping_module 26 y ninguno, commerce_module 21 y ninguno, y bot_protection_module y jobs_module se les suman. La superficie del dinero y la superficie de la tienda son enteramente internas, alcanzables solo desde una sesión del dashboard con la sesión iniciada y la autorización adecuada, lo cual es una forma deliberada y no un vacío de documentación. La última fila es la más contundente: los ocho métodos de gestión de claves de api_module son privados, así que una clave nunca puede crear, rotar ni siquiera enumerar claves. Lea primero los ceros, y después lea la parte alta de la tabla. Una fila merece una segunda mirada más que un titular: voice_module muestra 46 de 46 invocables desde fuera, el único módulo de la plataforma sin ningún método privado, y eso conviene contrastarlo con la intención.

Declarados
Alcanzables con una clave
Drive7766
App11061
Voz4646
Cuenta9032
IA9126
Equipo5014
Stripe750
MarketPlace250
Envíos260
Commerce210
Claves de API80, una clave nunca puede gestionar claves
Cómo empezar

Tres pasos de cero a una llamada funcionando.

El dashboard no le entrega una página de primeros pasos, le entrega cuatro pestañas de copiar y ejecutar en la pantalla de claves de API: MCP, curl, node y php, con MCP como predeterminada y su propia primera clave ya sustituida en el fragmento. Conviene decirlo con claridad antes de empezar: no hay SDK para REST. La pestaña de node es una llamada fetch literal y la de php es un bloque curl_init literal, así que contra lo que está integrando es un endpoint HTTP y un sobre JSON, no una librería cliente con tipos.

  1. 01

    Cree una clave

    Dos ámbitos, cuenta o app, y dos modos de acceso. Una clave de acceso completo se comprueba únicamente contra private, y esa comprobación se ejecuta de nuevo en cada llamada en lugar de fijarse en el momento de la emisión, así que los métodos publicados después de crear la clave quedan cubiertos sin tocarla. Una clave personalizada lleva en su lugar una lista api_allow_methods, con una excepción que conviene conocer: los métodos con clearance public se saltan la lista de permitidos por completo, así que los 51 métodos anónimos son alcanzables con cualquier clave válida.

  2. 02

    Pregunte al catálogo qué puede invocar

    get_account_system_api_methods devuelve los 331 métodos no privados como {description, fields, category}, donde category es el microservicio propietario. Esos son los mismos datos a partir de los cuales se compilan los esquemas de herramientas MCP, así que el catálogo REST y la lista de herramientas del agente nunca pueden divergir. get_app_system_api_methods es la vista con ámbito de app y devuelve 86, el conjunto admin no privado. Ambos son privados en sí mismos, así que se leen desde una sesión del dashboard.

  3. 03

    Invóquela, o entregue la misma clave a un agente

    POST /cpi/<method> con api_key en el cuerpo, o ejecute claude mcp add contra /mcp con la clave como token bearer. La misma barrera, los mismos 331 métodos, dos sobres. Una clave que emita para su backend y una clave que emita para un agente no se diferencian en nada salvo en la puerta a la que la apunta, y por eso acotar lo que un agente puede hacer significa emitir una clave más estrecha en lugar de configurar el agente.

Llamadas largas

Una llamada larga responde de inmediato y termina por un socket.

Lo que la mayoría de los integradores entiende al revés: la ruta de trabajo es la predeterminada. http_module la ejecuta salvo que la entrada del método diga job:false, y 573 de las 1008 entradas se excluyen así, lo que deja 435 dentro. Esos 435 son los despliegues, las compilaciones, las reinstalaciones, el trabajo que nunca iba a terminar dentro de una sola petición. Un trabajo se registra antes del despacho en cualquier caso, así que la única pregunta es si usted pidió recibir el comprobante en lugar del resultado. Un límite en la misma frase: algunos métodos finalizan su propio trabajo, así que el contador de pasos es orientativo y no una garantía.

  • Envíe job:true en el cuerpo y la llamada devuelve un comprobante con el id del trabajo mientras el resultado real llega más tarde por socket.io en el puerto 4010. Dos revisiones de evidencia discrepan sobre el código numérico de ese comprobante, una lee 88 y otra lee 999 con info "job successfully scheduled to run immediately", así que trate el número como SIN VERIFICAR y lea el id del trabajo.
  • job_steps es el texto literal de la barra de progreso, no una máquina de estados interna. 38 métodos declaran uno: build_app declara 7, reinstall_spa_resources declara 18, desde iniciar el proceso hasta activar el servidor. A un trabajo sin pasos declarados se le asigna un total de 2.
  • job_title es una plantilla de lodash renderizada contra el cuerpo de su propia petición, y por eso el aviso dice Reinstalling SPA Server en lugar de un nombre de método. 36 métodos declaran uno.
  • broadcast:true en 194 métodos es lo que hace que la actualización no sea silenciosa; todo lo demás se rastrea en silencio. job_timeout limita la espera del proxy y toma 1800 segundos por defecto, y 7 métodos lo sobrescriben.
Una llamada larga
  1. register_job
    un documento de trabajo en memcached, TTL de 600 segundos
  2. Recibe un comprobante, no un resultado
    solo cuando el llamante lo pidió enviando job:true
  3. El worker informa del progreso
    update_job recorre los job_steps declarados
  4. socket.io, puerto 4010
    hacia la sala a la que se unió su navegador; stat 3 si tiene éxito, 4 si falla
Una entrada, siete lectores

Una declaración, siete consumidores.

Las claves de una entrada de cpi_methods no son documentación. Cada una la lee un subsistema distinto en un momento distinto, y ninguno de esos subsistemas conoce a los demás. clearance la lee la barrera de sesión. private la leen el validador de claves de API y, por separado, el registrador de herramientas MCP. fields lo recorre el comprobador que se ejecuta antes de la autenticación y se compila a un esquema zod cuando un agente abre una sesión. job lo lee la cola, cache lo leen memcached y la cabecera del edge, min_plan la barrera de plan, security el escritor del registro. Añadir un método a config.json decide las siete cosas a la vez, y por eso no hay tabla de rutas, ni archivo de permisos, ni esquema aparte que mantener sincronizado.

Dos detalles de esa fila de caché conviene conocerlos antes de confiar en ella. La clave se construye retirando gtp_token, job_id, token_ret y app_token del cuerpo de la petición y serializando lo que queda, así que uid permanece y las entradas son por usuario en lugar de compartidas en toda la cuenta. Y un método marcado con cache:true sin cache_timeout se escribe con expiration 0, que memcached interpreta como sin caducidad alguna, de modo que esa entrada vive hasta que la máquina se reinicie o algo la borre a mano.
Conectar un agente

Conectar un agente es una sola línea.

Esta es toda la superficie de integración. El endpoint MCP es un servidor Streamable HTTP en el puerto 3012 servido tras un proxy en /mcp, y es el único lugar de la plataforma donde una clave de API viaja como cabecera: cualquier otra ruta, incluida la llamada de loopback que cada herramienta hace de vuelta a /cpi, lee api_key desde el cuerpo JSON. Dos hechos deciden si esto funciona a su escala. La lista de herramientas se resuelve por sesión en lugar de quedar fijada en la clave, así que una clave de acceso completo recoge los métodos publicados después de emitirla sin editar la clave ni redesplegar. Y esa misma clave entrega a un modelo las 331 herramientas en una sola lista, sin agrupación ni paginación, lo cual supera lo que la mayoría de los modelos selecciona bien. La única forma de acotarlo es emitir una clave más estrecha, porque una clave personalizada registra exactamente sus api_allow_methods y nada más.

claude mcp add --transport http xuda https://xuda.ai/mcp \
--header "Authorization: Bearer pk_..."
cualquier otro cliente: la misma URL, la misma cabecera
331 herramientas registradas, resueltas de nuevo al abrir la sesión
una clave de acceso completo recoge métodos publicados después de emitirla
id de sesión devuelto como mcp-session-id, cerrado a los 10 minutos
un método con una especificación de campo mal formada se omite con un aviso en consola,
así que una sesión puede registrar menos de 331
xu-channel: mcp
establecida en la llamada de loopback, que es lo que separa el tráfico de agentes
del REST en el registro de su cuenta
Las líneas grises son nuestras. Las dos líneas de acento de arriba son lo que el dashboard imprime literalmente con su propia clave sustituida, y la línea xu-channel es una cabecera de petición que mcp_module establece en la llamada de loopback, no algo que usted envíe.
Límites honestos

Nueve cosas que esta API no hace, incluidas tres que el dashboard da a entender que sí.

  • Una clave tiene cuatro estados en la interfaz, 1 borrador, 2 suspendida, 3 activa y 4 eliminada, y solo el estado 4 se rechaza. Una clave en borrador o suspendida sigue autenticando y sigue ejecutando, tanto en REST como en MCP. La comprobación vive en cpi/api_module/index.mjs, líneas 131 a 199.
  • La lista de dominios permitidos se guarda en la clave y se puede editar en la interfaz, pero todas las ramas que la aplicarían están comentadas. No diseñe contando con ella.
  • No hay límite de frecuencia en /cpi. Los tres limitadores de router_module están montados en /login y en cuatro rutas de formulario, y el caso cpi pasa directamente al puerto 3007 sin middleware por delante. Los métodos públicos están protegidos por el producto de protección contra bots, no por el despachador.
  • Los códigos negativos se colapsan. Solo la franja de -400 a -499 se corresponde con su propio estado HTTP, así que -401 y -403 llegan como 401 y 403 y todo lo demás, incluido el muy común -1, es un 400 sin más. Lea code y data, nunca el estado por sí solo.
  • private es falsedad de valor y no una declaración. 321 entradas omiten la clave por completo, así que un método recién publicado se puede invocar con una clave salvo que alguien se acuerde de marcarlo.
  • Cinco entradas no llevan una clearance utilizable, tres una cadena vacía y dos nada en absoluto, y la barrera lanza un error ante una clearance con valor falso. Deberían fallar de forma rotunda por HTTP. Si son elementos internos exclusivos del broker que nunca ven una petición está sin verificar.
  • El catálogo con ámbito de app filtra por valores de clearance studio y user_group que ningún método lleva, así que devuelve los 86 métodos admin y omite en silencio los 5 de user_access, entre los que están dbs_read y get_app_info.
  • MCP Server aparece como un tipo de despliegue en el asistente y en la documentación, y deploy_module lo rechaza con code -2 y el mensaje deploy_type in beta. La superficie MCP distribuida es el único endpoint de plataforma identificado por una clave de API, no un despliegue por proyecto.
  • Todos los recuentos de esta página se leyeron de la copia de config.json del repositorio. config_dev.json muestra 1026 declarados y 334 no privados, y la configuración maestra en vivo no se leyó, así que trate estas cifras como la forma general y no como el total exacto de hoy. Que voice_module figure con 46 de 46 no privados conviene confirmarlo con la intención.
Antes de construir

Las preguntas que se está haciendo ahora mismo

Seis cosas que la referencia no le contará, respondidas contra el código distribuido y no contra la intención.

Vea

Vea una llamada salir y volver.

Lea el catálogo antes de escribir una sola línea contra él.

Una sola llamada, get_account_system_api_methods, devuelve todos los métodos que puede alcanzar, cada uno reducido a su descripción, sus campos tipados y el microservicio que lo posee. Esa es la misma declaración a partir de la cual se compilan los esquemas de herramientas MCP, así que la lista que se entrega a un agente y la lista contra la que usted programa salen de un solo archivo y no de dos documentos que se separan con el tiempo. Léala primero, decida qué métodos nombró realmente, y después emita una clave personalizada que contenga solo esos. Una clave de acceso completo es la forma más rápida de empezar y lo más amplio que puede entregar a un modelo.