Crea un MCP Server que consume la API de GitHub: listar repos, buscar issues y crear issues mediante Claude.
La API de GitHub es una de las API REST mejor documentadas y utilizadas del mundo. Permite acceder de forma programática a casi todo lo que haces en la interfaz web: repositorios, issues, pull requests, commits y organizaciones. Para nuestro MCP Server, usaremos esta API como base para crear tools potentes que Claude pueda utilizar para interactuar con tus proyectos en GitHub.
La API REST de GitHub está disponible en api.github.com. Los principales endpoints que utilizaremos son: GET /user/repos para listar tus repositorios, GET /repos/{owner}/{repo}/issues para buscar issues de un repositorio específico, y POST /repos/{owner}/{repo}/issues para crear nuevas issues. Todas las respuestas están en formato JSON, lo que facilita el análisis y el formato de los datos. La API tiene límites de solicitudes —5.000 requests por hora con autenticación— que tu servidor debe respetar.
La API de GitHub procesa más de miles de millones de solicitudes al día y la usan prácticamente todas las herramientas de desarrollo modernas: CI/CD, IDE, bots y dashboards. Con autenticación mediante token, el límite de solicitudes aumenta a 5.000 solicitudes/hora, más que suficiente para un MCP Server personal. Sin autenticación, el límite baja a solo 60 requests/hora, lo que hace inviable cualquier uso práctico.
Antes de escribir código, prueba la API manualmente con curl o con herramientas como Insomnia/Postman. Ejecuta curl https://api.github.com/users/{seu-usuario}/repos para ver la estructura del JSON devuelto. Esto facilita mucho la implementación de las tools del MCP Server, porque ya sabrás exactamente qué campos extraer y dar formato.
Para acceder a la API de GitHub de forma autenticada —algo esencial para tener límites de solicitudes razonables y acceder a repositorios privados— necesitas un Personal Access Token (PAT). Este token funciona como una contraseña temporal y con permisos controlados, lo que permite que tu MCP Server acceda a la API en tu nombre sin necesitar tu contraseña real.
Para crear un Personal Access Token: accede GitHub Settings → Developer settings → Personal access tokens → Fine-grained tokens. Selecciona los permisos necesarios: para nuestro proyecto, necesitamos acceso a repos e issues. El token generado debe almacenarse como variable de entorno (por ej.: GITHUB_TOKEN) y se envía mediante headers HTTP como Authorization: Bearer {token}. Nunca pongas el token directamente en el código fuente.
¡NUNCA hagas commit de tokens en el repositorio! Si accidentalmente haces push de un token a GitHub, GitHub Secret Scanning lo revocará automáticamente, pero el daño ya podría estar hecho. Usa siempre variables de entorno, agrega .env a .gitignore, y nunca registres el valor del token en las salidas del servidor.
La primera tool de nuestro MCP Server GitHub será el listado de repositorios. Usando el endpoint GET /user/repos, buscaremos los repositorios del usuario autenticado, los filtraremos por visibilidad, los ordenaremos por fecha de actualización y daremos formato a los resultados para que Claude pueda presentarlos de forma legible al usuario.
La tool de listado de repositorios usa la biblioteca httpx para hacer requests asíncronos a la API de GitHub. El código esencial:
@server.tool()
async def list_repos(visibility: str = "all") -> str:
"""Lista repositórios do usuário autenticado."""
async with httpx.AsyncClient() as client:
resp = await client.get(
"https://api.github.com/user/repos",
headers={"Authorization": f"Bearer {GITHUB_TOKEN}"},
params={"visibility": visibility, "sort": "updated"}
)
repos = resp.json()
lines = []
for r in repos:
stars = r["stargazers_count"]
lang = r.get("language", "N/A")
lines.append(f"⭐ {stars} | {r['name']} ({lang}) — {r.get('description', 'Sem descrição')}")
return "\n".join(lines)
Formatea la salida de modo que el Claude pueda interpretar fácilmente. Incluye el nombre, la descripción, el lenguaje principal y el número de estrellas. Usa parámetros opcionales como visibility (all, public, private) para darle flexibilidad a Claude al consultar. El parámetro sort=updated garantiza que los repos más recientes aparezcan primero.
Claude recibe una solicitud del usuario ("lista mis repos") y llama a la tool list_repos con los parámetros adecuados.
El MCP Server hace GET /user/repos con el token de autenticación y recibe la lista de repositorios en JSON.
La tool da formato a los datos (nombre, descripción, lenguaje, stars) y devuelve texto estructurado para que Claude lo presente.
La segunda tool permite buscar issues de un repositorio específico. Usando el endpoint GET /repos/{owner}/{repo}/issues, Claude podrá consultar issues abiertas o cerradas, filtrar por labels y presentar información relevante como número, título, autor y labels asignadas.
La tool de búsqueda de issues recibe owner (propietario del repo), repo (nombre del repositorio) y parámetros opcionales como state (open/closed/all) y labels (filtrar por etiquetas). El endpoint devuelve un array JSON donde cada issue contiene: number, title, user.login, state, labels[] e created_at. Da formato a la salida con número, título, autor y labels para facilitar la lectura.
Un detalle importante: el endpoint /repos/{owner}/{repo}/issues devuelve tanto issues como pull requests. Para filtrar solo issues reales, verifica si el campo pull_request falta en el objeto. La API admite paginación mediante los parámetros page e per_page (máximo de 100 por página).
La tercera tool es la más poderosa — y la más peligrosa. Al usar POST /repos/{owner}/{repo}/issues, Claude podrá crear issues directamente en tu repositorio. Esta es una acción de escritura, lo que exige validar cuidadosamente los parámetros y ser conscientes de que estamos dando al modelo la capacidad de modificar datos reales.
La tool de creación de issues envía un POST con body JSON que contiene title (obligatorio) y body (descripción, opcional pero recomendada). Valida que el título no esté vacío y tenga una longitud razonable. Después de crearla, devuelve la URL de la issue creada para que el usuario pueda acceder directamente:
@server.tool()
async def create_issue(owner: str, repo: str, title: str, body: str = "") -> str:
"""Cria uma nova issue em um repositório GitHub."""
if not title.strip():
return "Erro: o título da issue não pode ser vazio."
async with httpx.AsyncClient() as client:
resp = await client.post(
f"https://api.github.com/repos/{owner}/{repo}/issues",
headers={"Authorization": f"Bearer {GITHUB_TOKEN}"},
json={"title": title, "body": body}
)
if resp.status_code == 201:
issue = resp.json()
return f"Issue criada com sucesso! #{issue['number']}: {issue['html_url']}"
return f"Erro ao criar issue: {resp.status_code}"
Crear issues es una acción de escritura — modifica datos reales en tu GitHub. A diferencia de listar o buscar, esta tool tiene efectos secundarios permanentes. Considera agregar una confirmación antes de ejecutar la creación o, al menos, registrar cada issue creada. En un entorno corporativo, esta tool debe tener control de acceso riguroso y un audit trail completo.
Siempre devuelve la URL del issue creado en la respuesta — esto permite que Claude le presente al usuario un enlace en el que pueda hacer clic. Valida los parámetros antes de enviar el request: título no vacío, owner y repo con el formato correcto. Considera agregar parámetros opcionales como labels e assignees para crear issues más completas.
Con las tres tools implementadas —listar repos, buscar issues y crear issues—, es hora de probar la integración completa. Configura el server en Claude Desktop y prueba comandos en lenguaje natural para verificar que todo funcione correctamente. Claude debería poder elegir automáticamente la tool adecuada según tu solicitud.
Prueba con estos prompts en Claude Desktop:
Pídele a Claude que liste tus repositorios. Verifica si los datos coinciden con lo que aparece en la interfaz web de GitHub.
Pídele que busque issues en un repositorio real. Confirma que los números, títulos y labels sean correctos.
Pídele que cree una issue de prueba. Verifica en GitHub si la issue se creó con el título y el cuerpo correctos.
Pídele que busque issues en un repositorio que no existe. Verifica si el error se maneja correctamente.
Siguiente Módulo: 4.5 — Combinando varios servidores