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.
- 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
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.
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).
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 } }
| 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.
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).
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 }
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.