Lección 40 · 10 min · Gratis

Administra credenciales de conectores

Almacena y gestiona múltiples conjuntos de credenciales para un solo conector, luego llama a las herramientas con credenciales específicas para controlar el acceso en tiempo de ejecución.

Este manual cubre dos patrones de autenticación:

  • Token de portador — tokens estáticos (PAT de GitHub), almacenados directamente a través de la API.
  • OAuth2 — flujos de autenticación delegada (cuentas de Microsoft), iniciados a través de get_auth_url.

Estado de la API: La gestión de credenciales utiliza client.beta.connectors. Este es un endpoint en beta y puede cambiar.


Parte 1: Múltiples credenciales de token de portador (GitHub MCP)

Requisitos previos (Portador)

Instalar

# Python
pip install mistralai
# or with uv
uv add mistralai

Para completar este manual, necesitarás una clave de API de Mistral. En Studio, navega a la sección de claves de API y crea una nueva clave de API.

MISTRAL_API_KEY=your-mistral-api-key
GITHUB_PAT_FULL=ghp_yourFullAccessToken
GITHUB_PAT_LIMITED=ghp_yourLimitedOrInvalidToken
  • GITHUB_PAT_FULL — un PAT con alcance de lectura repo, utilizado para listar problemas con éxito.
  • GITHUB_PAT_LIMITED — un PAT sin alcances o con un valor inválido, utilizado para demostrar una llamada rechazada.

Crea tokens de acceso personal de GitHub en tu configuración de desarrollador de GitHub.

Script: python/src/scripts/07_multiple_bearer_authentication.py


Cuándo usar múltiples credenciales de portador

  • Probar niveles de acceso — verifica que un token restringido no pueda acceder a recursos que un token completo sí puede.
  • Rotar credenciales de forma segura — añade las nuevas credenciales, promuévelas a predeterminadas y luego elimina las antiguas sin tiempo de inactividad.
  • Alcance explícito de las llamadas a herramientas — pasa credentials_name a call_tool para elegir qué identidad ejecuta la solicitud.

1. Inicializa el cliente

Python:

import os
from mistralai import Mistral

client = Mistral(api_key=os.environ["MISTRAL_API_KEY"])

curl:

export MISTRAL_API_KEY="your-api-key"
export BASE_URL="https://api.mistral.ai"

2. Crea un conector GitHub MCP

Puedes omitir este paso si usas un conector existente con autenticación de portador.

Python:

import asyncio
import json
import os
import subprocess

BASE_URL = "https://api.mistral.ai"
API_KEY = os.environ["MISTRAL_API_KEY"]


async def main() -> None:
    result = subprocess.run(
        [
            "curl", "-s", "-X", "POST",
            f"{BASE_URL}/v1/connectors",
            "-H", f"Authorization: Bearer {API_KEY}",
            "-H", "Content-Type: application/json",
            "-d", json.dumps({
                "name": "my_github",
                "description": "GitHub MCP connector for issue and PR management",
                "server": "https://api.githubcopilot.com/mcp/",
                "visibility": "private",
                "auth_scheme": {"type": "http", "scheme": "Bearer"},
            }),
        ],
        capture_output=True,
        text=True,
        check=True,
    )
    connector = json.loads(result.stdout)
    if "id" not in connector:
        raise RuntimeError(f"Failed to create connector: {result.stdout}")
    print(f"ID:   {connector['id']}")
    print(f"Name: {connector['name']}")


asyncio.run(main())

curl:

curl -X POST "${BASE_URL}/v1/connectors" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "my_github",
    "description": "GitHub MCP connector for issue and PR management",
    "server": "https://api.githubcopilot.com/mcp/",
    "visibility": "private",
    "auth_scheme": {"type": "http", "scheme": "Bearer"}
  }'

Salida:

ID:   a1b2c3d4-5678-90ab-cdef-1234567890ab
Name: my_github
Error Causa Solución
409 Conflict Ya existe un conector llamado my_github Elige un nombre diferente o elimina el existente primero

3. Obtén los métodos de autenticación

Objetivo: Descubrir qué esquemas de autenticación soporta el conector antes de almacenar las credenciales.

