Skip to main content
Glama

mcp-server

チームのエンジニアリングツール(Bitbucket、Jira、Confluence、ArgoCD)をMCP対応エディタ(VS Code + Copilot、Claude Codeなど)に公開するTypeScript製MCPサーバー。

これは薄いラッパーです。Bitbucket/Jira/Confluence/ArgoCDと直接通信したり、それらのサービスの認証情報を保存したりしません。すべてを内部バックエンドeng-apiへのHTTP呼び出しに変換します。eng-apiは既に接続と認証情報を解決しています。

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

利点: 開発者はBitbucket/Jira/Confluence/ArgoCDの個人トークンを必要としません。このMCPサーバーのAPIキーのみで、個別に失効可能です。


1. 要件

  • Node.js ≥ 22

  • ENG_API_BASE_URL(eng-apiのURL)へのネットワークアクセス

Related MCP server: Work Integrations MCP

2. ローカルでの実行

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

その他のコマンド:

コマンド

説明

npm run dev

.envを読み込んでウォッチモードで起動

npm run build

TypeScriptをdist/にコンパイル

npm run typecheck

出力なしで型チェック

npm start

コンパイル済みを起動(環境変数を使用。Podで実行されるもの)

npm run start:local

コンパイル済みを.envを読み込んで起動

動作確認のクイックチェック:

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

3. 設定 (.env)

すべての変数はprocess.envから読み取られます。必須の変数がないか無効な値の場合、プロセスは起動せず、修正すべき点を正確に説明します。

変数

必須

デフォルト

説明

ENG_API_BASE_URL

✅

—

eng-apiのベースURL。末尾にスラッシュなし。http(s)://…である必要あり

MCP_DEV_API_KEYS

✅

—

このMCPサーバーに対する開発者の有効なAPIキー(§4参照)

ENG_API_TIMEOUT_MS

—

10000

eng-api呼び出しのタイムアウト(1000–120000)

ENG_API_MAX_RETRIES

—

2

5xx/429/タイムアウト時の追加リトライ回数(0–5)

PORT

—

3000

MCPサーバーのHTTPポート

LOG_LEVEL

—

info

debug | info | warn | error

起動失敗の例(意図的):

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. 認証: 開発者ごとに1つのAPIキー

この認証層はMCPサーバー独自のものであり、eng-apiが最終サービスに対して使用するものとは独立しています。

キーの生成

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

設定

MCP_DEV_API_KEYSは4つの形式を受け入れます(キーは最低24文字、重複不可):

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

ラベル付き形式を使用してください。ラベルはMCPサーバーのログに表示され、X-Mcp-Devヘッダーとしてeng-apiに伝播されるため、キーを公開せずに誰が各操作(例:argocd_sync_app)を実行したかを監査できます。

使用

MCPクライアントは各リクエストで以下を送信する必要があります:

Authorization: Bearer <API_KEY>

(または代替としてx-api-key: <API_KEY>)。比較はSHA-256ダイジェストに対するタイミングセーフです。

状況

応答

キーなし

401 + 不足しているヘッダーを示すメッセージ

無効/失効キー

403 + 確認すべき点を示すメッセージ

/healthz, /readyz

認証なし(Kubernetesのプローブ用)

誰かを失効させる = MCP_DEV_API_KEYSからそのキーを削除し、Deploymentを再起動します。各開発者が自分のキーを持っているため、他の開発者には影響しません。本番環境では、値をKubernetesのSecretに保存し、ConfigMapには決して保存しないでください。

5. ツールカタログ

名前にはサービスのプレフィックスが付き、アクション指向です。該当するものはすべてページネーションをサポートします(page、pageSizeは1~100、デフォルトは25)。

Bitbucket(読み取り専用)

ツール

引数

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

ツール

引数

eng-apiエンドポイント

jira_search_issues

jql? または 単純フィルター(projectKey?, status?, assignee?, labels?)、fields?, page?, pageSize?

POST /jira/issues/search

jira_get_issue

issueKey(形式 PLAT-4821)、fields?, includeComments?

GET /jira/issues/{key}

jira_create_issue ✍️

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

POST /jira/issues

Confluence(読み取り専用)

ツール

引数

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

ツール

引数

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(正確、デフォルトなし)、revision?, prune?, dryRun?, resources?

POST /argocd/applications/{name}/sync

注釈(MCPクライアントへのヒント)

ツール

readOnlyHint

destructiveHint

idempotentHint

openWorldHint

読み取り系すべて

✅

❌

✅

✅

jira_create_issue ✍️

❌

❌

❌

✅

argocd_sync_app ⚠️

❌

✅

❌

✅

argocd_sync_appはアプリの正確な名前(ワイルドカードやデフォルト値なし)を要求し、prune/dryRunは明示的に要求されない限りfalseになります。

eng-apiのルートはすべてsrc/client/routes.tsにあります。eng-apiがパスを変更した場合、そのファイルのみを編集します。

