{"openapi":"3.1.0","info":{"title":"Sententia — API para agentes","version":"1.0.0","summary":"Servidor MCP de datos procesales y Authorization Server OAuth 2.1 de Sententia.","description":"Sententia es una plataforma de gestión procesal con IA para despachos de\nabogados en España. Esta especificación cubre la superficie pública\nconsumible por agentes: el flujo OAuth 2.1 (con PKCE y registro dinámico\nde clientes) y el endpoint MCP `/mcp`, que expone los casos, evidencias,\nplazos y anotaciones del usuario autenticado.\n\nHerramientas MCP disponibles: list_cases, get_case, list_evidences, read_evidence, list_claims, list_petitions, list_legal_references, list_comments, list_deadlines, add_comment, create_deadline, update_deadline, update_case, create_case.\n\nLímites de uso: las respuestas incluyen las cabeceras RateLimit de la RFC\n9331 (`RateLimit-Limit`, `RateLimit-Remaining`, `RateLimit-Reset`) y, ante\nun 429, `Retry-After` en segundos.","contact":{"name":"Sententia Studio S.L.","email":"info@sententia.studio"},"license":{"name":"Propietario","url":"https://www.sententia.studio/terms"}},"servers":[{"url":"https://www.sententia.studio","description":"Producción"}],"tags":[{"name":"MCP","description":"Endpoint Model Context Protocol de datos procesales."},{"name":"OAuth","description":"Authorization Server OAuth 2.1."},{"name":"Discovery","description":"Documentos de descubrimiento (RFC 8414 y RFC 9728)."}],"paths":{"/mcp":{"post":{"operationId":"callMcpEndpoint","tags":["MCP"],"summary":"Envía un mensaje JSON-RPC 2.0 al servidor Sententia Expedientes.","description":"Endpoint MCP (transporte HTTP streamable) que expone los datos procesales del usuario autenticado. Requiere un token personal o un token OAuth con el scope `casos`. Admite los métodos MCP estándar `initialize`, `tools/list` y `tools/call`.","security":[{"oauth2":["casos"]},{"personalToken":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcRequest"},"examples":{"listTools":{"summary":"Enumerar las herramientas disponibles","value":{"jsonrpc":"2.0","id":1,"method":"tools/list"}},"listCases":{"summary":"Invocar la herramienta list_cases","value":{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"list_cases","arguments":{"limit":10}}}}}}}},"responses":{"200":{"description":"Respuesta JSON-RPC correcta.","headers":{"RateLimit-Limit":{"description":"Número de peticiones permitidas en la ventana actual.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Peticiones restantes en la ventana actual.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos hasta que se reinicia la ventana.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcResponse"}}}},"202":{"description":"Notificación aceptada; sin cuerpo de respuesta."},"400":{"description":"Cuerpo JSON inválido.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcError"}}}},"401":{"description":"Falta el token, o es inválido o está revocado. Incluye `WWW-Authenticate` con la URL de los metadatos del recurso.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcError"}}}},"403":{"description":"El token carece del scope `casos`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcError"}}}},"429":{"description":"Se ha superado el límite de peticiones.","headers":{"RateLimit-Limit":{"description":"Número de peticiones permitidas en la ventana actual.","schema":{"type":"integer"}},"RateLimit-Remaining":{"description":"Peticiones restantes en la ventana actual.","schema":{"type":"integer"}},"RateLimit-Reset":{"description":"Segundos hasta que se reinicia la ventana.","schema":{"type":"integer"}},"Retry-After":{"description":"Segundos que el cliente debe esperar antes de reintentar.","schema":{"type":"integer"}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/JsonRpcError"}}}}}}},"/oauth/register":{"post":{"operationId":"registerOauthClient","tags":["OAuth"],"summary":"Registro dinámico de cliente (RFC 7591).","description":"Registra un cliente OAuth nuevo y devuelve su `client_id`. No requiere autenticación previa.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["redirect_uris"],"properties":{"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"},"description":"URIs de redirección. Se admite HTTPS, http://localhost para apps nativas y esquemas propios."},"client_name":{"type":"string","description":"Nombre legible del cliente."},"scope":{"type":"string","description":"Scopes separados por espacios. Admitidos: cendoj, casos."}}}}}},"responses":{"201":{"description":"Cliente registrado.","content":{"application/json":{"schema":{"type":"object","properties":{"client_id":{"type":"string"},"client_secret":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}}}}}}},"400":{"description":"Petición de registro inválida.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}}}}},"/oauth/authorize":{"get":{"operationId":"authorizeOauthRequest","tags":["OAuth"],"summary":"Endpoint de autorización (código con PKCE).","description":"Inicia el flujo `authorization_code`. Requiere PKCE con `code_challenge_method=S256`. Devuelve una página de consentimiento para el usuario y redirige a `redirect_uri` con el código.","parameters":[{"name":"response_type","in":"query","description":"Debe ser `code`.","required":true,"schema":{"type":"string","enum":["code"]}},{"name":"client_id","in":"query","description":"Identificador del cliente registrado.","required":true,"schema":{"type":"string"}},{"name":"redirect_uri","in":"query","description":"URI de redirección registrada.","required":true,"schema":{"type":"string","format":"uri"}},{"name":"code_challenge","in":"query","description":"Desafío PKCE.","required":true,"schema":{"type":"string"}},{"name":"code_challenge_method","in":"query","description":"Debe ser `S256`.","required":true,"schema":{"type":"string","enum":["S256"]}},{"name":"scope","in":"query","description":"Scopes separados por espacios: cendoj, casos.","required":false,"schema":{"type":"string"}},{"name":"state","in":"query","description":"Valor opaco devuelto sin modificar.","required":false,"schema":{"type":"string"}},{"name":"resource","in":"query","description":"Indicador de recurso (RFC 8707).","required":false,"schema":{"type":"string"}}],"responses":{"200":{"description":"Página de consentimiento (HTML)."},"302":{"description":"Redirección a `redirect_uri` con `code` y `state`."},"400":{"description":"Parámetros de autorización inválidos.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}}}}},"/oauth/token":{"post":{"operationId":"exchangeOauthToken","tags":["OAuth"],"summary":"Canjea un código de autorización o refresca un token.","description":"Emite un access token (`mcp_at_…`, 1 hora de validez) y un refresh token (`mcp_rt_…`, 30 días). Este endpoint no debe seguir redirecciones: usa siempre el host canónico del emisor.","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["grant_type"],"properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"code":{"type":"string","description":"Código recibido en la redirección."},"redirect_uri":{"type":"string","format":"uri"},"client_id":{"type":"string"},"client_secret":{"type":"string"},"code_verifier":{"type":"string","description":"Verificador PKCE."},"refresh_token":{"type":"string"}}}}}},"responses":{"200":{"description":"Token emitido.","content":{"application/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string","enum":["Bearer"]},"expires_in":{"type":"integer","description":"Segundos de validez."},"refresh_token":{"type":"string"},"scope":{"type":"string"}}}}}},"400":{"description":"Concesión inválida o PKCE incorrecto.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OAuthError"}}}}}}},"/oauth/revoke":{"post":{"operationId":"revokeOauthToken","tags":["OAuth"],"summary":"Revoca un access token o un refresh token (RFC 7009).","description":"Revoca el token indicado. Responde 200 aunque el token ya no exista, según la RFC.","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"type":"object","required":["token"],"properties":{"token":{"type":"string"},"token_type_hint":{"type":"string","enum":["access_token","refresh_token"]},"client_id":{"type":"string"}}}}}},"responses":{"200":{"description":"Token revocado (o inexistente)."}}}},"/.well-known/oauth-authorization-server":{"get":{"operationId":"getAuthorizationServerMetadata","tags":["Discovery"],"summary":"Metadatos del Authorization Server (RFC 8414).","description":"Documento de descubrimiento con los endpoints, grant types y scopes soportados.","responses":{"200":{"description":"Metadatos del servidor.","content":{"application/json":{"schema":{"type":"object","properties":{"issuer":{"type":"string","format":"uri"},"authorization_endpoint":{"type":"string","format":"uri"},"token_endpoint":{"type":"string","format":"uri"},"registration_endpoint":{"type":"string","format":"uri"},"revocation_endpoint":{"type":"string","format":"uri"},"scopes_supported":{"type":"array","items":{"type":"string"}}}}}}}}}},"/.well-known/oauth-protected-resource/mcp":{"get":{"operationId":"getProtectedResourceMetadata","tags":["Discovery"],"summary":"Metadatos del recurso protegido MCP (RFC 9728).","description":"Indica qué Authorization Server protege el endpoint `/mcp` y con qué scopes.","responses":{"200":{"description":"Metadatos del recurso.","content":{"application/json":{"schema":{"type":"object","properties":{"resource":{"type":"string","format":"uri"},"authorization_servers":{"type":"array","items":{"type":"string","format":"uri"}},"scopes_supported":{"type":"array","items":{"type":"string"}}}}}}}}}}},"components":{"securitySchemes":{"personalToken":{"type":"http","scheme":"bearer","description":"Token personal creado en Integraciones. Compartido con CENDOJ; válido hasta revocación o regeneración."},"oauth2":{"type":"oauth2","description":"OAuth 2.1 con PKCE (S256) y registro dinámico de clientes. El token se envía como `Authorization: Bearer <token>`.","flows":{"authorizationCode":{"authorizationUrl":"https://www.sententia.studio/oauth/authorize","tokenUrl":"https://www.sententia.studio/oauth/token","refreshUrl":"https://www.sententia.studio/oauth/token","scopes":{"casos":"Acceso a los casos, evidencias y plazos del usuario.","cendoj":"Acceso al servidor MCP de jurisprudencia CENDOJ."}}}}},"schemas":{"JsonRpcRequest":{"type":"object","required":["jsonrpc","method"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"description":"Identificador de la petición; se omite en notificaciones.","anyOf":[{"type":"string"},{"type":"integer"}]},"method":{"type":"string","description":"Método MCP, p. ej. `initialize`, `tools/list` o `tools/call`."},"params":{"type":"object","additionalProperties":true}}},"JsonRpcResponse":{"type":"object","required":["jsonrpc"],"properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]},"result":{"type":"object","additionalProperties":true}}},"JsonRpcError":{"type":"object","properties":{"jsonrpc":{"type":"string","enum":["2.0"]},"id":{"anyOf":[{"type":"string"},{"type":"integer"},{"type":"null"}]},"error":{"type":"object","properties":{"code":{"type":"integer"},"message":{"type":"string"}}}}},"OAuthError":{"type":"object","properties":{"error":{"type":"string"},"error_description":{"type":"string"}}}}}}