Nota: el connector_id_or_name es un poco feo y será reemplazado por connector_ref en futuras versiones.

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    methods = await client.beta.connectors.get_authentication_methods_async(
        connector_id_or_name="my_github",
    )
    for method in methods:
        print(f"Auth type: {method.method_type}")


asyncio.run(main())

curl:

curl -X GET "${BASE_URL}/v1/connectors/my_github/authentication_methods" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}"

Ejemplo de salida:

Auth type: bearer

4. Almacena las credenciales de portador

Objetivo: Almacenar credenciales de token de portador con nombre en el conector.

Las credenciales se pueden almacenar en tres ámbitos:

Ámbito Método del SDK Quién puede usarlo
user create_or_update_user_credentials Solo el usuario autenticado
workspace create_or_update_workspace_credentials Todos en el espacio de trabajo
organization create_or_update_organization_credentials Todos en la organización

Python:

import asyncio
import os
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    # Credentials A — full repo read access, set as default
    result = await client.beta.connectors.create_or_update_user_credentials_async(
        connector_id_or_name="my_github",
        name="github-pat-full",
        credentials={"bearer_token": os.environ["GITHUB_PAT_FULL"]},
        is_default=True,
    )
    print(result.message)

    # Credentials B — no scopes / invalid token
    result = await client.beta.connectors.create_or_update_user_credentials_async(
        connector_id_or_name="my_github",
        name="github-pat-limited",
        credentials={"bearer_token": os.environ["GITHUB_PAT_LIMITED"]},
    )
    print(result.message)


asyncio.run(main())

curl:

# Credentials A — full repo read access (set as default)
curl -X POST "${BASE_URL}/v1/connectors/my_github/user/credentials" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"github-pat-full\",
    \"credentials\": {\"bearer_token\": \"${GITHUB_PAT_FULL}\"},
    \"is_default\": true
  }"

# Credentials B — no scopes / invalid token
curl -X POST "${BASE_URL}/v1/connectors/my_github/user/credentials" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}" \
  -H "Content-Type: application/json" \
  -d "{
    \"name\": \"github-pat-limited\",
    \"credentials\": {\"bearer_token\": \"${GITHUB_PAT_LIMITED}\"}
  }"

Salida:

Credentials 'github-pat-full' saved successfully
Credentials 'github-pat-limited' saved successfully

Cómo funciona:

  • is_default: true marca las credenciales como predeterminadas — las llamadas que omitan credentials_name las usarán.
  • Llamar al mismo endpoint de nuevo con un name existente actualiza el token almacenado en su lugar.
  • El token sin procesar nunca es devuelto por los endpoints de lista o de obtención.
Error Causa Solución
400 Bad Request Objeto de credenciales vacío Proporciona al menos bearer_token
422 Unprocessable Entity Nombre de credenciales inválido Usa solo nombres alfanuméricos con guiones

5. Lista las credenciales

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    response = await client.beta.connectors.list_user_credentials_async(
        connector_id_or_name="my_github",
    )
    for cred in response.credentials:
        default_marker = " (default)" if cred.is_default else ""
        print(f"  {cred.name}  [{cred.authentication_type}]{default_marker}")


asyncio.run(main())

curl:

curl -X GET "${BASE_URL}/v1/connectors/my_github/user/credentials" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}"

Salida:

  github-pat-full  [bearer] (default)
  github-pat-limited  [bearer]

6. Llama a una herramienta con credenciales específicas

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    # Call with the full-access credentials — should succeed
    result = await client.beta.connectors.call_tool_async(
        connector_id_or_name="my_github",
        tool_name="list_issues",
        arguments={"owner": "octocat", "repo": "hello-world", "state": "open"},
        credentials_name="github-pat-full",
    )
    print(f"[github-pat-full] {result.content[:200]}")

    # Call with the limited/invalid credentials — access error is in the response content
    result = await client.beta.connectors.call_tool_async(
        connector_id_or_name="my_github",
        tool_name="list_issues",
        arguments={"owner": "octocat", "repo": "hello-world", "state": "open"},
        credentials_name="github-pat-limited",
    )
    print(f"[github-pat-limited] {result.content[:200]}")


