Tutorial: Crea un gestor de tareas
En este tutorial, construiremos un servidor MCP gestor de tareas con autenticación y autorización de usuarios.
Después de completar este tutorial, tendrás:
- ✅ Una comprensión básica de cómo configurar el control de acceso basado en roles (RBAC) en tu servidor MCP.
- ✅ Un servidor MCP que puede gestionar listas de tareas personales.
Antes de comenzar, te recomendamos encarecidamente que revises primero el tutorial ¿Quién soy? si no estás familiarizado con el servidor MCP y OAuth 2.
Descripción general
El tutorial involucrará los siguientes componentes:
- Servidor MCP: Un servidor MCP sencillo que utiliza los SDK oficiales de MCP para manejar solicitudes, con un servicio integrado de tareas para gestionar los elementos de tareas del usuario.
- Inspector MCP: Una herramienta visual de pruebas para servidores MCP. También actúa como un cliente OAuth / OIDC para iniciar el flujo de autorización y obtener tokens de acceso.
- Servidor de autorización: Un proveedor OAuth 2.1 u OpenID Connect que gestiona identidades de usuario y emite tokens de acceso.
Aquí tienes un diagrama de alto nivel de la interacción entre estos componentes:
Comprende tu servidor de autorización
Tokens de acceso con alcances
Para implementar el control de acceso basado en roles (RBAC) en tu servidor MCP, tu servidor de autorización debe admitir la emisión de tokens de acceso con alcances. Los alcances representan los permisos que se le han otorgado a un usuario.
- Logto
- OAuth 2.0 / OIDC
Logto proporciona soporte para RBAC a través de sus recursos de API (conforme a RFC 8707: Indicadores de recurso para OAuth 2.0) y funciones de roles. Así es como se configura:
-
Inicia sesión en Logto Console (o en tu Logto Console autoalojado)
-
Crea recurso de API y alcances:
- Ve a "Recursos de API"
- Crea un nuevo recurso de API llamado "Gestor de tareas"
- Añade los siguientes alcances:
create:todos
: "Crear nuevas tareas"read:todos
: "Leer todas las tareas"delete:todos
: "Eliminar cualquier tarea"
-
Crea roles (recomendado para una gestión más sencilla):
- Ve a "Roles"
- Crea un rol "Admin" y asigna todos los alcances (
create:todos
,read:todos
,delete:todos
) - Crea un rol "User" y asigna solo el alcance
create:todos
-
Asigna permisos:
- Ve a "Usuarios"
- Selecciona un usuario
- Puedes:
- Asignar roles en la pestaña "Roles" (recomendado)
- O asignar alcances directamente en la pestaña "Permisos"
Los alcances se incluirán en el reclamo scope
del token de acceso JWT como una cadena separada por espacios.
Los proveedores OAuth 2.0 / OIDC suelen admitir el control de acceso basado en alcances. Al implementar RBAC:
- Define los alcances requeridos en tu servidor de autorización
- Configura tu cliente para solicitar estos alcances durante el flujo de autorización
- Asegúrate de que tu servidor de autorización incluya los alcances otorgados en el token de acceso
- Los alcances suelen incluirse en el reclamo
scope
del token de acceso JWT
Consulta la documentación de tu proveedor para detalles específicos sobre:
- Cómo definir y gestionar alcances
- Cómo se incluyen los alcances en el token de acceso
- Cualquier característica adicional de RBAC como la gestión de roles
Validación de tokens y comprobación de permisos
Cuando tu servidor MCP recibe una solicitud, debe:
- Validar la firma y expiración del token de acceso
- Extraer los alcances del token validado
- Comprobar si el token tiene los alcances requeridos para la operación solicitada
Por ejemplo, si un usuario quiere crear una nueva tarea, su token de acceso debe incluir el alcance create:todos
. Así funciona el flujo:
Registro dinámico de clientes
El registro dinámico de clientes no es necesario para este tutorial, pero puede ser útil si deseas automatizar el proceso de registro del cliente MCP con tu servidor de autorización. Consulta ¿Es necesario el registro dinámico de clientes? para más detalles.
Comprende RBAC en el gestor de tareas
Con fines demostrativos, implementaremos un sistema sencillo de control de acceso basado en roles (RBAC) en nuestro servidor MCP gestor de tareas. Esto te mostrará los principios básicos de RBAC manteniendo la implementación simple.
Aunque este tutorial demuestra la gestión de alcances basada en RBAC, es importante señalar que no todos los proveedores de autenticación implementan la gestión de alcances a través de roles. Algunos proveedores pueden tener sus propias implementaciones y mecanismos únicos para gestionar el control de acceso y los permisos.
Herramientas y alcances
Nuestro servidor MCP gestor de tareas proporciona tres herramientas principales:
create-todo
: Crear una nueva tareaget-todos
: Listar todas las tareasdelete-todo
: Eliminar una tarea por ID
Para controlar el acceso a estas herramientas, definimos los siguientes alcances:
create:todos
: Permite crear nuevas tareasdelete:todos
: Permite eliminar tareas existentesread:todos
: Permite consultar y recuperar la lista de todas las tareas
Roles y permisos
Definiremos dos roles con diferentes niveles de acceso:
Rol | create:todos | read:todos | delete:todos |
---|---|---|---|
Admin | ✅ | ✅ | ✅ |
User | ✅ |
- User: Un usuario regular que puede crear tareas y ver o eliminar solo sus propias tareas
- Admin: Un administrador que puede crear, ver y eliminar todas las tareas, sin importar la propiedad
Propiedad de recursos
Aunque la tabla de permisos anterior muestra los alcances explícitos asignados a cada rol, hay un principio importante de propiedad de recursos a considerar:
- Los usuarios no tienen los alcances
read:todos
odelete:todos
, pero aún pueden:- Leer sus propias tareas
- Eliminar sus propias tareas
- Los administradores tienen todos los permisos (
read:todos
ydelete:todos
), lo que les permite:- Ver todas las tareas del sistema
- Eliminar cualquier tarea, sin importar la propiedad
Esto demuestra un patrón común en los sistemas RBAC donde la propiedad de recursos otorga permisos implícitos a los usuarios para sus propios recursos, mientras que los roles administrativos reciben permisos explícitos para todos los recursos.
Para profundizar en los conceptos y mejores prácticas de RBAC, consulta Dominando RBAC: Un ejemplo completo del mundo real.
Configura la autorización en tu proveedor
Para implementar el sistema de control de acceso que describimos antes, deberás configurar tu servidor de autorización para admitir los alcances requeridos. Así es como hacerlo con diferentes proveedores:
- Logto
- Keycloak
- OAuth 2 / OIDC
Logto proporciona soporte para RBAC a través de sus recursos de API y funciones de roles. Así es como se configura:
-
Inicia sesión en Logto Console (o en tu Logto Console autoalojado)
-
Crea recurso de API y alcances:
- Ve a "Recursos de API"
- Crea un nuevo recurso de API llamado "Gestor de tareas" y usa
https://todo.mcp-server.app
(para demostración) como el indicador. - Crea los siguientes alcances:
create:todos
: "Crear nuevas tareas"read:todos
: "Leer todas las tareas"delete:todos
: "Eliminar cualquier tarea"
-
Crea roles (recomendado para una gestión más sencilla):
- Ve a "Roles"
- Crea un rol "Admin" y asigna todos los alcances (
create:todos
,read:todos
,delete:todos
) - Crea un rol "User" y asigna solo el alcance
create:todos
- En la página de detalles del rol "User", cambia a la pestaña "General" y establece el rol "User" como el "Rol predeterminado".
-
Gestiona roles y permisos de usuario:
- Para nuevos usuarios:
- Obtendrán automáticamente el rol "User" ya que lo establecimos como rol predeterminado
- Para usuarios existentes:
- Ve a "Gestión de usuarios"
- Selecciona un usuario
- Asigna roles al usuario en la pestaña "Roles"
- Para nuevos usuarios:
También puedes usar la Management API de Logto para gestionar roles de usuario de forma programática. Esto es especialmente útil para la gestión automatizada de usuarios o al construir paneles de administración.
Al solicitar un token de acceso, Logto incluirá los alcances en el reclamo scope
del token según los permisos de rol del usuario.
En Keycloak, puedes configurar los permisos requeridos usando client scopes:
-
Crea client scopes:
- En tu realm, ve a "Client scopes"
- Crea tres nuevos client scopes:
create:todos
read:todos
delete:todos
-
Configura el cliente:
- Ve a la configuración de tu cliente
- En la pestaña "Client scopes", añade todos los scopes que creaste
- Asegúrate de que el token mapper esté configurado para incluir los scopes
-
Opcional: Usa roles para una gestión más sencilla
- Si prefieres la gestión basada en roles:
- Crea roles de realm para diferentes niveles de acceso
- Mapea scopes a roles
- Asigna roles a los usuarios
- De lo contrario, puedes asignar scopes directamente a los usuarios o a través de permisos a nivel de cliente
- Si prefieres la gestión basada en roles:
Keycloak incluirá los scopes otorgados en el reclamo scope
del token de acceso.
Para proveedores OAuth 2.0 u OpenID Connect, deberás configurar los scopes que representan diferentes permisos. Los pasos exactos dependerán de tu proveedor, pero generalmente:
-
Define scopes:
- Configura tu servidor de autorización para admitir:
create:todos
read:todos
delete:todos
- Configura tu servidor de autorización para admitir:
-
Configura el cliente:
- Registra o actualiza tu cliente para solicitar estos scopes
- Asegúrate de que los scopes estén incluidos en el token de acceso
-
Asigna permisos:
- Usa la interfaz de tu proveedor para otorgar los scopes apropiados a los usuarios
- Algunos proveedores pueden admitir la gestión basada en roles, mientras que otros pueden usar asignaciones directas de scopes
- Consulta la documentación de tu proveedor para el enfoque recomendado
La mayoría de los proveedores incluirán los scopes otorgados en el reclamo scope
del token de acceso. El formato suele ser una cadena de valores de scope separados por espacios.
Después de configurar tu servidor de autorización, los usuarios recibirán tokens de acceso que contienen los scopes otorgados. El servidor MCP usará estos scopes para determinar:
- Si un usuario puede crear nuevas tareas (
create:todos
) - Si un usuario puede ver todas las tareas (
read:todos
) o solo las suyas - Si un usuario puede eliminar cualquier tarea (
delete:todos
) o solo las suyas
Configura el servidor MCP
Usaremos los SDK oficiales de MCP para crear nuestro servidor MCP gestor de tareas.
Crea un nuevo proyecto
- Python
- Node.js
mkdir mcp-server
cd mcp-server
uv init # O usa `pipenv` o `poetry` para crear un nuevo entorno virtual
Configura un nuevo proyecto Node.js:
mkdir mcp-server
cd mcp-server
npm init -y # O usa `pnpm init`
npm pkg set type="module"
npm pkg set main="todo-manager.ts"
npm pkg set scripts.start="node --experimental-strip-types todo-manager.ts"
Usamos TypeScript en nuestros ejemplos ya que Node.js v22.6.0+ admite la ejecución de TypeScript de forma nativa usando la bandera --experimental-strip-types
. Si usas JavaScript, el código será similar; solo asegúrate de usar Node.js v22.6.0 o posterior. Consulta la documentación de Node.js para más detalles.
Instala el SDK de MCP y dependencias
- Python
- Node.js
pip install "mcp[cli]" starlette uvicorn
O cualquier otro gestor de paquetes que prefieras, como uv
o poetry
.
npm install @modelcontextprotocol/sdk express zod
O cualquier otro gestor de paquetes que prefieras, como pnpm
o yarn
.
Crea el servidor MCP
Primero, vamos a crear un servidor MCP básico con las definiciones de herramientas:
- Python
- Node.js
Crea un archivo llamado todo-manager.py
y añade el siguiente código:
from typing import Any
from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount
mcp = FastMCP("Gestor de tareas")
@mcp.tool()
def create_todo(content: str) -> dict[str, Any]:
"""Crear una nueva tarea."""
return {"error": "No implementado"}
@mcp.tool()
def get_todos() -> dict[str, Any]:
"""Listar todas las tareas."""
return {"error": "No implementado"}
@mcp.tool()
def delete_todo(id: str) -> dict[str, Any]:
"""Eliminar una tarea por id."""
return {"error": "No implementado"}
app = Starlette(
routes=[Mount('/', app=mcp.sse_app())]
)
Ejecuta el servidor con:
uvicorn todo_manager:app --host 0.0.0.0 --port 3001
Dado que la implementación actual del inspector MCP no maneja flujos de autorización, usaremos el enfoque SSE para configurar el servidor MCP. Actualizaremos el código aquí una vez que el inspector MCP admita flujos de autorización.
También puedes usar pnpm
o yarn
si lo prefieres.
Crea un archivo llamado todo-manager.ts
y añade el siguiente código:
// todo-manager.ts
import { z } from 'zod';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
import express from 'express';
// Crear un servidor MCP
const server = new McpServer({
name: 'Gestor de tareas',
version: '0.0.0',
});
server.tool('create-todo', 'Crear una nueva tarea', { content: z.string() }, async ({ content }) => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'No implementado' }) }],
};
});
server.tool('get-todos', 'Listar todas las tareas', async () => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'No implementado' }) }],
};
});
server.tool('delete-todo', 'Eliminar una tarea por id', { id: z.string() }, async ({ id }) => {
return {
content: [{ type: 'text', text: JSON.stringify({ error: 'No implementado' }) }],
};
});
// Código boilerplate de la documentación del SDK de MCP
const PORT = 3001;
const app = express();
const transports = {};
app.get('/sse', async (_req, res) => {
const transport = new SSEServerTransport('/messages', res);
transports[transport.sessionId] = transport;
res.on('close', () => {
delete transports[transport.sessionId];
});
await server.connect(transport);
});
app.post('/messages', async (req, res) => {
const sessionId = String(req.query.sessionId);
const transport = transports[sessionId];
if (transport) {
await transport.handlePostMessage(req, res, req.body);
} else {
res.status(400).send('No transport found for sessionId');
}
});
app.listen(PORT);
Ejecuta el servidor con:
npm start
Inspecciona el servidor MCP
Clona y ejecuta el inspector MCP
Ahora que tenemos el servidor MCP en funcionamiento, podemos usar el inspector MCP para ver si la herramienta whoami
está disponible.
Debido a la limitación de la implementación actual, hemos bifurcado el inspector MCP para hacerlo más flexible y escalable para autenticación y autorización. También hemos enviado un pull request al repositorio original para incluir nuestros cambios.
Para ejecutar el inspector MCP, puedes usar el siguiente comando (se requiere Node.js):
git clone https://github.com/mcp-auth/inspector.git
cd inspector
npm install
npm run dev
Luego, abre tu navegador y navega a http://localhost:6274/
(u otra URL mostrada en la terminal) para acceder al inspector MCP.
Conecta el inspector MCP al servidor MCP
Antes de continuar, verifica la siguiente configuración en el inspector MCP:
- Tipo de transporte: Establece en
SSE
. - URL: Establece la URL de tu servidor MCP. En nuestro caso, debe ser
http://localhost:3001/sse
.
Ahora puedes hacer clic en el botón "Connect" para ver si el inspector MCP puede conectarse al servidor MCP. Si todo está bien, deberías ver el estado "Connected" en el inspector MCP.
Punto de control: Ejecuta las herramientas del gestor de tareas
- En el menú superior del inspector MCP, haz clic en la pestaña "Tools".
- Haz clic en el botón "List Tools".
- Deberías ver las herramientas
create-todo
,get-todos
ydelete-todo
listadas en la página. Haz clic en una para ver los detalles de la herramienta. - Deberías ver el botón "Run Tool" en el lado derecho. Haz clic en él e ingresa los parámetros requeridos para ejecutar la herramienta.
- Deberías ver el resultado de la herramienta con la respuesta JSON
{"error": "No implementado"}
.
Integra con tu servidor de autorización
Para completar esta sección, hay varias consideraciones a tener en cuenta:
La URL del emisor de tu servidor de autorización
Normalmente es la URL base de tu servidor de autorización, como https://auth.example.com
. Algunos proveedores pueden tener una ruta como https://example.logto.app/oidc
, así que asegúrate de consultar la documentación de tu proveedor.
Cómo obtener los metadatos del servidor de autorización
- Si tu servidor de autorización cumple con OAuth 2.0 Authorization Server Metadata o OpenID Connect Discovery, puedes usar las utilidades integradas de MCP Auth para obtener los metadatos automáticamente.
- Si tu servidor de autorización no cumple con estos estándares, deberás especificar manualmente la URL de metadatos o los endpoints en la configuración del servidor MCP. Consulta la documentación de tu proveedor para los endpoints específicos.
Cómo registrar el inspector MCP como cliente en tu servidor de autorización
- Si tu servidor de autorización admite Dynamic Client Registration, puedes omitir este paso ya que el inspector MCP se registrará automáticamente como cliente.
- Si tu servidor de autorización no admite Dynamic Client Registration, deberás registrar manualmente el inspector MCP como cliente en tu servidor de autorización.
Comprende los parámetros de solicitud de token
Al solicitar tokens de acceso de diferentes servidores de autorización, encontrarás varios enfoques para especificar el recurso objetivo y los permisos. Aquí los principales patrones:
-
Basado en indicador de recurso:
- Usa el parámetro
resource
para especificar la API objetivo (ver RFC 8707: Indicadores de recurso para OAuth 2.0) - Común en implementaciones modernas de OAuth 2.0
- Ejemplo de solicitud:
{ "resource": "https://todo.mcp-server.app", "scope": "create:todos read:todos" }
- El servidor emite tokens vinculados específicamente al recurso solicitado
- Usa el parámetro
-
Basado en audiencia:
- Usa el parámetro
audience
para especificar el destinatario previsto del token - Similar a los indicadores de recurso pero con semántica diferente
- Ejemplo de solicitud:
{ "audience": "todo-api", "scope": "create:todos read:todos" }
- Usa el parámetro
-
Basado solo en scopes:
- Se basa únicamente en scopes sin parámetros de recurso/audiencia
- Enfoque tradicional de OAuth 2.0
- Ejemplo de solicitud:
{ "scope": "todo-api:create todo-api:read openid profile" }
- A menudo usa scopes con prefijo para namespacing de permisos
- Común en implementaciones más simples de OAuth 2.0
- Consulta la documentación de tu proveedor para los parámetros admitidos
- Algunos proveedores admiten varios enfoques simultáneamente
- Los indicadores de recurso proporcionan mejor seguridad mediante restricción de audiencia
- Considera usar indicadores de recurso cuando estén disponibles para un mejor control de acceso
Aunque cada proveedor puede tener sus propios requisitos específicos, los siguientes pasos te guiarán en el proceso de integración del inspector MCP y el servidor MCP con configuraciones específicas del proveedor.
Registra el inspector MCP como cliente
- Logto
- OAuth 2.0 / OIDC
Integrar el gestor de tareas con Logto es sencillo ya que es un proveedor OpenID Connect que admite indicadores de recurso y scopes, lo que te permite asegurar tu API de tareas con https://todo.mcp-server.app
como indicador de recurso.
Dado que Logto aún no admite Dynamic Client Registration, deberás registrar manualmente el inspector MCP como cliente en tu tenant de Logto:
- Abre tu inspector MCP, haz clic en el botón "OAuth Configuration". Copia el valor de Redirect URL (auto-populated), que debería ser algo como
http://localhost:6274/oauth/callback
. - Inicia sesión en Logto Console (o en tu Logto Console autoalojado).
- Navega a la pestaña "Applications", haz clic en "Create application". En la parte inferior de la página, haz clic en "Create app without framework".
- Completa los detalles de la aplicación y haz clic en "Create application":
- Selecciona un tipo de aplicación: Elige "Single-page application".
- Nombre de la aplicación: Ingresa un nombre para tu aplicación, por ejemplo, "MCP Inspector".
- En la sección "Settings / Redirect URIs", pega el valor de Redirect URL (auto-populated) que copiaste del inspector MCP. Luego haz clic en "Save changes" en la barra inferior.
- En la tarjeta superior, verás el valor "App ID". Cópialo.
- Vuelve al inspector MCP y pega el valor "App ID" en la sección "OAuth Configuration" bajo "Client ID".
- Ingresa el valor
{"scope": "create:todos read:todos delete:todos", "resource": "https://todo.mcp-server.app"}
en el campo "Auth Params". Esto asegurará que el token de acceso devuelto por Logto contenga los scopes necesarios para acceder al gestor de tareas.
Esta es una guía genérica de integración de proveedor OAuth 2.0 / OpenID Connect. Ambos siguen pasos similares ya que OIDC se basa en OAuth 2.0. Consulta la documentación de tu proveedor para detalles específicos.
Si tu proveedor admite Dynamic Client Registration, puedes ir directamente al paso 8 para configurar el inspector MCP; de lo contrario, deberás registrar manualmente el inspector MCP como cliente:
-
Abre tu inspector MCP, haz clic en el botón "OAuth Configuration". Copia el valor de Redirect URL (auto-populated), que debería ser algo como
http://localhost:6274/oauth/callback
. -
Inicia sesión en la consola de tu proveedor.
-
Navega a la sección "Applications" o "Clients", luego crea una nueva aplicación o cliente.
-
Si tu proveedor requiere un tipo de cliente, selecciona "Single-page application" o "Public client".
-
Después de crear la aplicación, deberás configurar la URI de redirección. Pega el valor de Redirect URL (auto-populated) que copiaste del inspector MCP.
-
Busca el "Client ID" o "Application ID" de la nueva aplicación y cópialo.
-
Vuelve al inspector MCP y pega el valor "Client ID" en la sección "OAuth Configuration" bajo "Client ID".
-
Ingresa el siguiente valor en el campo "Auth Params" para solicitar los scopes necesarios para las operaciones de tareas:
{ "scope": "create:todos read:todos delete:todos" }
Configura MCP auth
En tu proyecto del servidor MCP, necesitas instalar el SDK de MCP Auth y configurarlo para usar los metadatos de tu servidor de autorización.
- Python
- Node.js
Primero, instala el paquete mcpauth
:
pip install mcpauth
O cualquier otro gestor de paquetes que prefieras, como uv
o poetry
.
Primero, instala el paquete mcp-auth
:
npm install mcp-auth
MCP Auth requiere los metadatos del servidor de autorización para poder inicializarse. Dependiendo de tu proveedor:
- Logto
- OAuth 2.0 / OIDC
La URL del emisor se puede encontrar en la página de detalles de tu aplicación en Logto Console, en la sección "Endpoints & Credentials / Issuer endpoint". Debería verse como https://my-project.logto.app/oidc
.
- Python
- Node.js
Actualiza el archivo todo-manager.py
para incluir la configuración de MCP Auth:
from mcpauth import MCPAuth
from mcpauth.config import AuthServerType
from mcpauth.utils import fetch_server_config
auth_issuer = '<issuer-endpoint>' # Reemplaza con tu endpoint de emisor
auth_server_config = fetch_server_config(auth_issuer, type=AuthServerType.OIDC)
mcp_auth = MCPAuth(server=auth_server_config)
Actualiza el archivo todo-manager.ts
para incluir la configuración de MCP Auth:
import { MCPAuth, fetchServerConfig } from 'mcp-auth';
const authIssuer = '<issuer-endpoint>'; // Reemplaza con tu endpoint de emisor
const mcpAuth = new MCPAuth({
server: await fetchServerConfig(authIssuer, { type: 'oidc' }),
});
Para proveedores OAuth 2.0, deberás:
- Consultar la documentación de tu proveedor para la URL del servidor de autorización (a menudo llamada issuer URL o base URL)
- Algunos proveedores pueden exponer esto en
https://{your-domain}/.well-known/oauth-authorization-server
- Busca en la consola de administración de tu proveedor bajo la configuración OAuth/API
- Python
- Node.js
Actualiza el archivo todo-manager.py
para incluir la configuración de MCP Auth:
from mcpauth import MCPAuth
from mcpauth.config import AuthServerType
from mcpauth.utils import fetch_server_config
auth_issuer = '<issuer-endpoint>' # Reemplaza con tu endpoint de emisor
auth_server_config = fetch_server_config(auth_issuer, type=AuthServerType.OAUTH) # o AuthServerType.OIDC
mcp_auth = MCPAuth(server=auth_server_config)
Actualiza el archivo todo-manager.ts
para incluir la configuración de MCP Auth:
import { MCPAuth, fetchServerConfig } from 'mcp-auth';
const authIssuer = '<issuer-endpoint>'; // Reemplaza con tu endpoint de emisor
const mcpAuth = new MCPAuth({
server: await fetchServerConfig(authIssuer, { type: 'oauth' }), // o { type: 'oidc' }
});
En algunos casos, la respuesta del proveedor puede estar malformada o no ajustarse al formato de metadatos esperado. Si estás seguro de que el proveedor es compatible, puedes transpilar los metadatos mediante la opción de configuración:
- Python
- Node.js
mcp_auth = MCPAuth(
server=fetch_server_config(
# ...otras opciones
transpile_data=lambda data: {**data, 'response_types_supported': ['code']}
)
)
const mcpAuth = new MCPAuth({
server: await fetchServerConfig(authIssuer, {
// ...otras opciones
transpileData: (data) => ({ ...data, response_types_supported: ['code'] }),
}),
});
Si tu proveedor no admite Metadatos del servidor de autorización OAuth 2.0, puedes especificar manualmente la URL de metadatos o los endpoints. Consulta Otras formas de inicializar MCP Auth para más detalles.
- Python
- Node.js
Actualiza el todo-manager.py
para incluir la configuración de MCP Auth:
from mcpauth import MCPAuth
from mcpauth.config import AuthServerType
from mcpauth.utils import fetch_server_config
auth_issuer = '<issuer-endpoint>' # Reemplaza con tu endpoint de emisor
auth_server_config = fetch_server_config(auth_issuer, type=AuthServerType.OIDC)
mcp_auth = MCPAuth(server=auth_server_config)
Actualiza el todo-manager.ts
para incluir la configuración de MCP Auth:
// todo-manager.ts
import { MCPAuth, fetchServerConfig } from 'mcp-auth';
const authIssuer = '<issuer-endpoint>'; // Reemplaza con tu endpoint de emisor
const mcpAuth = new MCPAuth({
server: await fetchServerConfig(authIssuer, { type: 'oidc' }),
});
Actualiza el servidor MCP
¡Ya casi terminamos! Es momento de actualizar el servidor MCP para aplicar la ruta y función middleware de MCP Auth, luego implementar el control de acceso basado en permisos para las herramientas del gestor de tareas según los scopes del usuario.
- Python
- Node.js
@mcp.tool()
def create_todo(content: str) -> dict[str, Any]:
"""Crear una nueva tarea."""
return (
mcp_auth.auth_info.scopes
if mcp_auth.auth_info # Esto será rellenado por el middleware Bearer auth
else {"error": "No autenticado"}
)
# ...
bearer_auth = Middleware(mcp_auth.bearer_auth_middleware("jwt"))
app = Starlette(
routes=[
# Añade la ruta de metadatos (`/.well-known/oauth-authorization-server`)
mcp_auth.metadata_route(),
# Protege el servidor MCP con el middleware Bearer auth
Mount('/', app=mcp.sse_app(), middleware=[bearer_auth]),
],
)
server.tool(
'create-todo',
'Crear una nueva tarea',
{ content: z.string() },
async ({ content, authInfo }) => {
return {
content: [
{ type: 'text', text: JSON.stringify(authInfo?.scopes ?? { error: 'No autenticado' }) },
],
};
}
);
// ...
app.use(mcpAuth.delegatedRouter());
app.use(mcpAuth.bearerAuth('jwt'));
A continuación, implementemos las herramientas específicas.
Primero, vamos a crear un servicio de tareas sencillo para proporcionar operaciones CRUD básicas para gestionar tareas en memoria.
- Python
- Node.js
# service.py
"""
Un servicio de tareas sencillo para fines demostrativos.
Utiliza una lista en memoria para almacenar tareas.
"""
from datetime import datetime
from typing import List, Optional, Dict, Any
import random
import string
class Todo:
"""Representa una tarea."""
def __init__(self, id: str, content: str, owner_id: str, created_at: str):
self.id = id
self.content = content
self.owner_id = owner_id
self.created_at = created_at
def to_dict(self) -> Dict[str, Any]:
"""Convierte la tarea a diccionario para serialización JSON."""
return {
"id": self.id,
"content": self.content,
"ownerId": self.owner_id,
"createdAt": self.created_at
}
class TodoService:
"""Un servicio de tareas sencillo para fines demostrativos."""
def __init__(self):
self._todos: List[Todo] = []
def get_all_todos(self, owner_id: Optional[str] = None) -> List[Dict[str, Any]]:
"""
Obtiene todas las tareas, opcionalmente filtradas por owner_id.
Args:
owner_id: Si se proporciona, solo retorna tareas de este usuario
Returns:
Lista de diccionarios de tareas
"""
if owner_id:
filtered_todos = [todo for todo in self._todos if todo.owner_id == owner_id]
return [todo.to_dict() for todo in filtered_todos]
return [todo.to_dict() for todo in self._todos]
def get_todo_by_id(self, todo_id: str) -> Optional[Todo]:
"""
Obtiene una tarea por su ID.
Args:
todo_id: El ID de la tarea a recuperar
Returns:
Objeto Todo si se encuentra, None en caso contrario
"""
for todo in self._todos:
if todo.id == todo_id:
return todo
return None
def create_todo(self, content: str, owner_id: str) -> Dict[str, Any]:
"""
Crea una nueva tarea.
Args:
content: El contenido de la tarea
owner_id: El ID del usuario propietario de la tarea
Returns:
Diccionario de la tarea creada
"""
todo = Todo(
id=self._generate_id(),
content=content,
owner_id=owner_id,
created_at=datetime.now().isoformat()
)
self._todos.append(todo)
return todo.to_dict()
def delete_todo(self, todo_id: str) -> Optional[Dict[str, Any]]:
"""
Elimina una tarea por su ID.
Args:
todo_id: El ID de la tarea a eliminar
Returns:
Diccionario de la tarea eliminada si se encuentra, None en caso contrario
"""
for i, todo in enumerate(self._todos):
if todo.id == todo_id:
deleted_todo = self._todos.pop(i)
return deleted_todo.to_dict()
return None
def _generate_id(self) -> str:
"""Genera un ID aleatorio para una tarea."""
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=8))
// todo-service.ts
type Todo = {
id: string;
content: string;
ownerId: string;
createdAt: string;
};
/**
* Un servicio de tareas sencillo para fines demostrativos.
* Usa un array en memoria para almacenar tareas
*/
export class TodoService {
private readonly todos: Todo[] = [];
getAllTodos(ownerId?: string): Todo[] {
if (ownerId) {
return this.todos.filter((todo) => todo.ownerId === ownerId);
}
return this.todos;
}
getTodoById(id: string): Todo | undefined {
return this.todos.find((todo) => todo.id === id);
}
createTodo({ content, ownerId }: { content: string; ownerId: string }): Todo {
const todo: Todo = {
id: this.genId(),
content,
ownerId,
createdAt: new Date().toISOString(),
};
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
this.todos.push(todo);
return todo;
}
deleteTodo(id: string): Todo | undefined {
const index = this.todos.findIndex((todo) => todo.id === id);
if (index === -1) {
return undefined;
}
// eslint-disable-next-line @silverhand/fp/no-mutating-methods
const [deleted] = this.todos.splice(index, 1);
return deleted;
}
private genId(): string {
return Math.random().toString(36).slice(2, 10);
}
}
luego en la capa de herramientas, determinaremos si las operaciones están permitidas según los scopes del usuario:
- Python
- Node.js
# todo-manager.py
from typing import Any, Optional
from mcpauth.errors import MCPAuthBearerAuthError
def assert_user_id(auth_info: Optional[dict]) -> str:
"""Extrae y valida el ID de usuario del auth info."""
subject = auth_info.get('subject') if auth_info else None
if not subject:
raise ValueError('Información de autenticación inválida')
return subject
def has_required_scopes(user_scopes: list[str], required_scopes: list[str]) -> bool:
"""Comprueba si el usuario tiene todos los scopes requeridos."""
return all(scope in user_scopes for scope in required_scopes)
# Crea una instancia de TodoService
todo_service = TodoService()
@mcp.tool()
def create_todo(content: str) -> dict[str, Any]:
"""Crear una nueva tarea.
Solo los usuarios con el scope 'create:todos' pueden crear tareas.
"""
# Obtiene la información de autenticación
auth_info = mcp_auth.auth_info
# Valida el ID de usuario
try:
user_id = assert_user_id(auth_info)
except ValueError as e:
return {"error": str(e)}
# Comprueba si el usuario tiene los permisos requeridos
if not has_required_scopes(auth_info.scopes if auth_info else [], ['create:todos']):
raise MCPAuthBearerAuthError('missing_required_scopes')
# Crea nueva tarea
created_todo = todo_service.create_todo(content=content, owner_id=user_id)
# Retorna la tarea creada
return created_todo.__dict__
# ...
Puedes consultar nuestro código de ejemplo para todas las implementaciones detalladas.
// todo-manager.ts
// ... otros imports
import assert from 'node:assert';
import { type AuthInfo } from '@modelcontextprotocol/sdk/server/auth/types.js';
import { TodoService } from './todo-service.js';
const todoService = new TodoService();
const assertUserId = (authInfo?: AuthInfo) => {
const { subject } = authInfo ?? {};
assert(subject, 'Información de autenticación inválida');
return subject;
};
/**
* Comprueba si el usuario tiene todos los scopes requeridos para una operación
*/
const hasRequiredScopes = (userScopes: string[], requiredScopes: string[]): boolean => {
return requiredScopes.every((scope) => userScopes.includes(scope));
};
server.tool(
'create-todo',
'Crear una nueva tarea',
{ content: z.string() },
({ content }: { content: string }, { authInfo }) => {
const userId = assertUserId(authInfo);
/**
* Solo los usuarios con el scope 'create:todos' pueden crear tareas
*/
if (!hasRequiredScopes(authInfo?.scopes ?? [], ['create:todos'])) {
throw new MCPAuthBearerAuthError('missing_required_scopes');
}
const createdTodo = todoService.createTodo({ content, ownerId: userId });
return {
content: [{ type: 'text', text: JSON.stringify(createdTodo) }],
};
}
);
// ...
Puedes consultar nuestro código de ejemplo para todas las implementaciones detalladas.
Punto de control: Ejecuta las herramientas todo-manager
Reinicia tu servidor MCP y abre el inspector MCP en tu navegador. Cuando hagas clic en el botón "Connect", deberías ser redirigido a la página de inicio de sesión de tu servidor de autorización.
Una vez que inicies sesión y regreses al inspector MCP, repite las acciones que hicimos en el punto de control anterior para ejecutar las herramientas del gestor de tareas. Esta vez, puedes usar estas herramientas con tu identidad de usuario autenticada. El comportamiento de las herramientas dependerá de los roles y permisos asignados a tu usuario:
-
Si has iniciado sesión como User (con solo el scope
create:todos
):- Puedes crear nuevas tareas usando la herramienta
create-todo
- Solo puedes ver y eliminar tus propias tareas
- No podrás ver ni eliminar tareas de otros usuarios
- Puedes crear nuevas tareas usando la herramienta
-
Si has iniciado sesión como Admin (con todos los scopes:
create:todos
,read:todos
,delete:todos
):- Puedes crear nuevas tareas
- Puedes ver todas las tareas del sistema usando la herramienta
get-todos
- Puedes eliminar cualquier tarea usando la herramienta
delete-todo
, sin importar quién la creó
Puedes probar estos diferentes niveles de permisos:
- Cerrando la sesión actual (haz clic en el botón "Disconnect" en el inspector MCP)
- Iniciando sesión con una cuenta de usuario diferente que tenga otros roles/permisos
- Probando las mismas herramientas de nuevo para observar cómo cambia el comportamiento según los permisos del usuario
Esto demuestra cómo funciona el control de acceso basado en roles (RBAC) en la práctica, donde diferentes usuarios tienen diferentes niveles de acceso a la funcionalidad del sistema.
- Python
- Node.js
Consulta el repositorio del SDK de MCP Auth Python para el código completo del servidor MCP (versión OIDC).
Consulta el repositorio del SDK de MCP Auth Node.js para el código completo del servidor MCP (versión OIDC).
Notas finales
🎊 ¡Felicidades! Has completado con éxito el tutorial. Recapitulemos lo que hemos hecho:
- Configuración de un servidor MCP básico con herramientas de gestión de tareas (
create-todo
,get-todos
,delete-todo
) - Implementación de control de acceso basado en roles (RBAC) con diferentes niveles de permisos para usuarios y administradores
- Integración del servidor MCP con un servidor de autorización usando MCP Auth
- Configuración del Inspector MCP para autenticar usuarios y usar tokens de acceso con scopes para llamar a herramientas
Asegúrate de consultar otros tutoriales y documentación para sacar el máximo provecho de MCP Auth.