Skip to main content
Glama
Dyoma3

@dinko/adonis-mcp

by Dyoma3

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

packages/oauth (@dinko/adonis-oauth)

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.

packages/mcp (@dinko/adonis-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, resource_name y proveedor de tokens. Aún no iniciado.

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 injection

Related 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 first

Durante 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-oauth

La configuración genera tres archivos y nunca sobrescribe uno existente:

File

Qué hacer con él

config/oauth.ts

Declara tus recursos, sus clientes y issueToken.

database/migrations/..._create_oauth_authorization_codes_table.ts

Ajusta la columna user_id a tu tabla de usuarios y luego migra.

app/controllers/oauth_controller.ts

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

id

Slug utilizado en /.well-known/oauth-protected-resource/<id>.

resource

Indicador de recurso canónico que los clientes envían como parámetro resource.

resourceName

Nombre legible por humanos, anunciado a través de discovery.

scopes

Todos los scopes que el recurso entiende.

clients

id, redirectUris, redirectUriPatterns, allowedScopes.

issueToken

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues

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

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A 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,607
    112
    ISC
  • F
    license
    Not graded
    quality
    D
    maintenance
    A 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.
  • A
    license
    Not graded
    quality
    D
    maintenance
    Drop-in OAuth 2.1 + Dynamic Client Registration for MCP servers, providing authentication middleware and token verification.
    20
    MIT

Latest Blog Posts

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