asyncio.run(main())

curl:

curl -X POST "${BASE_URL}/v1/connectors/my_github/call_tool" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "tool_name": "list_issues",
    "arguments": {"owner": "octocat", "repo": "hello-world", "state": "open"},
    "credentials_name": "github-pat-full"
  }'

Ejemplo de salida:

[github-pat-full] [{"number": 42, "title": "Fix typo in README", "state": "open", ...}]
[github-pat-limited] {"error": "Bad credentials", "status": 401}

Cómo funciona:

  • credentials_name selecciona qué credenciales almacenadas recibe el servidor MCP. Omítelo para usar las predeterminadas.
  • Si las credenciales con nombre no existen, la llamada devuelve un 404.

7. Elimina credenciales

Nota: No puedes eliminar las credenciales predeterminadas mientras existan otras credenciales. Primero, promueve otras credenciales a predeterminadas y luego elimina las antiguas.

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    result = await client.beta.connectors.delete_user_credentials_async(
        connector_id_or_name="my_github",
        credentials_name="github-pat-limited",
    )
    print(result.message)


asyncio.run(main())

curl:

curl -X DELETE "${BASE_URL}/v1/connectors/my_github/user/credentials/github-pat-limited" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}"

Salida:

Credentials 'github-pat-limited' deleted successfully
Error Causa Solución
404 Not Found El nombre de las credenciales no existe Verifica el nombre con list_user_credentials primero
409 Conflict Intentando eliminar las credenciales predeterminadas actuales mientras existen otras Promueve otras credenciales a predeterminadas primero

Parte 2: Múltiples credenciales OAuth2 (ej: Outlook Calendar MCP)

Requisitos previos (OAuth2)

MISTRAL_API_KEY=your-mistral-api-key
  • El conector outlook_calendar debe estar habilitado en tu espacio de trabajo. Habilítalo en Studio.
  • Necesitas dos cuentas de Microsoft para autenticarte por separado.

Script: python/src/scripts/08_multiple_oauth_authentication.py


Cuándo usar múltiples credenciales OAuth2

  • Acceso multi-cuenta — permite que un solo usuario se autentique con múltiples identidades (por ejemplo, cuentas de Microsoft de trabajo y personales) y cambie entre ellas en el momento de la llamada.
  • Delegación por usuario — cada usuario en un espacio de trabajo autentica su propia cuenta; credentials_name enruta las llamadas a herramientas a la correcta.
  • Rotación segura — autentica la nueva cuenta bajo un nuevo nombre, promuévela a predeterminada y luego revoca la antigua.

1. Obtén el conector de Outlook Calendar

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    connector = await client.beta.connectors.get_async(
        connector_id_or_name="outlook_calendar",
    )
    print(f"ID:   {connector.id}")
    print(f"Name: {connector.name}")


asyncio.run(main())

curl:

curl -X GET "${BASE_URL}/v1/connectors/outlook_calendar" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}"

2. Obtén los métodos de autenticación

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    methods = await client.beta.connectors.get_authentication_methods_async(
        connector_id_or_name="outlook_calendar",
    )
    for method in methods:
        print(f"Auth type: {method.method_type}")


asyncio.run(main())

curl:

curl -X GET "${BASE_URL}/v1/connectors/outlook_calendar/authentication_methods" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}"

Ejemplo de salida:

Auth type: oauth2

3. Autentica cuentas vía OAuth2

Objetivo: Obtener URLs de autorización OAuth2 y permitir que cada cuenta complete el flujo del navegador. El parámetro credentials_name controla en qué ranura con nombre se almacena el token resultante. Omitirlo almacena el token como las credenciales predeterminadas.

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    # Account A — stored as the default credentials
    result = await client.beta.connectors.get_auth_url_async(
        connector_id_or_name="outlook_calendar",
        # no credentials_name => stored under name="default"
    )
    print(f"Follow this link to authenticate account A: {result.auth_url}")
    input("Press Enter once done")

    # Account B — stored under the name "personal"
    result = await client.beta.connectors.get_auth_url_async(
        connector_id_or_name="outlook_calendar",
        credentials_name="personal",
    )
    print(f"Follow this link to authenticate account B: {result.auth_url}")
    input("Press Enter once done")


