Skip to main content
Glama

mcp-server

MCP Server en TypeScript que expone las herramientas de ingeniería del equipo —Bitbucket, Jira, Confluence y ArgoCD— a los editores con soporte MCP (VS Code + Copilot, Claude Code, etc.).

Es una capa delgada (thin wrapper): no habla con Bitbucket/Jira/Confluence/ArgoCD, ni guarda credenciales de esos servicios. Todo lo traduce a llamadas HTTP contra el backend interno eng-api, que ya tiene resueltas conexiones y credenciales.

VS Code (dev A) ─┐
VS Code (dev B) ─┼─► MCP Server  ───► eng-api ───► Bitbucket / Jira / Confluence / ArgoCD
VS Code (dev C) ─┘   (este repo)      (credenciales viven aquí)
                     Streamable HTTP      HTTP
                     + API key por dev

Ventaja: ningún dev necesita tokens personales de Bitbucket/Jira/Confluence/ArgoCD. Solo una API key de este MCP Server, revocable individualmente.


1. Requisitos

  • Node.js ≥ 22

  • Acceso de red a ENG_API_BASE_URL (la URL de eng-api)

Related MCP server: Work Integrations MCP

2. Correr localmente

npm ci
cp .env.example .env      # y rellena los valores (ver sección 3)
npm run dev               # hot-reload, lee .env automáticamente

Otros comandos:

Comando

Qué hace

npm run dev

Arranca en modo watch leyendo .env

npm run build

Compila TypeScript a dist/

npm run typecheck

Type-check sin emitir

npm start

Arranca lo compilado (usa variables del entorno; es lo que corre en el pod)

npm run start:local

Arranca lo compilado leyendo .env

Comprobación rápida de que está vivo:

curl http://localhost:3000/healthz
# {"status":"ok","server":"mcp-server","version":"0.1.0"}

3. Configuración (.env)

Todas las variables se leen de process.env. Si falta una obligatoria o tiene un valor inválido, el proceso no arranca y explica exactamente qué corregir.

Variable

Obligatoria

Default

Descripción

ENG_API_BASE_URL

URL base de eng-api, sin barra final. Debe ser http(s)://…

MCP_DEV_API_KEYS

API keys válidas de los devs contra este MCP Server (ver §4)

ENG_API_TIMEOUT_MS

10000

Timeout por llamada a eng-api (1000–120000)

ENG_API_MAX_RETRIES

2

Reintentos adicionales ante 5xx/429/timeout (0–5)

PORT

3000

Puerto HTTP del MCP Server

LOG_LEVEL

info

debug | info | warn | error

Ejemplo de arranque fallido (a propósito):

Configuración inválida: el MCP Server no puede arrancar.
  - Falta la variable obligatoria ENG_API_BASE_URL. Debe apuntar a la URL base de eng-api, ej. https://eng-api.internal.example/api/v1
Revisa tu archivo .env (usa .env.example como plantilla) o el ConfigMap/Secret del Deployment.

4. Autenticación: una API key por dev

Esta capa de auth es propia del MCP Server e independiente de la que eng-api use hacia los servicios finales.

Generar keys

openssl rand -hex 32     # una por cada persona del equipo

Configurarlas

MCP_DEV_API_KEYS acepta cuatro formatos (mínimo 24 caracteres por key, sin duplicados):

MCP_DEV_API_KEYS=<key1>,<key2>                          # CSV simple
MCP_DEV_API_KEYS=alice:<key1>,bob:<key2>                # CSV etiquetado ← recomendado
MCP_DEV_API_KEYS=["<key1>","<key2>"]                    # JSON array
MCP_DEV_API_KEYS={"alice":"<key1>","bob":"<key2>"}      # JSON objeto

Usa el formato etiquetado: la etiqueta aparece en los logs del MCP Server y se propaga a eng-api en el header X-Mcp-Dev, así que se puede auditar quién disparó cada operación (por ejemplo, un argocd_sync_app) sin exponer la key.

