Jira Thing

Un servidor MCP de ejemplo para interactuar con Jira, desplegable en Cloudflare Workers.

Documentación

Servidor Model Context Protocol (MCP) con GitHub OAuth

Este proyecto proporciona una plantilla para un servidor Model Context Protocol (MCP) que utiliza GitHub para la autenticación OAuth 2.0. Está construido para ejecutarse en Cloudflare Workers, proporcionando una base robusta y escalable para tus propios servicios MCP remotos.

Los usuarios pueden conectarse a tu servidor MCP desplegado, y se les pedirá que inicien sesión con su cuenta de GitHub para autorizar el acceso.

Características

  • Integración con GitHub OAuth: Autentica de forma segura a los usuarios mediante GitHub, actuando como cliente OAuth hacia GitHub y como servidor OAuth hacia el cliente MCP.
  • Carga dinámica de herramientas: Demuestra cómo exponer condicionalmente herramientas según la identidad del usuario autenticado.
  • Herramientas de ejemplo: Incluye dos herramientas de muestra:
    • Una herramienta pública add disponible para todos los usuarios autenticados.
    • Una herramienta restringida generateImage que solo está disponible para una lista predefinida de usuarios autorizados.
  • Despliegue sin servidor: Construido en Cloudflare Workers para una arquitectura sin servidor escalable y de bajo mantenimiento.
  • Gestión segura de secretos: Utiliza secretos de Wrangler para gestionar credenciales sensibles, evitando valores codificados en el código fuente.

Primeros pasos

Requisitos previos

Instalación

  1. Clona el repositorio:

    git clone https://github.com/PortNumber53/mcp-jira-thing.git
    cd mcp-jira-thing
    
  2. Instala las dependencias:

    npm install
    

Frontend React (frontend/)

Una aplicación de una sola página React + Vite se encuentra en frontend/. Proporciona el flujo de inicio de sesión de GitHub para los usuarios y se puede desarrollar con el servidor estándar de Vite.

cd mcp-jira-thing
npm install    # install Worker + shared dependencies (repo root)

cd frontend
npm install    # install local dependencies before development
npm run dev          # starts the Vite development server with HMR
npm run dev:worker   # runs the merged Worker locally on :18112

Para compilaciones y despliegues reproducibles:

npm run build   # runs tsc + vite build (Worker deploy is from repo root)
npm run deploy  # uploads the merged Worker and SPA assets via wrangler deploy

El Worker de Cloudflare se define en la raíz del repositorio (src/index.ts). Sirve la SPA desde frontend/dist/client en / y expone el servidor MCP bajo /sse (y /mcp). El despliegue se realiza desde la raíz del repositorio con npm run deploy, que primero compila la SPA. Ejecutar npm run dev:worker en frontend/ inicia el mismo Worker fusionado localmente usando ../wrangler.jsonc.

Aplicación de Slack (integración de proyectos de Jira por canal)

Si deseas que los usuarios de Slack interactúen con Jira en el contexto de un canal de Slack (por ejemplo, crear/buscar incidencias contra un proyecto de Jira predeterminado por canal), conecta una aplicación de Slack a este Worker y almacena una asignación:

  • ID del canal de SlackClave del proyecto de Jira (por ejemplo, C01234567ENG)

Este repositorio no incluye aún endpoints de Slack, pero los pasos a continuación describen la configuración que necesitarás una vez que los agregues.

1) Crear una aplicación de Slack

En Slack, crea una aplicación desde cero y habilita:

  • Interactividad y accesos directos (opcional pero recomendado)
  • Comandos de barra (recomendado)
  • Suscripciones a eventos (opcional; útil para menciones de @yourapp)
  • OAuth y permisos

2) Configuración de OAuth + alcances requeridos

Agrega una URL de redirección para el Worker, por ejemplo:

  • http://localhost:18112/slack/oauth/callback (local)
  • https://<your-worker>.<your-subdomain>.workers.dev/slack/oauth/callback (producción)

