Documentación técnica · Desarrolladores

API del servidor MCP

Referencia para integrar el servidor MCP de Inversión Consciente en un cliente propio. Para conectar clientes empaquetados (Claude, ChatGPT, Cursor…), ver la guía de usuario.

Resumen
  • Endpoint: POST https://www.xn--inversinconsciente-w1b.com/mcp
  • Transporte: Streamable HTTP, spec MCP 2025-06-18.
  • Codificación: JSON-RPC 2.0. Header Accept: application/json, text/event-stream obligatorio.
  • Auth: OAuth 2.1 bearer JWT emitido por https://<project-ref>.supabase.co/auth/v1. Audience aceptada: authenticated.
  • Manifest público (catálogo de tools sin auth): https://www.xn--inversinconsciente-w1b.com/mcp-manifest.json
Descubrimiento OAuth

Los clientes MCP detectan el authorization server via el recurso protegido: GET https://www.xn--inversinconsciente-w1b.com/.well-known/oauth-protected-resource

200 OK · application/json
{ "resource": "https://www.xn--inversinconsciente-w1b.com/mcp", "authorization_servers": [ "https://vbtwupryyrgaudznkmel.supabase.co/auth/v1" ], "scopes_supported": ["openid", "email", "profile"], "bearer_methods_supported": ["header"] }

El authorization server soporta Dynamic Client Registration — los clientes se registran solos y obtienen client_id sin intervención manual. Scopes solicitados: openid email profile.

Handshake JSON-RPC

1. initialize

curl
curl -X POST https://www.xn--inversinconsciente-w1b.com/mcp \ -H "Authorization: Bearer <ACCESS_TOKEN>" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "initialize", "params": { "protocolVersion": "2025-06-18", "capabilities": {}, "clientInfo": { "name": "my-client", "version": "0.1.0" } } }'
response
{ "jsonrpc": "2.0", "id": 1, "result": { "protocolVersion": "2025-06-18", "capabilities": { "tools": { "listChanged": false } }, "serverInfo": { "name": "inversion-consciente-mcp", "title": "Inversión Consciente", "version": "0.1.0" } } }

2. tools/list

curl
curl -X POST https://www.xn--inversinconsciente-w1b.com/mcp \ -H "Authorization: Bearer <ACCESS_TOKEN>" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

Devuelve el mismo catálogo que /mcp-manifest.json (name, title, description, inputSchema, annotations).

Invocar tools

Tool pública (sin bearer, RLS anon):

curl · list_articles
curl -X POST https://www.xn--inversinconsciente-w1b.com/mcp \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": { "name": "list_articles", "arguments": { "category": "Cripto", "limit": 5 } } }'

Tool de usuario (bearer requerido, RLS del usuario):

curl · my_ai_usage
curl -X POST https://www.xn--inversinconsciente-w1b.com/mcp \ -H "Authorization: Bearer <ACCESS_TOKEN>" \ -H "Content-Type: application/json" \ -H "Accept: application/json, text/event-stream" \ -d '{ "jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": { "name": "my_ai_usage", "arguments": {} } }'

Formato de response:

response
{ "jsonrpc": "2.0", "id": 3, "result": { "content": [ { "type": "text", "text": "[{\"slug\":\"...\",\"headline\":\"...\"}]" } ], "structuredContent": { "articles": [ /* ... */ ] }, "isError": false } }
Catálogo de tools
nameauthargumentsdescripción
list_articlespúblicocategory?: enum, limit?: 1–50Lista artículos publicados; filtra por categoría opcional.
get_articlepúblicoslug: stringDevuelve el artículo completo (Markdown) por slug.
search_articlespúblicoquery: string (≥2 chars), limit?: 1–30Búsqueda por texto en titular y bajada.
my_ai_usageusuario(sin argumentos)Uso del chat IA del usuario autenticado hoy.
my_recent_messagesusuariolimit?: 1–50Últimos mensajes del chat del usuario autenticado.

El inputSchema completo (JSON Schema draft-07) está en el manifest.

Errores
  • 401 Unauthorized — bearer ausente, inválido o expirado. Renueva vía OAuth refresh.
  • 406 Not Acceptable — falta Accept: application/json, text/event-stream.
  • JSON-RPC error — la response contiene error: { code, message } en lugar de result.
  • Error de negocio — la tool responde con result.isError: true y el mensaje en content[0].text (ej. tool de usuario invocada sin sesión).
Esquema tipo OpenAPI

MCP es JSON-RPC, no REST. El contrato canónico vive en el manifest MCP (name/version/tools con JSON Schema por tool). Para consumidores que insistan en OpenAPI 3.1, este fragmento envuelve el endpoint POST /mcp:

openapi 3.1 · fragmento
openapi: 3.1.0 info: title: Inversión Consciente MCP version: 0.1.0 servers: - url: https://www.xn--inversinconsciente-w1b.com paths: /mcp: post: summary: JSON-RPC endpoint (MCP Streamable HTTP) security: - bearerAuth: [] requestBody: required: true content: application/json: schema: $ref: "#/components/schemas/JSONRPCRequest" responses: "200": description: JSON-RPC response (JSON or SSE stream) content: application/json: schema: $ref: "#/components/schemas/JSONRPCResponse" components: securitySchemes: bearerAuth: { type: http, scheme: bearer, bearerFormat: JWT } schemas: JSONRPCRequest: type: object required: [jsonrpc, method] properties: jsonrpc: { type: string, const: "2.0" } id: { oneOf: [{ type: string }, { type: integer }] } method: { type: string } params: { type: object } JSONRPCResponse: type: object properties: jsonrpc: { type: string, const: "2.0" } id: { oneOf: [{ type: string }, { type: integer }, { type: "null" }] } result: { type: object } error: type: object properties: code: { type: integer } message: { type: string }
SDKs recomendados

SDK oficial: @modelcontextprotocol/sdk (TS/JS) y mcp en Python. Ejemplo mínimo TypeScript:

typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js"; import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"; const transport = new StreamableHTTPClientTransport( new URL("https://www.xn--inversinconsciente-w1b.com/mcp"), { requestInit: { headers: { Authorization: `Bearer ${accessToken}` } } } ); const client = new Client({ name: "my-client", version: "0.1.0" }); await client.connect(transport); const { tools } = await client.listTools(); const result = await client.callTool({ name: "list_articles", arguments: { category: "IA", limit: 10 }, });

¿Encontraste un bug o quieres proponer una tool? Contáctanos.