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-streamobligatorio. - 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
curlcurl -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
curlcurl -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_articlescurl -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_usagecurl -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
| name | auth | arguments | descripción |
|---|---|---|---|
list_articles | público | category?: enum, limit?: 1–50 | Lista artículos publicados; filtra por categoría opcional. |
get_article | público | slug: string | Devuelve el artículo completo (Markdown) por slug. |
search_articles | público | query: string (≥2 chars), limit?: 1–30 | Búsqueda por texto en titular y bajada. |
my_ai_usage | usuario | (sin argumentos) | Uso del chat IA del usuario autenticado hoy. |
my_recent_messages | usuario | limit?: 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— faltaAccept: application/json, text/event-stream.- JSON-RPC error — la response contiene
error: { code, message }en lugar deresult. - Error de negocio — la tool responde con
result.isError: truey el mensaje encontent[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 · fragmentoopenapi: 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:
typescriptimport { 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.