Skip to main content
Glama
@dinko/adonis-mcp

@dinko/adonis-mcp

by Dyoma3

adonis-oauth-mcp

两个 AdonisJS 包的 monorepo。它们一起版本化和发布,因此对资源注册契约的更改永远不需要跨仓库协调。

包

负责

packages/oauth (@dinko/adonis-oauth)

OAuth 2.1 授权服务器:token / approve / deny、redirect-URI 校验、授权码存储、授权服务器元数据,以及由资源注册表驱动的通用受保护资源元数据端点。对 MCP 一无所知。

packages/mcp (@dinko/adonis-mcp)

MCP 服务器:请求处理器、控制器、工具契约、认证中间件。将自身注册为 OAuth 受保护资源,声明其资源 URL、作用域、客户端、resource_name 和令牌提供者。尚未启动。

依赖关系是单向的:mcp → oauth。oauth 中的任何内容都不得从 mcp 导入。

布局

每个包都遵循 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: mcpauth

开发

npm install     # links the workspaces
npm run build   # tsc + copy stubs, per package
npm run typecheck
npm test        # runs against build/, so build first

开发时,从本检出安装到应用中(npm link、file: 或 git 依赖),而不是从注册表安装。

@dinko/adonis-oauth

一个带 PKCE 的 OAuth 2.1 授权服务器,适用于需要向第三方客户端发放访问令牌的 AdonisJS 应用。

该包负责协议。应用拥有三件无法委托的事情:同意屏幕、发放哪个令牌以及路由。

npm i @dinko/adonis-oauth
node ace configure @dinko/adonis-oauth

配置会生成三个文件,并且永远不会覆盖已有文件:

文件

如何处理

config/oauth.ts

声明你的资源、它们的客户端和 issueToken。

database/migrations/..._create_oauth_authorization_codes_table.ts

将 user_id 列调整为你用户表的对应列,然后执行迁移。

app/controllers/oauth_controller.ts

从这里开始就是你的了:委托给该包,也是你添加它未覆盖的任何内容的地方。

路由

不会自动注册——它们放在哪里以及由哪个中间件保护,由你决定。将它们添加到 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 和 deny 必须经过认证:授权码绑定到授予访问权限的用户。令牌端点按规范是公开的,并且是授权码兑换的地方,因此它是设置限流的合适位置。

处理重定向返回

approve 和 deny 默认返回 200 { redirect_to },同意屏幕自行导航:

window.location.assign(response.redirect_to)

这就是使用 fetch 或 axios 提交决策的屏幕所需要的。XHR 通过重新发起请求来跟随 302,因此页面永远不会导航:用户停留在同意屏幕上,而请求跨域到达客户端的回调并因 CORS 失败。

当同意屏幕是纯 HTML 表单时,设置 redirectMode: 'http'。此时浏览器正在导航文档,因此它会原生地跟随 302,用户会落到客户端。

发放令牌

令牌的类型取决于被访问的资源,因此该决策属于每个资源而不是控制器。一旦包验证了请求、消费了授权码并验证了 PKCE 验证器,它就会调用:

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 是随授权码存储的任何内容:该包不了解你的用户模型,也从不加载它。

同意屏幕

GET /oauth/authorize 页面是你的——Edge、Inertia 或独立的前端。该包只验证其背后的请求:

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

可选:在其他地方渲染该屏幕的应用可以跳过它,因为 approve 和 deny 会自行再次验证请求。

配置

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],
})

每个资源声明:

字段

id

用于 /.well-known/oauth-protected-resource/<id> 中的 slug。

resource

客户端作为 resource 参数发送的规范资源指示符。

resourceName

人类可读的名称,通过发现机制公布。

scopes

资源理解的每个作用域。

clients

id、redirectUris、redirectUriPatterns、allowedScopes。

issueToken

铸造访问令牌。

根据 RFC 8252,环回重定向 URI(http://localhost/callback)匹配任何端口,并且 redirectUriPatterns 允许单个 :param 段,用于回调携带 id 的客户端。

Related MCP Connectors

Related MCP Servers

  • 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.
    12 npm
    1
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    A production-grade Model Context Protocol (MCP) resource server with OAuth 2.1 authorization, provider-agnostic and supporting multiple MCP specification versions.
    18
    MIT