Alcances sugeridos para el token del bot (ajusta según tus necesidades):

  • commands: habilita comandos de barra como /jira
  • chat:write: publicar respuestas/mensajes
  • channels:read y/o groups:read: leer metadatos del canal (público vs privado)
  • app_mentions:read (si se usan eventos para menciones)
  • users:read (si deseas mostrar nombres de usuario / enriquecer mensajes)

3) Configurar las URLs de solicitud de Slack (endpoints del Worker que agregarás)

Una vez implementado, Slack debería apuntar a endpoints del Worker como:

  • URL de solicitud de comando de barra: .../slack/commands
  • URL de solicitud de interactividad: .../slack/interactions
  • URL de solicitud de eventos (si está habilitado): .../slack/events

Todos estos endpoints deben:

  • Verificar las firmas de Slack usando SLACK_SIGNING_SECRET
  • Responder dentro de 3 segundos (usa response_url o seguimientos asíncronos para llamadas lentas a Jira)

4) Almacenar la asignación “canal → proyecto de Jira”

Enfoque recomendado en este repositorio:

  • Almacenar la asignación en Cloudflare KV (simple) o en un Durable Object (consistencia fuerte)
  • Clave por ID de canal, el valor incluye al menos { projectKey, updatedAt, updatedBy }

Ejemplo de clave de asignación:

  • slack:channel-project:C01234567{"projectKey":"ENG","updatedAt":...}

5) UX sugerida de Slack (comandos)

Un patrón práctico es un comando /jira que establece o usa el proyecto predeterminado del canal:

  • Establecer predeterminado: /jira project set ENG
  • Mostrar predeterminado: /jira project get
  • Crear incidencia: /jira create "Bug title" --type Task
  • Buscar: /jira search status=Open assignee=me

Detalle de implementación: el manejador lee el ID del canal desde el payload de Slack, busca la clave del proyecto para ese canal y luego llama al cliente/herramientas de Jira existentes en src/tools/jira/ para realizar la acción solicitada.

6) Variables de entorno que necesitarás

Agrega estas como secretos/variables de Wrangler (los nombres son sugerencias; elige una convención y mantente fiel a ella):

  • SLACK_SIGNING_SECRET: requerido para verificar solicitudes de Slack
  • SLACK_CLIENT_ID / SLACK_CLIENT_SECRET: requeridos para el flujo de instalación de OAuth de Slack
  • SLACK_BOT_TOKEN: requerido para llamar a las APIs de Slack (o almacenar tokens por espacio de trabajo después de OAuth)

Si soportas múltiples espacios de trabajo de Slack, almacena la información de instalación del espacio/equipo clave por team_id, no globalmente.

Backend Go (backend/)

El backend Go expone endpoints REST que sirven datos al frontend (u otros consumidores). La implementación inicial incluye:

  • GET /healthz — sonda de salud simple para balanceadores de carga y comprobaciones de humo de Jenkins.
  • GET /api/users?limit=50 — devuelve una lista paginada de usuarios de NextAuth desde la base de datos.

Variables de entorno

Crea una copia de backend/env.example y proporciona los valores requeridos:

VariableRequeridoDescripción
BACKEND_ADDRopcionalDirección en la que el servidor HTTP escucha. Por defecto :18111.
DATABASE_URLDSN de Postgres utilizado por el backend en tiempo de ejecución.
MCP_SESSION_API_TOKENopcionalCredencial interna de Node MCP → backend; por defecto COOKIE_SECRET.
BACKEND_HTTP_TIMEOUT_SECONDSopcionalTiempo de espera de solicitudes salientes, por defecto 15 segundos.

go test y el código en tiempo de ejecución esperan que las variables de entorno estén presentes. Cuando se ejecuta localmente, puedes exportarlas o usar un cargador dotenv (direnv, dotenvx, etc.).

Desarrollo local

cd backend
cp env.example .env   # edit with your credentials (or export env vars)
go test ./...
go run ./cmd/server

# or via make
make test
make run

Recarga en caliente con Air