asyncio.run(main())

curl:

# Account A — default credentials
curl -X GET "${BASE_URL}/v1/connectors/outlook_calendar/auth_url" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}"

# Account B — named "personal"
curl -X GET "${BASE_URL}/v1/connectors/outlook_calendar/auth_url?credentials_name=personal" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}"

Cómo funciona:

  • get_auth_url devuelve una URL que el usuario debe abrir en un navegador para completar el flujo de consentimiento de OAuth2.
  • Una vez que el flujo se completa, el token se almacena automáticamente bajo el credentials_name dado (o como default si se omite).
  • El script se pausa con input() para darle tiempo al usuario de completar el flujo del navegador antes de continuar.

4. Lista las credenciales

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    response = await client.beta.connectors.list_user_credentials_async(
        connector_id_or_name="outlook_calendar",
    )
    for cred in response.credentials:
        default_marker = " (default)" if cred.is_default else ""
        print(f"  {cred.name}  [{cred.authentication_type}]{default_marker}")


asyncio.run(main())

Salida:

  default  [oauth2] (default)
  personal  [oauth2]

5. Llama a una herramienta con credenciales específicas

Objetivo: Invocar una herramienta de calendario usando credenciales con nombre para consultar el calendario de una cuenta específica.

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    # Query the default account
    result = await client.beta.connectors.call_tool_async(
        connector_id_or_name="outlook_calendar",
        tool_name="search_calendar_events",
        arguments={"query": "meeting"},
        credentials_name="default",
    )
    print(f"[default] {result.content[:300]}")

    # Query the personal account
    result = await client.beta.connectors.call_tool_async(
        connector_id_or_name="outlook_calendar",
        tool_name="search_calendar_events",
        arguments={"query": "meeting"},
        credentials_name="personal",
    )
    print(f"[personal] {result.content[:300]}")


asyncio.run(main())

curl:

curl -X POST "${BASE_URL}/v1/connectors/outlook_calendar/call_tool" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{"tool_name": "search_calendar_events", "arguments": {"query": "meeting"}, "credentials_name": "default"}'

6. Promueve credenciales a predeterminadas

Para cambiar qué cuenta se usa cuando se omite credentials_name, actualiza is_default sin proporcionar un nuevo token — el token OAuth2 almacenado se conserva.

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    await client.beta.connectors.create_or_update_user_credentials_async(
        connector_id_or_name="outlook_calendar",
        name="personal",
        is_default=True,
    )
    print("Promoted 'personal' to default")

    # Now call without specifying credentials — uses personal account
    result = await client.beta.connectors.call_tool_async(
        connector_id_or_name="outlook_calendar",
        tool_name="search_calendar_events",
        arguments={"query": "meeting"},
    )
    print(f"[default] {result.content[:300]}")


asyncio.run(main())

7. Elimina credenciales

Nota: No puedes eliminar las credenciales predeterminadas mientras existan otras credenciales. Primero, promueve otras credenciales a predeterminadas.

Python:

import asyncio
from mistralai import Mistral

client = Mistral(api_key="your-api-key")


async def main() -> None:
    for name in ("default", "personal"):
        result = await client.beta.connectors.delete_user_credentials_async(
            connector_id_or_name="outlook_calendar",
            credentials_name=name,
        )
        print(result.message)


asyncio.run(main())

curl:

curl -X DELETE "${BASE_URL}/v1/connectors/outlook_calendar/user/credentials/personal" \
  -H "Authorization: Bearer ${MISTRAL_API_KEY}"

Convenciones de nombres

Concepto Python
Obtener conector client.beta.connectors.get_async(connector_id_or_name=)
Obtener métodos de autenticación get_authentication_methods_async(connector_id_or_name=)
Obtener URL de OAuth2 get_auth_url_async(connector_id_or_name=, credentials_name=)
Almacenar credenciales de portador create_or_update_user_credentials_async(connector_id_or_name=, name=, credentials={"bearer_token": ...}, is_default=)
Promover a predeterminado create_or_update_user_credentials_async(connector_id_or_name=, name=, is_default=True)
Listar credenciales de usuario list_user_credentials_async(connector_id_or_name=)
Eliminar credenciales de usuario delete_user_credentials_async(connector_id_or_name=, credentials_name=)
Llamar herramienta call_tool_async(connector_id_or_name=, tool_name=, arguments=, credentials_name=)
Ámbito: espacio de trabajo *_workspace_credentials*
Ámbito: organización *_organization_credentials*