Usarlas

El cliente MCP debe enviar en cada petición:

Authorization: Bearer <API_KEY>

(o, como alternativa, x-api-key: <API_KEY>). La comparación es timing-safe sobre digests SHA-256.

Situación

Respuesta

Sin key

401 + mensaje indicando qué header falta

Key inválida/revocada

403 + mensaje indicando qué revisar

/healthz, /readyz

Sin auth (para los probes de Kubernetes)

Revocar a alguien = quitar su key de MCP_DEV_API_KEYS y reiniciar el Deployment. Como cada dev tiene la suya, no afecta al resto. En producción, guarda el valor en un Secret de Kubernetes, nunca en un ConfigMap.

5. Catálogo de tools

Los nombres llevan prefijo del servicio y son orientados a acción. Todos soportan paginación donde aplica (page, pageSize de 1 a 100, por defecto 25).

Bitbucket (solo lectura)

Tool

Argumentos

Endpoint eng-api

bitbucket_list_prs

workspace, repoSlug, state? (OPEN|MERGED|DECLINED|ALL), author?, page?, pageSize?

GET /bitbucket/repositories/{ws}/{repo}/pull-requests

bitbucket_get_pr

workspace, repoSlug, pullRequestId

GET /bitbucket/repositories/{ws}/{repo}/pull-requests/{id}

bitbucket_get_commits

workspace, repoSlug, branch, sinceCommit?, sinceDate?, page?, pageSize?

GET /bitbucket/repositories/{ws}/{repo}/commits

Jira

Tool

Argumentos

Endpoint eng-api

jira_search_issues

jql? o filtros simples (projectKey?, status?, assignee?, labels?), fields?, page?, pageSize?

POST /jira/issues/search

jira_get_issue

issueKey (formato PLAT-4821), fields?, includeComments?

GET /jira/issues/{key}

jira_create_issue ✍️

projectKey, issueType, summary, description?, assignee?, labels?, priority?, parentKey?, extraFields?

POST /jira/issues

Confluence (solo lectura)

Tool

Argumentos

Endpoint eng-api

confluence_search_pages

query, spaceKey?, page?, pageSize?

GET /confluence/pages/search

confluence_get_page

pageId, format? (plain|storage|view)

GET /confluence/pages/{id}

ArgoCD

Tool

Argumentos

Endpoint eng-api

argocd_list_apps

project?, namespace?, syncStatus?, healthStatus?, page?, pageSize?

GET /argocd/applications

argocd_get_app_status

appName

GET /argocd/applications/{name}

argocd_sync_app ⚠️

appName (exacto, sin default), revision?, prune?, dryRun?, resources?

POST /argocd/applications/{name}/sync

Anotaciones (hints para el cliente MCP)

Tool

readOnlyHint

destructiveHint

idempotentHint

openWorldHint

Todos los de lectura

jira_create_issue ✍️

argocd_sync_app ⚠️

argocd_sync_app exige el nombre exacto de la app (sin comodines ni valores por defecto) y prune/dryRun van en false salvo que se pidan explícitamente.

Las rutas de eng-api viven todas en src/client/routes.ts. Si eng-api cambia un path, se toca solo ese archivo.

6. Configurar VS Code (cada dev, con su propia key)

Crea .vscode/mcp.json en tu workspace (o el mcp.json de usuario, si lo quieres en todos los proyectos):

{
  "inputs": [
    {
      "type": "promptString",
      "id": "eng-mcp-api-key",
      "description": "Tu API key personal del MCP Server de ingeniería",
      "password": true
    }
  ],
  "servers": {
    "eng": {
      "type": "http",
      "url": "https://<host-del-mcp-server>/mcp",
      "headers": {
        "Authorization": "Bearer ${input:eng-mcp-api-key}"
      }
    }
  }
}