Air ofrece recarga en vivo para aplicaciones Go, de modo que los cambios se reconstruyen y reinician automáticamente durante el desarrollo, reduciendo los bucles de retroalimentación 1.

# install once (requires Go 1.25+)
go install github.com/air-verse/air@latest

# start the watcher from the backend directory
cd backend
make dev  # runs `air -c .air.toml`

Air usa la configuración en backend/.air.toml para reconstruir ./tmp/main cada vez que cambian los archivos Go o de entorno, y luego reinicia el servidor de forma transparente. Asegúrate de que los valores de .env estén presentes antes de lanzar el observador.

Flujo de trabajo de despliegue

  • Compilar/Probar: make build compila un binario estático de Linux en backend/bin/mcp-backend. make test ejecuta las pruebas unitarias. Ambos comandos son orquestados por el pipeline de Jenkins (ver más abajo).
  • Artefacto: Jenkins comprime el binario a backend/bin/mcp-backend.tar.gz y lo publica como artefacto de compilación.
  • Script de despliegue: scripts/deploy-backend.sh compila de forma cruzada el binario de Linux, lo sube mediante scp, lo descomprime en $DEPLOY_PATH y opcionalmente reinicia un servicio systemd cuando se proporciona SERVICE_NAME.

Pipeline de Jenkins

Este repositorio ahora contiene un Jenkinsfile de nivel superior que realiza las siguientes etapas:

  1. Checkout — obtiene el repositorio para la compilación actual.
  2. Prueba de Go — ejecuta go test ./... dentro de backend/.
  3. Compilar Backend — ejecuta make build para generar el binario mcp-backend.
  4. Archivar Artefacto — comprime el binario y lo archiva para su recuperación posterior.
  5. Desplegar (solo master) — ejecuta scripts/deploy-backend.sh, que espera que las siguientes variables de entorno sean proporcionadas por las credenciales de Jenkins o la configuración del trabajo:
    • DEPLOY_HOST: Host/IP del servidor de producción (Arch Linux).
    • DEPLOY_USER: Usuario SSH con permiso para escribir en DEPLOY_PATH y ejecutar sudo systemctl restart en el servicio objetivo.
    • DEPLOY_PATH: Directorio de destino en el servidor (por ejemplo, /opt/mcp-backend).
    • SERVICE_NAME (opcional): nombre de la unidad systemd para reiniciar después del despliegue.

La etapa de despliegue solo se ejecuta para compilaciones en la rama master, por lo que las ramas de características permanecen solo para pruebas. Jenkins debe proporcionar acceso SSH, típicamente mediante una credencial de clave SSH asociada con la cuenta DEPLOY_USER. Revisa scripts/deploy-backend.sh para obtener detalles adicionales o puntos de personalización.

Configuración y despliegue

Sigue estos pasos para configurar y desplegar tu servidor MCP.

1. Crear una aplicación OAuth de GitHub

Primero, necesitas crear una aplicación OAuth de GitHub para obtener tus credenciales de cliente.

  • URL de página de inicio: https://<your-worker-name>.<your-subdomain>.workers.dev
  • URL de devolución de llamada de autorización: https://<your-worker-name>.<your-subdomain>.workers.dev/callback/github

Una vez que se crea la aplicación, anota el ID de cliente y genera un nuevo secreto de cliente.

2. Configurar secretos

A continuación, usa Wrangler para almacenar de forma segura tus credenciales de GitHub y una clave de cifrado de sesión como secretos.

# Will prompt for your GitHub Client ID
npx wrangler secret put GITHUB_CLIENT_ID

# Will prompt for your GitHub Client Secret
npx wrangler secret put GITHUB_CLIENT_SECRET

# Will prompt for a random string used to sign session cookies
npx wrangler secret put SESSION_SECRET

# Optionally, provide COOKIE_SECRET if you need an override for local testing
npx wrangler secret put COOKIE_SECRET

Para el SESSION_SECRET, puedes generar una cadena aleatoria segura con openssl rand -hex 32.

3. Almacenamiento de sesión