Solución de problemas

Las credenciales no surten efecto al llamar a una herramienta

  • Verifica que las credenciales se hayan guardado: list_user_credentials.
  • Comprueba que credentials_name coincida exactamente con el nombre almacenado (distingue mayúsculas y minúsculas).
  • Si se omite credentials_name, se usan las credenciales predeterminadas — confirma cuáles son las predeterminadas con list_user_credentials.

El token OAuth2 no se almacena después de completar el flujo del navegador

  • Asegúrate de haber presionado Enter después de completar el consentimiento en el navegador, no antes.
  • Si la URL de autenticación expiró, llama a get_auth_url de nuevo para obtener una nueva.

401 Unauthorized del servidor MCP (GitHub)

  • El PAT puede haber expirado. Vuelve a ejecutar create_or_update_user_credentials con el mismo nombre para rotarlo en su lugar.
  • El PAT puede carecer de los alcances requeridos.

No se pueden eliminar las credenciales predeterminadas

  • Promueve otras credenciales a predeterminadas primero: create_or_update_user_credentials(..., is_default=True), luego elimina las antiguas.

403 Forbidden en los endpoints de gestión de credenciales

  • Las credenciales a nivel de organización requieren el permiso de organización ModifyConnector.
  • Las credenciales a nivel de espacio de trabajo requieren el permiso de espacio de trabajo ModifyConnector.
  • Las credenciales a nivel de usuario solo requieren autenticación.

Referencia de códigos de error

Estado HTTP Cuándo ocurre Qué hacer
400 Bad Request Objeto de credenciales vacío, o is_default: false en las únicas credenciales existentes Proporciona bearer_token; siempre mantén una credencial como predeterminada
401 Unauthorized Clave de API de Mistral inválida, o el servidor MCP rechazó el token almacenado Verifica tu MISTRAL_API_KEY; rota las credenciales
403 Forbidden Permisos insuficientes para el ámbito elegido Usa un ámbito inferior o solicita el permiso ModifyConnector
404 Not Found El nombre de las credenciales o del conector no existe Verifica los nombres con list_user_credentials
409 Conflict El nombre del conector ya está en uso, o se está eliminando el predeterminado activo Cambia el nombre del conector o promueve otras credenciales a predeterminadas primero
422 Unprocessable Entity Formato de nombre de credenciales inválido Usa solo caracteres alfanuméricos y guiones

Resumen

Este manual cubrió cómo almacenar y gestionar múltiples conjuntos de credenciales para un solo Conector —tanto tokens de portador (PAT de GitHub) como OAuth2 (Outlook Calendar)— y cómo enrutar llamadas de herramientas específicas a credenciales específicas en tiempo de ejecución.

Lo que cubre este manual:

  • Almacenar múltiples credenciales de token de portador para un conector GitHub MCP
  • Almacenar múltiples credenciales OAuth2 para un conector Outlook Calendar MCP
  • Listar, seleccionar y eliminar credenciales
  • Promover credenciales a predeterminadas
  • Llamar a herramientas con una credencial con nombre específico

Características de Mistral utilizadas:

  • API de Conectores — gestión de credenciales (beta)

Otros servicios:

  • GitHub MCP — Conector autenticado por portador (tokens de acceso personal)
  • Outlook Calendar MCP — Conector autenticado por OAuth2

Consulta tus Conectores en Studio.

Lección del curso «Mistral Cookbook» de Mistral AI, publicado con licencia MIT. Traducción y adaptación al español de IA con Clase. IA con Clase no está afiliado a Mistral AI. Ver el original · Licencia
Esta lección es gratuita. El resto del curso se abre con la Membresía de IA con Clase, que incluye todos los cursos del catálogo. Ver precios