@manolibre/mcp-oauth
v0.1.1
Published
Servidor de autorización OAuth 2.1 para MCP. Sin dependencias y sin atarse a un runtime: Node, Deno o Workers.
Maintainers
Readme
@manolibre/mcp-oauth
Servidor de autorización OAuth 2.1 para servidores MCP.
Sin dependencias y sin APIs de Node: solo estándares web (WebCrypto,
Request/Response). Corre igual en Node 18+, Deno y Cloudflare Workers.
No sabe nada de tu base de datos, tu framework ni tus permisos. Le inyectas dos
interfaces —OAuthStore e IdentityProvider— y él pone el protocolo.
Por qué no tiene modelo de permisos
El token hereda los permisos que el usuario ya tiene en tu aplicación. El
paquete emite el parámetro scope porque el estándar lo exige en las
respuestas, pero no decide nada con él: quien decide es tu canAuthorize().
Es deliberado. Un segundo modelo de permisos conviviendo con el tuyo acaba desincronizado, y en autorización eso significa que la interfaz promete accesos que la base de datos deniega, o al revés.
Uso
import { createOAuth } from '@manolibre/mcp-oauth'
const oauth = createOAuth({
issuer: 'https://midominio.app',
resource: 'https://midominio.app/api/mcp',
consentPath: '/oauth/consent',
store: miAlmacen, // OAuthStore
identity: miIdentidad, // IdentityProvider
})
// Next.js (route handler) y Deno (Deno.serve) usan el mismo estándar:
export const GET = oauth.handle
export const POST = oauth.handleNotas de build
Tres opciones del tsconfig.json no son estilo, son requisitos. Si alguien las
quita, el paquete deja de funcionar en uno de los dos runtimes:
types: [] — impide que TypeScript cargue los tipos de @types/node del
workspace. Es lo que hace que import 'node:crypto' deje de compilar aquí
dentro. Es la segunda barrera; la primera es la regla de ESLint.
allowImportingTsExtensions + rewriteRelativeImportExtensions — el fuente
se escribe con ./x.ts y tsc reescribe a ./x.js al emitir, que es lo que
Node y Deno necesitan encontrar en ejecución.
Deno lee dist/, no el fuente
Todos los consumidores usan dist/, Deno incluido. No hay condición deno
en exports, y es deliberado.
La versión 0.1.0 sí la tenía, apuntando a src/. Estaba rota: Deno no puede
borrar tipos de ficheros dentro de node_modules
(ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING), y un paquete consumido con
npm: acaba justo ahí. Funcionaba con un enlace de workspace —que resuelve por
otro camino— y fallaba en el uso real.
La preocupación original que motivó esa condición —que Deno resolviera
./types.js dentro de un .d.ts al JavaScript emitido en vez del .d.ts
hermano— no ocurre cuando el paquete se resuelve como paquete npm. Solo
aparecía en aquel montaje con enlace de workspace.
Comprobado con el tarball instalado de verdad: deno check limpio y deno run
completando registro y autorización.
Consumir desde Deno
import { createOAuth } from 'npm:@manolibre/[email protected]'Con dos avisos del propio Deno, no del paquete:
- Necesita
"nodeModulesDir": "auto"endeno.json(odeno install). - Deno rechaza paquetes npm publicados hace poco, como defensa de cadena de
suministro. Si acabas de publicar una versión:
deno check --minimum-dependency-age=0. Al cabo de unos días deja de hacer falta.
Desarrollo
npm run build # tsc --build → dist/
npm run test # suite en memoria, bajo Node
npm run test:deno # la misma suite, bajo DenoQue la suite corra en los dos runtimes es la prueba de portabilidad real:
Deno soporta los builtins node:, así que ejecutarlo solo ahí no demostraría
nada. El lint es la barrera; los dos runtimes son la confirmación.