6. VS Codeの設定(各開発者が自分のキーを使用)

ワークスペースに.vscode/mcp.jsonを作成します(またはすべてのプロジェクトで使用する場合はユーザーのmcp.json):

{
  "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は初回にキーを要求し、暗号化して保存します。決してコミットしないでください。 その後、Agentモードでチャットを開くと、engサーバーの下に11個のツールが表示されます。

Claude Code(CLI)の場合、同等のものは:

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

ローカルでは、URLをhttp://localhost:3000/mcpに置き換えてください。

7. MCP Inspectorでテスト

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

InspectorのUIで:

  1. Transport Type: Streamable HTTP

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

  3. Authenticationで、Header NameにAuthorization、Bearer TokenにAPIキーを設定

  4. Connect → Toolsタブ → List Tools → 任意のツールをテスト

curlで直接テストすることもできます(CIや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'

ツールの呼び出し:

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

node:22-alpine上のマルチステージイメージ: 最終イメージにはdist/ + 本番依存関係のみが含まれ、nodeユーザー(rootなし)として実行され、Node自身で/healthzを叩くHEALTHCHECKを含みます(curl/wgetなし)。

Kubernetes用(マニフェストはこのリポジトリにはありません):

  • サーバーはステートレス: セッションをメモリに保存しないため、スティッキーセッションなしでNレプリカにスケールします。

  • プローブ: livenessProbe → GET /healthz、readinessProbe → GET /readyz(どちらも認証なし)。

  • MCP_DEV_API_KEYSはSecretに、ENG_API_BASE_URLとタイムアウトはConfigMapに設定できます。

  • SIGTERMを処理し、HTTPサーバーをグレースフルにシャットダウンします(最大10秒のドレイン)。

9. 新しいサービスやツールの追加方法

パターンは、新しいサービスを追加しても既存のものに触れないように設計されています。架空のGrafanaの例:

1. ルートを追加 src/client/routes.tsに:

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

2. src/tools/grafana.tsを作成 他のものと同じテンプレートに従って:

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. 登録 TOOL_REGISTRARS in src/server.tsに:

const TOOL_REGISTRARS = [ …, registerGrafanaTools ];

以上です。registerEngToolはすでに以下を無料で提供します: Zod検証、レスポンスのフォーマット、巨大なペイロードの切り詰め、エラーキャプチャとアクション可能なメッセージへの変換、requestId付きのロギング。

新しいツールのスタイルルール:

  • 名前はサービス_アクション_オブジェクト、小文字。

  • スキーマの各フィールドには**.describe()と具体的な例**を付ける — モデルがそれを呼び出す方法を決定する唯一の情報です。

  • 正直な注釈: 書き込みならreadOnlyHint: false、何かを削除する可能性があるならdestructiveHint: true。

  • 破壊的な操作には危険なデフォルトなし: 正確な識別子を要求。

  • リストを返すものにはすべてページネーション(...paginationShape + paginationQuery(args))。

  • tools/内でURLを手動で構築しない: 常にengApiRoutesを経由。

10. エラー処理

どのツールも生の「Error 500」を返しません。各エラーには何が失敗したか、何を確認すべきか、およびeng-apiのログと照合するためのrequestIdが含まれます。実際の例:

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

状況

MCPサーバーの動作

タイムアウト / ネットワークエラー

指数バックオフ+ジッターで再試行し(ENG_API_MAX_RETRIES)、その後ENG_API_BASE_URL/レイテンシを確認するよう説明する

429, 5xx

再試行する(Retry-Afterがあれば尊重)、それでも続く場合はeng-apiのログを参照するよう促す

400 / 422

再試行しない: パラメータが無効

401 / 403 de eng-api

MCPのAPIキーではなく、eng-apiの認証情報/権限の問題であることを明確にする

404

正確な識別子とroutes.tsのルートを確認するよう提案する

409

状態の競合(例:ArgoCDの同期が進行中):状態を確認し、後で再試行する

非JSON応答

通常はプロキシがHTMLを返している: ルートが存在しない可能性が高い

巨大なペイロード

120,000文字に切り詰め、pageSizeを減らすかフィルタを絞るよう注意を促す

11. プロジェクト構造

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

設計上の決定:

  • ステートレスモードのStreamable HTTP (sessionIdGenerator: undefined, enableJsonResponse: true): リクエストごとにMcpServer+トランスポートが作成される。開発者間で状態を共有せず、スティッキーセッション不要で水平スケーリングが可能、レスポンスはプレーンJSON(SSEよりもイングレス/プロキシに優しい)。

  • POST /mcpのみ: GET/DELETEは405を返す。ステートレスではサーバー→クライアントのストリームや閉じるべきセッションがないため。

  • トレーサビリティ: 各リクエストはX-Request-Id(クライアントから送信された場合はそれを尊重)と、開発者のタグが付いたX-Mcp-Devを持ち、両方ともeng-apiに伝搬される。

Related MCP Connectors

Related MCP Servers