VS Code pedirá la key la primera vez y la guardará cifrada; no se commitea nunca. Después, abre el chat en modo Agent y verás los 11 tools bajo el servidor eng.

Para Claude Code (CLI), el equivalente es:

claude mcp add --transport http eng https://<host-del-mcp-server>/mcp \
  --header "Authorization: Bearer <TU_API_KEY>"

En local, sustituye la URL por http://localhost:3000/mcp.

7. Probarlo con MCP Inspector

npm run build && npm run start:local     # en una terminal
npx @modelcontextprotocol/inspector      # en otra

En la UI del Inspector:

  1. Transport Type: Streamable HTTP

  2. URL: http://localhost:3000/mcp

  3. En Authentication, pon Header Name Authorization y el Bearer Token con tu API key

  4. Connect → pestaña ToolsList Tools → prueba cualquiera

También se puede probar con curl directamente (útil en CI o desde un pod):

KEY=<tu-api-key>
curl -s -X POST http://localhost:3000/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $KEY" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq '.result.tools[].name'

Llamar a un tool:

curl -s -X POST http://localhost:3000/mcp \
  -H "Accept: application/json, text/event-stream" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $KEY" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{
        "name":"bitbucket_list_prs",
        "arguments":{"workspace":"acme","repoSlug":"web-frontend","state":"OPEN","pageSize":10}}}'

8. Docker

docker build -t mcp-server:0.1.0 .

docker run --rm -p 3000:3000 \
  -e ENG_API_BASE_URL="https://<eng-api>/api/v1" \
  -e MCP_DEV_API_KEYS="alice:<key1>,bob:<key2>" \
  mcp-server:0.1.0

Imagen multi-stage sobre node:22-alpine: la final solo lleva dist/ + dependencias de producción, corre como usuario node (sin root) e incluye un HEALTHCHECK que golpea /healthz con el propio Node (sin curl/wget).

Para Kubernetes (los manifiestos no están en este repo):

  • El servidor es stateless: no guarda sesiones en memoria, así que escala a N réplicas sin sticky sessions.

  • Probes: livenessProbeGET /healthz, readinessProbeGET /readyz (ambos sin auth).

  • MCP_DEV_API_KEYS va en un Secret; ENG_API_BASE_URL y los timeouts pueden ir en un ConfigMap.

  • Maneja SIGTERM cerrando el servidor HTTP con gracia (drenaje de 10 s como máximo).

9. Cómo añadir un servicio o tool nuevo

El patrón está pensado para que añadir un servicio no toque nada existente. Ejemplo con un hipotético Grafana:

1. Añade sus rutas en src/client/routes.ts:

grafana: {
  listDashboards: (): string => "/grafana/dashboards",
  getDashboard: (uid: string): string => `/grafana/dashboards/${seg(uid)}`,
},

2. Crea src/tools/grafana.ts siguiendo el mismo molde que los demás:

export function registerGrafanaTools(server: McpServer, deps: ToolDeps): void {
  registerEngTool(server, deps, {
    name: "grafana_list_dashboards",              // prefijo de servicio + acción
    title: "Grafana: listar dashboards",
    description: "Qué hace y cuándo usarlo.",
    inputSchema: { query: z.string().optional().describe('Texto a buscar. Ejemplo: "latencia checkout".'),
                   ...paginationShape },
    annotations: readOnlyAnnotations("Grafana: listar dashboards"),
    describeOperation: (args) => `listar dashboards de Grafana`,   // encaja tras "al …"
    execute: (args, { client, context }) =>
      client.get(engApiRoutes.grafana.listDashboards(), {
        query: { query: args.query, ...paginationQuery(args) },
        context,
      }),
  });
}

3. Regístralo en TOOL_REGISTRARS de src/server.ts:

const TOOL_REGISTRARS = [ …, registerGrafanaTools ];

Eso es todo. registerEngTool ya te da gratis: validación Zod, formateo de la respuesta, truncado de payloads enormes, captura de errores y traducción a mensajes accionables, y logging con requestId.

