Skip to main content
Glama

mcp-server

MCP Server in TypeScript, der die Engineering-Tools des Teams – Bitbucket, Jira, Confluence und ArgoCD – für Editoren mit MCP-Unterstützung (VS Code + Copilot, Claude Code, etc.) bereitstellt.

Es ist eine dünne Schicht (Thin Wrapper): Es kommuniziert nicht direkt mit Bitbucket/Jira/Confluence/ArgoCD und speichert keine Anmeldedaten für diese Dienste. Alles wird in HTTP-Aufrufe gegen das interne Backend eng-api übersetzt, das bereits Verbindungen und Anmeldedaten verwaltet.

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

Vorteil: Kein Entwickler benötigt persönliche Tokens für Bitbucket/Jira/Confluence/ArgoCD. Nur ein API-Key dieses MCP Servers, der individuell widerrufbar ist.


1. Voraussetzungen

  • Node.js ≥ 22

  • Netzwerkzugriff auf ENG_API_BASE_URL (die URL von eng-api)

Related MCP server: Work Integrations MCP

2. Lokal ausführen

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

Andere Befehle:

Befehl

Beschreibung

npm run dev

Startet im Watch-Modus und liest .env

npm run build

Kompiliert TypeScript nach dist/

npm run typecheck

Type-Check ohne Ausgabe

npm start

Startet das Kompilierte (nutzt Umgebungsvariablen; läuft so im Pod)

npm run start:local

Startet das Kompilierte und liest .env

Schnelle Prüfung, ob der Server läuft:

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

3. Konfiguration (.env)

Alle Variablen werden aus process.env gelesen. Fehlt eine Pflichtvariable oder hat einen ungültigen Wert, startet der Prozess nicht und erklärt genau, was zu korrigieren ist.

Variable

Pflicht

Standard

Beschreibung

ENG_API_BASE_URL

Basis-URL von eng-api, ohne abschließenden Schrägstrich. Muss http(s)://… sein

MCP_DEV_API_KEYS

Gültige API-Keys der Entwickler für diesen MCP Server (siehe §4)

ENG_API_TIMEOUT_MS

10000

Timeout pro Aufruf von eng-api (1000–120000)

ENG_API_MAX_RETRIES

2

Zusätzliche Wiederholungsversuche bei 5xx/429/Timeout (0–5)

PORT

3000

HTTP-Port des MCP Servers

LOG_LEVEL

info

debug | info | warn | error

Beispiel für einen fehlgeschlagenen Start (absichtlich):

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. Authentifizierung: ein API-Key pro Entwickler

Diese Auth-Schicht ist eigenständig für den MCP Server und unabhängig von der, die eng-api gegenüber den Enddiensten verwendet.

Schlüssel generieren

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

Konfigurieren

MCP_DEV_API_KEYS akzeptiert vier Formate (mindestens 24 Zeichen pro Key, keine Duplikate):

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

Verwende das beschriftete Format: Die Bezeichnung erscheint in den Logs des MCP Servers und wird an eng-api im Header X-Mcp-Dev weitergegeben, sodass nachvollziehbar ist, wer welche Operation ausgelöst hat (z. B. ein argocd_sync_app), ohne den Key offenzulegen.

Verwendung

Der MCP-Client muss bei jeder Anfrage senden:

Authorization: Bearer <API_KEY>

(oder alternativ x-api-key: <API_KEY>). Der Vergleich erfolgt timing-sicher über SHA-256-Digests.

Situation

Antwort

Ohne Key

401 + Nachricht, welcher Header fehlt

Ungültiger/widerrufener Key

403 + Nachricht, was zu überprüfen ist

/healthz, /readyz

Keine Authentifizierung (für Kubernetes-Probes)

Jemanden widerrufen = seinen Key aus MCP_DEV_API_KEYS entfernen und das Deployment neu starten. Da jeder Entwickler seinen eigenen hat, hat dies keine Auswirkungen auf die anderen. In Produktion den Wert in einem Kubernetes-Secret speichern, niemals in einem ConfigMap.

5. Tool-Katalog

Die Namen haben einen Dienstpräfix und sind handlungsorientiert. Alle unterstützen Paginierung, wo zutreffend (page, pageSize von 1 bis 100, standardmäßig 25).

Bitbucket (schreibgeschützt)

Tool

Argumente

Eng-API-Endpunkt

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

Argumente

Eng-API-Endpunkt

jira_search_issues

jql? oder einfache Filter (projectKey?, status?, assignee?, labels?), fields?, page?, pageSize?

POST /jira/issues/search

jira_get_issue

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

GET /jira/issues/{key}

jira_create_issue ✍️

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

POST /jira/issues

Confluence (schreibgeschützt)

Tool

Argumente

Eng-API-Endpunkt

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

Argumente

Eng-API-Endpunkt

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 (exakt, ohne Standard), revision?, prune?, dryRun?, resources?

POST /argocd/applications/{name}/sync

Annotationen (Hinweise für den MCP-Client)

Tool

readOnlyHint

destructiveHint

idempotentHint

openWorldHint

Alle Lese-Tools

jira_create_issue ✍️

argocd_sync_app ⚠️

argocd_sync_app erfordert den exakten Namen der App (keine Platzhalter oder Standardwerte) und prune/dryRun stehen auf false, es sei denn, sie werden explizit angefordert.

Die Routen von eng-api befinden sich alle in src/client/routes.ts. Wenn eng-api einen Pfad ändert, wird nur diese Datei angepasst.

6. VS Code konfigurieren (jeder Entwickler mit seinem eigenen Key)

