@dinko/adonis-mcp
adonis-oauth-mcp
Monorepo para dos paquetes AdonisJS. Versionan y publican juntos, por lo que los cambios en el contrato de registro de recursos nunca necesitan coordinación entre repositorios.
Package | Posee |
| Servidor de autorización OAuth 2.1: token / approve / deny, validación de redirect-URI, almacenamiento de códigos de autorización, metadatos del servidor de autorización y un endpoint genérico de metadatos de recursos protegidos impulsado por un registro de recursos. No sabe nada de MCP. |
| Servidor MCP: manejador de peticiones, controlador, contrato de herramientas, middleware de autenticación. Se registra como recurso protegido OAuth, declarando su URL de recurso, scopes, clientes, |
La dependencia es unidireccional: mcp → oauth. Nada en oauth puede importar desde mcp.
Estructura
Cada paquete sigue la convención de paquetes de AdonisJS:
index.ts re-exports `configure` and `stubsRoot` (what `node ace configure` imports)
configure.ts the configure hook, driving codemods and stubs
stubs/ .stub templates rendered into the target app
src/ runtime code the app imports
providers/ service providers registered by the configure hook
services/ container services, for code that cannot use dependency injectionRelated MCP server: OAuth MCP Server
Desarrollo
npm install # links the workspaces
npm run build # tsc + copy stubs, per package
npm run typecheck
npm test # runs against build/, so build firstDurante el desarrollo, instala en una aplicación desde este checkout (npm link, file: o una dependencia git) en lugar de desde el registro.
@dinko/adonis-oauth
Un servidor de autorización OAuth 2.1 con PKCE, para aplicaciones AdonisJS que necesitan entregar tokens de acceso a clientes de terceros.
El paquete es dueño del protocolo. La aplicación es dueña de tres cosas que no puede delegar: la pantalla de consentimiento, qué token emitir y las rutas.
npm i @dinko/adonis-oauth
node ace configure @dinko/adonis-oauthLa configuración genera tres archivos y nunca sobrescribe uno existente:
File | Qué hacer con él |
| Declara tus recursos, sus clientes y |
| Ajusta la columna |
| Tuyo a partir de aquí: delega en el paquete y es donde añades cualquier cosa que no cubra. |
Rutas
No se registran automáticamente: dónde viven y qué middleware las protege es tu decisión. Añádelas a start/routes.ts:
router.get('.well-known/oauth-authorization-server', [OauthController, 'getAuthorizationServer'])
router.get('.well-known/oauth-protected-resource/:resource', [OauthController, 'getProtectedResource'])
router
.group(() => {
router.post('token', [OauthController, 'token'])
router
.group(() => {
router.post('authorize/approve', [OauthController, 'approveAuthorization'])
router.post('authorize/deny', [OauthController, 'denyAuthorization'])
})
.use(middleware.auth())
})
.prefix('oauth')Approve y deny deben estar autenticados: el código de autorización está vinculado al usuario que concede el acceso. El endpoint de token es público por especificación y es donde se canjean los códigos de autorización, por lo que es un buen lugar para un throttle.
Devolviendo la redirección
Approve y deny responden 200 { redirect_to } por defecto, y la pantalla de consentimiento navega por sí misma:
window.location.assign(response.redirect_to)Eso es lo que necesita una pantalla que envía su decisión con fetch o axios. Un XHR sigue un 302 reemitiendo la petición, por lo que la página nunca navega: el usuario permanece en la pantalla de consentimiento mientras la petición llega cross-origin al callback del cliente y falla CORS.
Establece redirectMode: 'http' cuando la pantalla de consentimiento es un formulario HTML simple. Ahí el navegador navega el documento, por lo que sigue el 302 de forma nativa y el usuario aterriza en el cliente.
Emisión de tokens
El tipo de token depende del recurso al que se accede, por lo que esa decisión vive con cada recurso en lugar de en el controlador. Una vez que el paquete ha validado la petición, consumido el código de autorización y verificado el verificador PKCE, llama a:
issueToken: async ({ userId, scopes, client, resource, ctx }) => {
const user = await User.find(userId)
if (!user) return null // rejects the exchange with invalid_grant
const expiresIn = 30 * 24 * 60 * 60
const token = await User.accessTokens.create(user, scopes, {
name: `oauth:${client.id}`,
expiresIn,
})
return { accessToken: token.value!.release(), expiresIn }
}userId es lo que se almacenó con el código de autorización: el paquete no tiene conocimiento de tu modelo de usuario y nunca lo carga.
La pantalla de consentimiento
La página GET /oauth/authorize es tuya — Edge, Inertia o un front end separado. El paquete solo valida la petición detrás de ella:
const validation = server.validateAuthorizationRequest(request.qs())
if (!validation.valid) {
return view.render('oauth/authorize', { error: validation.error })
}
return view.render('oauth/authorize', {
client: validation.client,
requestedScopes: validation.scopes,
authorizationFields: validation.fields, // post these back to approve
})Opcional: las aplicaciones que renderizan la pantalla en otro lugar pueden omitirlo, ya que approve y deny validan la petición de nuevo por su cuenta.
Configuración
export default defineConfig({
issuer: env.get('APP_URL'),
authorizationEndpoint: `${env.get('APP_URL')}/oauth/authorize`,
tokenEndpoint: `${env.get('APP_URL')}/oauth/token`,
// optional
redirectMode: 'json', // or 'http'
tokenEndpointAuthMethods: ['none'],
authorizationCodeTtlSeconds: 10 * 60,
authorizationCodesTable: 'oauth_authorization_codes',
authenticatedUserId: (ctx) => ctx.auth.user?.id, // defaults to this
resources: [mcpResource],
})Cada recurso declara:
Field | |
| Slug utilizado en |
| Indicador de recurso canónico que los clientes envían como parámetro |
| Nombre legible por humanos, anunciado a través de discovery. |
| Todos los scopes que el recurso entiende. |
|
|
| Genera el token de acceso. |
Las redirect URIs de loopback (http://localhost/callback) coinciden en cualquier puerto, según RFC 8252, y redirectUriPatterns permite un único segmento :param para clientes cuyo callback lleva un id.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Self-hosted federated MCP gateway: one OAuth 2.1 MCP server in front of N apps, user-level scopes.
MCP server for verifying EUDI/Talao wallet data via OIDC4VP (pull) for AI agents.
Hosted MCP server with managed OAuth for 15+ toolkits: Google Workspace, Fitbit, Oura, Kalshi, etc.
MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA self-hostable OAuth 2.0 server designed for the Model-Context-Protocol (MCP) that enables you to secure your MCP applications with a robust implementation you control.3,607112ISC
- FlicenseNot gradedqualityDmaintenanceA complete OAuth 2.1 server implementation for FastMCP with PKCE support, enabling secure authentication and authorization flows. Provides authorization code exchange, token management, and refresh capabilities for building authenticated MCP applications.
- AlicenseNot gradedqualityDmaintenanceDrop-in OAuth 2.1 + Dynamic Client Registration for MCP servers, providing authentication middleware and token verification.20MIT
- AlicenseNot gradedqualityCmaintenanceImplements an MCP server with OAuth 2.1 Protected Resource Metadata, enabling token-based authentication for MCP tools like ping.MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Dyoma3/adonis-oauth-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server