Reglas de estilo para tools nuevos:

  • Nombre servicio_accion_objeto, en minúsculas.

  • Cada campo del schema con .describe() y un ejemplo concreto — es lo único que el modelo lee para decidir cómo llamarlo.

  • Anotaciones honestas: si escribe, readOnlyHint: false; si puede borrar algo, destructiveHint: true.

  • Nada de defaults peligrosos en operaciones destructivas: exige identificadores exactos.

  • Paginación (...paginationShape + paginationQuery(args)) en todo lo que devuelva listas.

  • Nunca construyas URLs a mano en tools/: siempre a través de engApiRoutes.

10. Manejo de errores

Ningún tool devuelve un "Error 500" pelado. Cada error incluye qué falló, qué revisar y un requestId para cruzarlo con los logs de eng-api. Ejemplo real:

No existe el recurso al obtener el estado de la aplicación boom (404). Verifica los identificadores
exactos (workspace/repo, key de issue, id de página, nombre de app) — distinguen mayúsculas. Si los
identificadores son correctos, la ruta de eng-api puede haber cambiado (src/client/routes.ts).
[requestId=8a4bf9e6-…, intentos=1, upstream=GET /argocd/applications/boom]
Respuesta de eng-api: {"error":"application not found"}

Situación

Qué hace el MCP Server

Timeout / error de red

Reintenta con backoff exponencial + jitter (ENG_API_MAX_RETRIES), luego explica que revises ENG_API_BASE_URL / la latencia

429, 5xx

Reintenta (respeta Retry-After si viene) y, si persiste, apunta a los logs de eng-api

400 / 422

No reintenta: los parámetros son inválidos

401 / 403 de eng-api

Aclara que no es tu API key del MCP, sino las credenciales/permisos de eng-api

404

Sugiere verificar identificadores exactos y las rutas de routes.ts

409

Conflicto de estado (p. ej. un sync de ArgoCD ya en curso): consulta el estado y reintenta luego

Respuesta no-JSON

Suele ser un proxy devolviendo HTML: la ruta probablemente no existe

Payload gigante

Se trunca a 120 000 caracteres con un aviso para reducir pageSize o acotar filtros

11. Estructura del proyecto

src/
├── index.ts                 # entrypoint: Express + Streamable HTTP (stateless), /healthz, /readyz
├── config.ts                # lectura y validación de env vars, fail-fast
├── auth.ts                  # middleware de API key (timing-safe)
├── logger.ts                # logs JSON de una línea, aptos para Cloud Logging
├── server.ts                # createMcpServer(): registra todas las familias de tools
├── client/
│   ├── routes.ts            # ÚNICO sitio con las rutas de eng-api
│   ├── errors.ts            # EngApiError → mensajes accionables
│   └── engApiClient.ts      # fetch + timeout + retry con backoff
└── tools/
    ├── shared.ts            # registerEngTool(), paginación, formateo, errores
    ├── bitbucket.ts  ├── jira.ts  ├── confluence.ts  └── argocd.ts

Decisiones de diseño:

  • Streamable HTTP en modo stateless (sessionIdGenerator: undefined, enableJsonResponse: true): se crea un McpServer + transport por petición. Sin estado compartido entre devs, sin sticky sessions, escala horizontalmente y las respuestas son JSON plano (más amables con ingress/proxies que SSE).

  • Solo POST /mcp: GET/DELETE responden 405, porque en stateless no hay stream servidor→cliente ni sesión que cerrar.

  • Trazabilidad: cada petición lleva un X-Request-Id (se respeta el del cliente si lo manda) y un X-Mcp-Dev con la etiqueta del dev, ambos propagados a eng-api.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • A MCP server built for developers enabling Git based project management with project and personal…

  • MCP server for interacting with the Supabase platform

  • An MCP server that let you interact with Cycloid.io Internal Development Portal and Platform

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ElJijuna/mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server