El estado de OAuth y los datos de sesión del usuario se almacenan en cookies firmadas y solo HTTP; no se requiere un espacio de nombres de Cloudflare KV. Asegúrate de que SESSION_SECRET (o COOKIE_SECRET) esté configurado para que el Worker pueda firmar y validar esas cookies de forma segura. Si anteriormente usaste COOKIE_ENCRYPTION_KEY, renombra ese secreto a SESSION_SECRET.

4. Autorizar usuarios

Para otorgar acceso a herramientas restringidas como generateImage, debes agregar los nombres de usuario de GitHub de los usuarios autorizados al conjunto ALLOWED_USERNAMES en src/index.ts.

// src/index.ts
const ALLOWED_USERNAMES = new Set<string>([
  "PortNumber53",
  // Add other authorized GitHub usernames here
]);

5. Desplegar el Worker

Finalmente, despliega tu worker configurado en Cloudflare.

npx wrangler deploy

Herramientas disponibles

Este servidor MCP expone las siguientes herramientas:

  • add

    • Descripción: Suma dos números.
    • Acceso: Público (disponible para todos los usuarios autenticados).
    • Parámetros: a (número), b (número).
  • generateImage

    • Descripción: Genera una imagen usando el modelo @cf/black-forest-labs/flux-1-schnell.
    • Acceso: Restringido (solo disponible para usuarios en ALLOWED_USERNAMES).
    • Parámetros: prompt (cadena), steps (número, 4-8).

Uso

Puedes probar tu servidor remoto usando el Inspector MCP:

npx @modelcontextprotocol/inspector@latest

Ingresa la URL SSE de tu worker (https://<your-worker-name>.<your-subdomain>.workers.dev/sse) y haz clic en Conectar. Serás redirigido a GitHub para autenticarte. Una vez autenticado, verás las herramientas disponibles en el Inspector.

MCP Inspector showing available tools

Estructura del proyecto

  • src/index.ts: El punto de entrada principal para el Worker de Cloudflare. Define el servidor MCP, sus herramientas y la lógica para el acceso condicional a herramientas.
  • src/github-handler.ts: Contiene la lógica para manejar el flujo de OAuth de GitHub.
  • src/workers-oauth-utils.ts: Proporciona funciones de utilidad para el proceso de OAuth, adaptadas de la librería workers-oauth-provider.
  • wrangler.jsonc: El archivo de configuración para el Worker de Cloudflare.
  • package.json: Define los scripts y dependencias del proyecto.

¡Ahora tienes un servidor MCP remoto desplegado!

Control de acceso

Este servidor MCP usa OAuth de GitHub para la autenticación. Todos los usuarios autenticados de GitHub pueden acceder a herramientas básicas como "add" y "userInfoOctokit".

La herramienta "generateImage" está restringida a usuarios específicos de GitHub listados en la configuración ALLOWED_USERNAMES:

// Add GitHub usernames for image generation access
const ALLOWED_USERNAMES = new Set(["yourusername", "teammate1"]);

Accede al servidor MCP remoto desde Claude Desktop

Abre Claude Desktop y navega a Configuración -> Desarrollador -> Editar Config. Esto abre el archivo de configuración que controla a qué servidores MCP puede acceder Claude.

Reemplaza el contenido con la siguiente configuración. Una vez que reinicies Claude Desktop, se abrirá una ventana del navegador mostrando tu página de inicio de sesión OAuth. Completa el flujo de autenticación para otorgar a Claude acceso a tu servidor MCP. Después de otorgar el acceso, las herramientas estarán disponibles para que las uses.

{
  "mcpServers": {
    "math": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://mcp-github-oauth.<your-subdomain>.workers.dev/sse"
      ]
    }
  }
}

Una vez que las Herramientas (bajo 🔨) aparezcan en la interfaz, puedes pedirle a Claude que las use. Por ejemplo: "¿Podrías usar la herramienta de matemáticas para sumar 23 y 19?". Claude debería invocar la herramienta y mostrar el resultado generado por el servidor MCP.