Erstelle .vscode/mcp.json in deinem Workspace (oder die benutzerspezifische mcp.json, wenn du sie für alle Projekte haben möchtest):

{
  "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 fragt beim ersten Mal nach dem Key und speichert ihn verschlüsselt; wird niemals committet. Öffne anschließend den Chat im Agent-Modus und du siehst die 11 Tools unter dem Server eng.

Für Claude Code (CLI) lautet das Äquivalent:

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

Lokal ersetze die URL durch http://localhost:3000/mcp.

7. Testen mit MCP Inspector

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

In der UI des Inspectors:

  1. Transport Type: Streamable HTTP

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

  3. Bei Authentication setze Header Name auf Authorization und den Bearer Token auf deinen API-Key

  4. Connect → Tab ToolsList Tools → teste eines aus

Man kann auch direkt mit curl testen (nützlich in CI oder von einem Pod aus):

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'

Ein Tool aufrufen:

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

Multi-Stage-Image basierend auf node:22-alpine: Das finale Image enthält nur dist/ + Produktionsabhängigkeiten, läuft als Benutzer node (ohne Root) und enthält einen HEALTHCHECK, der /healthz mit Node selbst aufruft (ohne curl/wget).

Für Kubernetes (die Manifests befinden sich nicht in diesem Repo):

  • Der Server ist zustandslos: Er speichert keine Sitzungen im Arbeitsspeicher, sodass er auf N Replikate ohne Sticky Sessions skaliert.

  • Probes: livenessProbeGET /healthz, readinessProbeGET /readyz (beide ohne Authentifizierung).

  • MCP_DEV_API_KEYS kommt in ein Secret; ENG_API_BASE_URL und die Timeouts können in ein ConfigMap.

  • Behandelt SIGTERM, indem es den HTTP-Server anmutig herunterfährt (maximal 10 s Drain-Zeit).

9. Hinzufügen eines neuen Dienstes oder Tools

Das Muster ist so ausgelegt, dass das Hinzufügen eines Dienstes nichts Bestehendes berührt. Beispiel mit einem hypothetischen Grafana:

1. Füge seine Routen in src/client/routes.ts hinzu:

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

2. Erstelle src/tools/grafana.ts nach dem gleichen Schema wie die anderen:

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. Registriere es in TOOL_REGISTRARS in src/server.ts:

const TOOL_REGISTRARS = [ …, registerGrafanaTools ];

Das war's. registerEngTool bietet dir kostenlos: Zod-Validierung, Formatierung der Antwort, Kürzung großer Payloads, Fehlererfassung und Übersetzung in handlungsorientierte Meldungen sowie Logging mit requestId.

Styleregeln für neue Tools:

  • Name servicio_accion_objeto, in Kleinbuchstaben.

  • Jedes Schema-Feld mit .describe() und einem konkreten Beispiel – das ist das Einzige, was das Modell liest, um zu entscheiden, wie es aufgerufen wird.

  • Ehrliche Annotationen: Wenn es schreibt, readOnlyHint: false; wenn es etwas löschen kann, destructiveHint: true.

  • Keine gefährlichen Standardwerte bei destruktiven Operationen: erfordere exakte Kennungen.

  • Paginierung (...paginationShape + paginationQuery(args)) bei allem, was Listen zurückgibt.

  • Erstelle niemals URLs manuell in tools/: immer über engApiRoutes.

10. Fehlerbehandlung

Kein Tool gibt einen nackten "Error 500" zurück. Jeder Fehler enthält was fehlgeschlagen ist, was zu überprüfen ist und eine requestId, um ihn mit den Logs von eng-api abzugleichen. Reales Beispiel:

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"}

Situation

Verhalten des MCP Servers

Timeout / Netzwerkfehler

Wiederholt mit exponentiellem Backoff + Jitter (ENG_API_MAX_RETRIES), erklärt dann, dass du ENG_API_BASE_URL / die Latenz überprüfen sollst

429, 5xx

Wiederholt (beachtet Retry-After falls vorhanden) und verweist bei anhaltendem Fehler auf die Logs von eng-api

400 / 422

Keine Wiederholung: die Parameter sind ungültig

401 / 403 von eng-api

Stellt klar, dass es nicht dein MCP-API-Key ist, sondern die Anmeldedaten/Berechtigungen von eng-api

404

Schlägt vor, die genauen Identifikatoren und die Pfade in routes.ts zu überprüfen

409

Statuskonflikt (z.B. ein bereits laufender ArgoCD-Sync): Status abfragen und später erneut versuchen

Nicht-JSON-Antwort

Meistens ein Proxy, der HTML zurückgibt: Der Pfad existiert wahrscheinlich nicht

Riesige Payload

Wird auf 120.000 Zeichen gekürzt mit einem Hinweis, pageSize zu reduzieren oder Filter einzuschränken

11. Projektstruktur

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

Designentscheidungen:

  • Streamable HTTP im stateless-Modus (sessionIdGenerator: undefined, enableJsonResponse: true): Pro Anfrage wird ein McpServer + Transport erstellt. Kein gemeinsamer Zustand zwischen Entwicklern, keine Sticky Sessions, horizontale Skalierung und die Antworten sind reines JSON (freundlicher zu Ingress/Proxies als SSE).

  • Nur POST /mcp: GET/DELETE antworten mit 405, da es im stateless-Modus keinen Server→Client-Stream und keine zu schließende Sitzung gibt.

  • Nachverfolgbarkeit: Jede Anfrage enthält eine X-Request-Id (die des Clients wird respektiert, falls gesendet) und einen X-Mcp-Dev-Header mit dem Entwickler-Tag, beide werden an eng-api weitergegeben.

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