Para desarrollo local

Si deseas iterar y probar tu servidor MCP, puedes hacerlo en desarrollo local. Esto requerirá que crees otra aplicación OAuth en GitHub:

  • Para la URL de la página de inicio, especifica http://localhost:18112
  • Para la URL de devolución de llamada de autorización, especifica http://localhost:18112/callback/github
  • Anota tu ID de cliente y genera un secreto de cliente.
  • Crea un archivo .dev.vars en la raíz de tu proyecto con:
GITHUB_CLIENT_ID=your_development_github_client_id
GITHUB_CLIENT_SECRET=your_development_github_client_secret

Desarrollo y prueba

Ejecuta el servidor localmente para que esté disponible en http://localhost:18112 wrangler dev

Para probar el servidor local, ingresa http://localhost:18112/sse en Inspector y presiona conectar. Una vez que sigas las indicaciones, podrás "Listar herramientas".

Uso de Claude y otros clientes MCP

Al usar Claude para conectarte a tu servidor MCP remoto, es posible que veas algunos mensajes de error. Esto se debe a que Claude Desktop aún no admite servidores MCP remotos, por lo que a veces se confunde. Para verificar si el servidor MCP está conectado, pasa el cursor sobre el ícono 🔨 en la esquina inferior derecha de la interfaz de Claude. Deberías ver tus herramientas disponibles allí.

Uso de Cursor y otros clientes MCP

Para conectar Cursor con tu servidor MCP, elige Type: "Comando" y en el campo Command, combina los campos de comando y argumentos en uno (por ejemplo, npx mcp-remote https://<your-worker-name>.<your-subdomain>.workers.dev/sse).

Ten en cuenta que, aunque Cursor admite servidores HTTP+SSE, no admite autenticación, por lo que aún necesitas usar mcp-remote (y usar un servidor STDIO, no uno HTTP).

Puedes conectar tu servidor MCP a otros clientes MCP como Windsurf abriendo el archivo de configuración del cliente, agregando el mismo JSON que se usó para la configuración de Claude y reiniciando el cliente MCP.

¿Cómo funciona?

Transporte y persistencia de MCP

Cloudflare Workers enrutan el tráfico de /sse y /mcp al servidor MCP independiente de Node.js configurado por MCP_SERVER_URL. El servicio Node no se conecta directamente a la base de datos. Persiste la identidad de la sesión de transporte, los metadatos de inicialización, la propiedad del inquilino, la caducidad y las marcas de tiempo de última vista a través de la API interna protegida del backend Go. El backend Go es el único propietario de la base de datos y almacena esos registros en PostgreSQL.

Las sesiones HTTP transmitibles se pueden reconstruir desde PostgreSQL después de un reinicio del proceso Node. Los flujos de respuesta en vivo y los sockets SSE heredados necesariamente permanecen locales al proceso; si un socket SSE se interrumpe, el cliente se reconecta y crea una nueva sesión persistida.

Establece MCP_SESSION_API_TOKEN al mismo valor en el backend Go y en el servicio MCP de Node. Si se omite, ambos servicios pueden usar el COOKIE_SECRET/SESSION_SECRET compartido existente. MCP_SESSION_TTL_SECONDS controla la duración de la sesión deslizante del servicio Node y tiene un valor predeterminado de 24 horas.

MCP Remote

La biblioteca MCP Remote permite que tu servidor exponga herramientas que pueden ser invocadas por clientes MCP como Inspector. Esta:

  • Define el protocolo de comunicación entre los clientes y tu servidor
  • Proporciona una forma estructurada de definir herramientas
  • Maneja la serialización y deserialización de solicitudes y respuestas
  • Mantiene la conexión de Eventos Enviados por el Servidor (SSE) entre los clientes y tu servidor

Footnotes

  1. Air es una utilidad de recarga en vivo de código abierto para aplicaciones Go que observa el código fuente, reconstruye y vuelve a ejecutar el binario compilado automáticamente para agilizar el desarrollo air-verse/air.