Servidor MCP (Model Context Protocol) para Azure DevOps que proporciona acceso completo a la API REST de Azure DevOps a través del protocolo MCP.
- Protocolo MCP completo: Soporte para JSON-RPC 2.0 sobre STDIO, HTTP real y modo híbrido STDIO+HTTP
- APIs de Azure DevOps: Acceso completo a Core, Work Items, Work Management, y más
- Containerizado: Imagen Docker lista para producción
- Múltiples modos de transporte:
stdio-http(default recomendado),stdio,httpreal y WebSocket (próximamente) - Configuración flexible: Variables de entorno y archivos de configuración
- Uploads grandes: Endpoint HTTP multipart para adjuntar archivos grandes a work items, compatible con MCP remoto y Docker/devcontainers
- WIQL robusto:
topreal, preservación deORDER BY, salida estructurada, identities compactas y URLs directas - HTML enriquecido seguro: Normalización de Description, Acceptance Criteria y comentarios; tablas con estilos automáticos compatibles con Azure DevOps
Este servidor pasó de exponer decenas de tools individuales (p.ej. azuredevops_wit_work_item_get, azuredevops_core_get_projects, etc.) a exponer un set compacto de 20 tools router.
Cada router tool recibe un parámetro obligatorio operation y enruta internamente hacia la implementación existente.
- Reducir superficie de exposición: un catálogo muy grande de tools aumenta el riesgo de exponer operaciones destructivas/administrativas por accidente.
- Mejor UX para el cliente MCP: menos tools, más fáciles de descubrir y documentar.
- Estandarización: unifica convenciones de entrada/salida (un solo punto por dominio) y deja el terreno preparado para políticas de enablement por
env vara nivel de operación.
- Breaking change: no se mantiene compatibilidad hacia atrás. Los nombres antiguos dejan de aparecer en
tools/list. - El servidor sigue conteniendo los tools “leaf” (internos), pero solo los routers se exponen por defecto.
- Java 21+
- Docker (para uso containerizado)
- Azure DevOps PAT (Personal Access Token)
- Organización de Azure DevOps
- Construir la imagen:
docker build -t mcp-azure-devops .- Crear archivo de variables de entorno:
# .env
AZURE_DEVOPS_ORGANIZATION=tu-organizacion
AZURE_DEVOPS_PAT=tu-personal-access-token- Ejecutar el contenedor:
# Modo recomendado: STDIO + HTTP real para MCP y uploads multipart
docker run --rm -i -p 9090:8080 --env-file .env mcp-azure-devops
# Modo HTTP puro (sin STDIO) para acceso remoto en puerto 8080
docker run -p 8080:8080 --env-file .env mcp-azure-devops http
# Modo STDIO puro (sin endpoint HTTP de uploads)
docker run --rm -i --env-file .env mcp-azure-devops stdioSi no se especifica modo, la imagen arranca en
stdio-http. Este modo mantiene el protocolo MCP por STDIO y expone HTTP real para/mcp,/mcp/healthy/mcp/uploads/....
- Configurar variables de entorno:
export AZURE_DEVOPS_ORGANIZATION=tu-organizacion
export AZURE_DEVOPS_PAT=tu-personal-access-token- Ejecutar con Gradle:
./gradlew bootRunNota (Dev Container / filesystem montado): en algunos entornos el directorio .gradle/ dentro del workspace puede causar errores de lock al finalizar el build. Si te ocurre, ejecuta builds usando caches fuera del workspace:
./gradlew --no-daemon -g /tmp/gradle-user-home --project-cache-dir /tmp/gradle-project-cache clean build{
"inputs": [
{
"id": "azure_devops_org",
"type": "promptString",
"description": "Azure DevOps organization name (e.g. 'contoso')"
},
{
"id": "azure_devops_pat",
"type": "promptString",
"description": "Azure DevOps Personal Access Token (PAT)"
}
],
"servers": {
"azure-devops-mcp": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"-p",
"9090:8080",
"--env",
"AZURE_DEVOPS_ORGANIZATION=${input:azure_devops_org}",
"--env",
"AZURE_DEVOPS_PAT=${input:azure_devops_pat}",
"mcp-azure-devops"
]
}
}
}En esta configuración el cliente MCP habla por STDIO, y los archivos grandes se suben por HTTP multipart usando el puerto publicado 9090. El agente debe construir la URL base con el puerto publicado del contenedor y el uploadPath devuelto por prepare_upload.
Este repositorio incluye una configuración local en .opencode/opencode.json que levanta el MCP mediante:
scripts/opencode-mcp-azure-devops.shEl script usa stdio-http y el puerto esperado 127.0.0.1:9091:
- Si
9091está libre, arranca Docker con-p 127.0.0.1:9091:8080. - Si
9091ya está ocupado, arranca el MCP igualmente por STDIO sin publicar-p. - En ambos casos anuncia
MCP_PUBLIC_BASE_URL=http://127.0.0.1:9091, asumiendo que el servicio que ocupa ese puerto es una instancia compatible de esta imagen. - No usa
--name, por lo que no hay conflicto de nombres entre proyectos.
Variables opcionales del script:
| Variable | Descripción | Default |
|---|---|---|
AZURE_DEVOPS_MCP_IMAGE |
Imagen Docker a ejecutar | mcp-azure-devops:latest |
AZURE_DEVOPS_MCP_ENV_FILE |
Archivo de variables para Docker | <repo>/.env |
AZURE_DEVOPS_MCP_HOST |
Host donde publicar HTTP si el puerto está libre | 127.0.0.1 |
AZURE_DEVOPS_MCP_HTTP_PORT |
Puerto HTTP esperado para uploads | 9091 |
AZURE_DEVOPS_MCP_GIT_PERSIST |
Si true, monta volumen/bind para persistir repos locales Git |
false |
AZURE_DEVOPS_MCP_GIT_VOLUME |
Nombre de volumen Docker para persistencia Git | mcp-azure-devops-git-workspace |
AZURE_DEVOPS_MCP_GIT_BIND |
Ruta host opcional para bind mount Git (si se define, tiene prioridad sobre volumen) | - |
AZURE_DEVOPS_MCP_GIT_ROOT |
Ruta interna del contenedor para workspace Git local | /tmp/mcp-git |
El servidor expone recursos MCP en Markdown para que clientes o agentes que soporten resources/list y resources/read puedan obtener plantillas de configuración para otros proyectos:
| URI | Propósito |
|---|---|
azuredevops-mcp://config/index |
Índice de recursos de configuración disponibles |
azuredevops-mcp://config/opencode |
Plantilla para .opencode/opencode.json |
azuredevops-mcp://config/vscode |
Plantilla para .vscode/mcp.json |
azuredevops-mcp://config/docker-script |
Script recomendado para opencode con Docker stdio-http |
Estos recursos son documentación de configuración. Las tools operativas, como azuredevops_wit_attachments con operation=prepare_upload, solo devuelven datos necesarios para ejecutar la operación actual.
{
"servers": {
"azure-devops-local": {
"command": "java",
"args": [
"-jar",
"/ruta/completa/build/libs/mcp-azure-devops.jar",
"--mcp.stdio=true"
],
"env": {
"AZURE_DEVOPS_ORGANIZATION": "tu-organizacion",
"AZURE_DEVOPS_PAT": "tu-pat"
}
}
}
}Esta configuración local usa STDIO puro. Si necesitas uploads grandes en desarrollo local, arranca también HTTP con
-Dspring.main.web-application-type=servlet -Dmcp.http=true -Dserver.port=8080o usa Docker en modostdio-http.
Si el cliente MCP soporta transporte HTTP, apunta al endpoint /mcp del servidor remoto:
{
"servers": {
"azure-devops-remote": {
"url": "https://servidor-remoto/mcp"
}
}
}Si tu cliente MCP solo soporta command/STDIO, usa Docker en modo stdio-http localmente para conservar STDIO y publicar el endpoint de uploads.
{
"inputs": [
{
"id": "dev_org",
"type": "promptString",
"description": "Dev organization"
},
{
"id": "prod_org",
"type": "promptString",
"description": "Production organization"
},
{
"id": "dev_pat",
"type": "promptString",
"description": "Development PAT"
},
{
"id": "prod_pat",
"type": "promptString",
"description": "Production PAT"
}
],
"servers": {
"azure-devops-dev": {
"command": "docker",
"args": [
"run", "--rm", "-i", "-p", "9091:8080",
"--env", "AZURE_DEVOPS_ORGANIZATION=${input:dev_org}",
"--env", "AZURE_DEVOPS_PAT=${input:dev_pat}",
"mcp-azure-devops"
]
},
"azure-devops-prod": {
"command": "docker",
"args": [
"run", "--rm", "-i", "-p", "9092:8080",
"--env", "AZURE_DEVOPS_ORGANIZATION=${input:prod_org}",
"--env", "AZURE_DEVOPS_PAT=${input:prod_pat}",
"mcp-azure-devops"
]
}
}
}Ideal para clientes MCP locales que usan STDIO y además necesitan subir archivos grandes por HTTP multipart.
docker run --rm -i -p 9090:8080 --env-file .env mcp-azure-devopsEquivale a:
docker run --rm -i -p 9090:8080 --env-file .env mcp-azure-devops stdio-httpEste modo habilita simultáneamente:
- STDIO para JSON-RPC MCP normal.
- HTTP real para
/mcp,/mcp/healthy/mcp/uploads/wit/workitems/{id}/attachment.
Ideal cuando no se necesitan uploads grandes. No expone endpoint HTTP.
docker run -i --env-file .env mcp-azure-devops stdioExpone el servidor MCP por HTTP real. No usa STDIO.
docker run -p 8080:8080 --env-file .env mcp-azure-devops httpdocker run -p 8081:8081 --env-file .env mcp-azure-devops websocketCuando el MCP corre en Docker o dentro de un devcontainer, el agente debe descubrir el host/puerto publicado para construir la URL multipart:
docker psBuscar algo como:
0.0.0.0:9090->8080/tcp
Entonces la base HTTP es:
http://localhost:9090
Y si prepare_upload devuelve:
/mcp/uploads/wit/workitems/123/attachment
La URL final es:
http://localhost:9090/mcp/uploads/wit/workitems/123/attachment
Con Docker Compose también puede usarse:
docker compose psEn opencode local, prepare_upload puede devolver directamente uploadUrl cuando la instancia fue arrancada por scripts/opencode-mcp-azure-devops.sh. Para ejemplos de agente, use un host/puerto descubierto del contenedor (no una loopback fija). El patron recomendado es:
http://IP_descubierta_docker:PUERTO_PUBLICADO/mcp/uploads/wit/workitems/{id}/attachment?project={project}
Si el script detecta que el puerto configurado ya estaba ocupado, no publica un nuevo puerto para evitar que Docker falle. En ese caso el agente debe usar uploadUrl si viene en respuesta; si no viene, debe descubrir host/puerto publicado con docker ps/docker compose ps y construir http://<host>:<puerto> + uploadPath.
El workspace Git local del MCP vive en MCP_GIT_WORKSPACE_ROOT (default /tmp/mcp-git).
- Sin volumen/bind montado: los clones viven mientras el contenedor exista (efímero).
- Con volumen/bind montado: los clones persisten entre reinicios.
Estructura administrada de carpetas para evitar desorden:
{MCP_GIT_WORKSPACE_ROOT}/{organization}/{project}/{repository}
Ejemplo con volumen Docker:
docker run --rm -i \
-p 9090:8080 \
-v mcp-azure-devops-git-workspace:/tmp/mcp-git \
--env MCP_GIT_WORKSPACE_ROOT=/tmp/mcp-git \
--env MCP_GIT_PERSISTENT=true \
--env-file .env \
mcp-azure-devops stdio-httpEjemplos con el script local de opencode:
# Persistencia con volumen Docker nombrado
AZURE_DEVOPS_MCP_GIT_PERSIST=true \
AZURE_DEVOPS_MCP_GIT_VOLUME=mcp-azure-devops-git-workspace \
./scripts/opencode-mcp-azure-devops.sh
# Persistencia con carpeta host (bind mount)
AZURE_DEVOPS_MCP_GIT_PERSIST=true \
AZURE_DEVOPS_MCP_GIT_BIND=/ruta/host/mcp-git \
./scripts/opencode-mcp-azure-devops.sh| Variable | Descripción | Requerido | Default |
|---|---|---|---|
AZURE_DEVOPS_ORGANIZATION |
Nombre de la organización Azure DevOps | ✅ | - |
AZURE_DEVOPS_PAT |
Personal Access Token | ✅ | - |
AZURE_DEVOPS_API_VERSION |
Versión de la API | ❌ | 7.2-preview.1 |
AZURE_DEVOPS_VSSPS_API_VERSION |
Versión API VSSPS | ❌ | 7.1 |
MCP_GIT_WORKSPACE_ROOT |
Root interno para clones Git locales del MCP | ❌ | /tmp/mcp-git |
MCP_GIT_PERSISTENT |
Indicador informativo de persistencia del workspace Git local | ❌ | false |
HTTP_PORT |
Puerto HTTP (modo http) | ❌ | 8080 |
WS_PORT |
Puerto WebSocket (modo websocket) | ❌ | 8081 |
El servidor expone 20 tools router. Cada una recibe operation y parámetros según la operación.
Resumen rápido de cantidad por dominio:
- 1 de identidad/perfil
- 4 de Core
- 1 de Work/Planning
- 4 de CI/CD (Pipelines, Environments, Approvals/Checks, Release)
- 6 de WIT
- 4 de Git
azuredevops_profile_identityoperation: get_my_memberid
azuredevops_core_projectsoperation: list | get | get_properties | create | update | delete | set_properties
azuredevops_core_teamsoperation: list | get | list_all | members | categorized | create | update | delete
azuredevops_core_processesoperation: list | get
azuredevops_core_avatarsoperation: get | set
azuredevops_work_planningoperation: get_boards | get_backlogs
azuredevops_wit_work_itemsoperation: get | create | update | delete | batch_get | bulk_delete | get_fields
azuredevops_wit_commentsoperation: list | add | update | delete | versions_list | versions_get | reactions_list | reactions_add | reactions_delete | reactions_engaged_users
azuredevops_wit_attachmentsoperation: get | delete | attach | prepare_upload | add_to_work_item
azuredevops_wit_classification_nodesoperation: get_root | get | get_by_ids | upsert | update | delete
azuredevops_wit_queriesoperation: wiql_query | wiql_by_id | search_queries | list_root_folders | recent_created_by_me | recent_changed_by_me | assigned_to_me
azuredevops_wit_reportingoperation: revisions_list | revisions_get | reporting_links_get | reporting_revisions_get | reporting_revisions_post
azuredevops_pipelinesoperation: list | get | create | runs_list | runs_get | run | logs_list | logs_get | preview | artifact_get
azuredevops_environmentsoperation: list | get | create | update | delete
azuredevops_approvals_checksoperation: check_configurations_list | check_configurations_add | approvals_get | approvals_update | pipeline_permissions_get | pipeline_permissions_update_resource | pipeline_permissions_update_resources | evaluations_get | evaluations_evaluate
azuredevops_releaseoperation: definitions_list | releases_list | releases_get | releases_create | approvals_list
azuredevops_git_apioperation: (catálogo dinámico de operaciones Git REST 7.2; cobertura total API-first)
azuredevops_git_repositoriesoperation: list | search | find | get_by_name | get | create | update | delete | items_get | items_get_safe | items_read_window | items_list | items_list_recursive | items_batch | search_files | find_files | search_content | explore_repo | commits_list | refs_list | refs_update | pushes_list | pushes_get | pushes_create | download_zip | repo_to_pipelines | pipeline_to_repo
azuredevops_git_pull_requestsoperation: get | list | list_by_project | assigned_to_me | create | update | reviewers_list | reviewer_add | reviewer_update | threads_list | thread_create | thread_update | comments_add | comment_update | comment_delete | statuses_list | status_add | labels_list | label_add | label_delete | iterations_list | iteration_changes_get | work_items_list | query | share
azuredevops_git_localoperation: workspace_info | workspace_list | clone_or_sync | status | checkout | create_branch | log | commit | push
Notas importantes para azuredevops_git_api:
- Diseñado para uso API-first y cobertura completa del área Git REST 7.2 (112 operaciones documentadas).
- Usa
operation+ path params +query/queryJson+bodyJsonpara evitar depender de clone local. - Soporta respuestas JSON/text/binario y guardado opcional con
outputPath. - Compatibilidad adicional para
operation=items_list: si llegarecursionLevelsinscopePath/path, aplicascopePath='/'; si la API remota aún exige scopePath válido, realiza fallback automático atrees_gety reportawarnings.
Notas importantes para azuredevops_git_repositories:
pushes_createpermite crear commits/cambios por API REST sin clonar repositorio local (bodyJson,commitsJson,changesJsono modo simplificado de un cambio).repo_to_pipelinesestá optimizado para velocidad: listaCI/*.yml|yamldel repositorio y cruza contra Build Definitions porname=<repositoryName>(includeAllProperties=true) para devolver los pipelines encontrados.pipeline_to_repodevuelve el repositorio padre usando Build Definition (repository.id/properties.safeRepository) y valida rápidamente queprocess.yamlFilenameexista en ese repo; si falta repo en definición, aplica fallback por nombre.listahora soporta alcance por proyecto u organización completa (siprojectse omite) y filtrosnameContains/nameSearchcon paginación local (skip/top) y metadatos (count,totalCount,hasMore).searchyfindejecutan búsqueda por patrón de nombre (nameContainsonameSearch) con alcance opcional cross-project.get_by_nameresuelve nombre exacto (case-insensitive). Si hay múltiples coincidencias, retorna error por ambigüedad con candidatos para desambiguar.- Operaciones de contenido aceptan
repositoryIdorepositoryNamepara evitar llamadas de resolución manual. - Versionado por familia de endpoint: repos/commits/refs usan
7.2-preview.2; items/trees/blobs/itemsbatch usan7.2-preview.1; pushes usan7.2-preview.3(con override opcional porapiVersion). items_list_recursiveintentaitems_listcon recursión y hace fallback atrees_getsi la API responde error/inconsistencia.items_listaplicascopePath='/'automáticamente cuando reciberecursionLevelsinscopePath/path; si aun así la API exige scopePath válido, realiza fallback automático aitems_list_recursivey retornawarnings.items_get_safeintentaitems_get includeContent=truey usa fallbackblobs/getcuando la API no devuelve contenido.items_read_windowhabilita lectura por líneas (offset/limit) sobre archivos de texto, con cache temporal en disco y preparación asíncrona (warming_up) para archivos grandes; nunca procesa binarios.search_files/find_filespermiten localizar archivos porfilePattern(glob),pathRegexy/oextensions.search_contentagrega búsqueda por texto/regex sobre archivos con límites conservadores por defecto (maxFiles=200,maxBytesPerFile=262144), configurables por parámetro con advertencias.explore_repodevuelve estructura resumida y archivos clave de integración/configuración en una sola operación.
Notas importantes para azuredevops_git_pull_requests:
list_by_projectconstatus=allejecuta consulta poractive,completedyabandoned, y devuelve respuesta combinada constatusQueryMode=all.- El default de
apiVersionahora se ajusta por operación: endpoints PR principales (get|list|list_by_project|assigned_to_me|create|update|statuses_list|status_add|iterations_list) usan7.2-preview.2; endpoints legacy de reviewers/threads/comments/labels/iteration_changes/work_items/query/share usan7.2-preview.1. - Se puede forzar una versión específica por llamada con
apiVersion.
Notas importantes para azuredevops_git_local:
- El workspace local Git siempre se mantiene dentro de
MCP_GIT_WORKSPACE_ROOT. - Este router es de última opción para agentes; priorizar
azuredevops_git_apiyazuredevops_git_repositories(REST-first) cuando sea posible. - Estructura administrada:
{root}/{organization}/{project}/{repository}. - Si hay colisión de nombre de repo con distinto
repositoryId, se agrega sufijo__{id8}automáticamente. - Cada repo guarda metadata en
.mcp-repo.jsonpara resolución consistente. - Requiere binario
giten runtime (incluido en las imágenes Docker publicadas por este repositorio).
Nota: existen tools internos (“leaf”) en el código, pero no se exponen en tools/list tras esta refactorización.
echo '{"jsonrpc":"2.0","method":"tools/call","id":1,"params":{"name":"azuredevops_core_projects","arguments":{"operation":"list"}}}' | docker run -i --env-file .env mcp-azure-devops stdioecho '{"jsonrpc":"2.0","method":"tools/call","id":2,"params":{"name":"azuredevops_wit_work_items","arguments":{"operation":"create","project":"MiProyecto","type":"User Story","title":"Nueva funcionalidad","description":"Implementar nueva característica"}}}' | docker run -i --env-file .env mcp-azure-devops stdioecho '{"jsonrpc":"2.0","method":"tools/call","id":3,"params":{"name":"azuredevops_wit_queries","arguments":{"operation":"wiql_query","project":"MiProyecto","query":"SELECT [System.Id], [System.Title] FROM workitems WHERE [System.WorkItemType] = '\''User Story'\'' AND [System.State] = '\''Active'\''"}}}' | docker run -i --env-file .env mcp-azure-devops stdioPara archivos pequeños puede usarse dataUrl o dataBase64 por MCP JSON-RPC. Para archivos reales o grandes, usa prepare_upload + HTTP multipart.
- Asegurar que el MCP esté corriendo en
stdio-httpcon puerto publicado. En un cliente MCP real, el cliente mantiene vivo el contenedor configurado enmcp.json.
Para una prueba manual sin cliente MCP, puedes iniciar HTTP real en background:
docker run --rm -d --name mcp-azure-devops-upload-test \
-p 9090:8080 \
--env-file .env \
mcp-azure-devops http- Preparar upload por MCP usando la tool
prepare_upload.
En un cliente MCP, llama:
{
"operation": "prepare_upload",
"workItemId": 123
}Si estás probando contra el servidor HTTP real:
curl -s -X POST http://localhost:9090/mcp \
-H "Content-Type: application/json" \
-H "MCP-Protocol-Version: 2025-06-18" \
-d '{"jsonrpc":"2.0","method":"tools/call","id":4,"params":{"name":"azuredevops_wit_attachments","arguments":{"operation":"prepare_upload","workItemId":123}}}'Respuesta esperada:
{
"uploadAvailable": true,
"method": "POST",
"uploadPath": "/mcp/uploads/wit/workitems/123/attachment",
"contentType": "multipart/form-data",
"fileField": "file"
}- Descubrir el puerto publicado del contenedor si aplica:
docker ps- Subir el archivo real por multipart:
curl -X POST \
-F "file=@/ruta/evidencia.zip" \
-F "comment=Evidencia" \
http://localhost:9090/mcp/uploads/wit/workitems/123/attachmentEl endpoint infiere project desde workItemId, infiere nombre/MIME, sube el archivo a Azure DevOps y crea la relación AttachedFile.
echo '{"jsonrpc":"2.0","method":"tools/call","id":4,"params":{"name":"azuredevops_wit_attachments","arguments":{"operation":"add_to_work_item","project":"MiProyecto","workItemId":123,"fileName":"evidencia.txt","dataBase64":"'"$(printf 'Hola mundo' | base64 -w0)"'","comment":"Evidencia de prueba"}}}' | docker run -i --env-file .env mcp-azure-devops stdio
filePathsolo funciona si el archivo existe dentro del entorno donde corre el MCP. En MCP remoto o Docker no debe usarse; envía el archivo por multipart.
wiql_query preserva el orden devuelto por Azure DevOps, incluyendo ORDER BY, y aplica top tanto en WIQL como fallback local.
echo '{"jsonrpc":"2.0","method":"tools/call","id":5,"params":{"name":"azuredevops_wit_queries","arguments":{"operation":"recent_created_by_me","project":"MiProyecto","top":10}}}' | docker run --rm -i --env-file .env mcp-azure-devops stdioOperaciones de alto nivel disponibles:
recent_created_by_me: work items más recientes creados por el usuario autenticado.recent_changed_by_me: work items más recientes modificados por el usuario autenticado.assigned_to_me: work items asignados al usuario autenticado.
Para consumo programático, usa raw=true:
{
"operation": "recent_created_by_me",
"project": "MiProyecto",
"top": 10,
"raw": true
}La respuesta incluye:
items[]estructuradocounttopAppliedidsInWiqlOrderfieldsUsed- identities compactas
- URLs directas a work items
Los campos HTML conocidos se normalizan automáticamente:
System.DescriptionMicrosoft.VSTS.Common.AcceptanceCriteriaMicrosoft.VSTS.TCM.ReproStepsMicrosoft.VSTS.TCM.SystemInfoMicrosoft.VSTS.Common.Resolution- comentarios WIT
Se soporta HTML simple como:
<p><strong>/<em><ul><li>/<ol><li><code>/<pre><blockquote><a href="..."><table>
Las tablas reciben estilos automáticos compatibles con Azure DevOps: borde gris, encabezado gris claro, padding y ancho completo. No es necesario que el agente agregue estilos manualmente.
El contenedor incluye un health check que verifica la disponibilidad del servicio:
docker ps # Muestra (healthy) cuando todo está funcionandoLos logs están optimizados para producción sin spam del health check:
docker logs nombre-contenedor
# Output: Starting MCP Server in STDIO + HTTP mode on port 8080...La imagen utiliza build multi-stage para optimización:
- Builder: Gradle + OpenJDK 21 (para construcción)
- Runtime: OpenJDK 21 JRE + herramientas (imagen final ~470MB)
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-azure-devops
spec:
replicas: 2
template:
spec:
containers:
- name: mcp-server
image: mcp-azure-devops:latest
args: ["http"]
env:
- name: AZURE_DEVOPS_ORGANIZATION
valueFrom:
secretKeyRef:
name: azure-devops-secrets
key: organization
- name: AZURE_DEVOPS_PAT
valueFrom:
secretKeyRef:
name: azure-devops-secrets
key: pat
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: mcp-azure-devops-service
spec:
selector:
app: mcp-azure-devops
ports:
- port: 80
targetPort: 8080version: '3.8'
services:
mcp-azure-devops:
build: .
command: ["stdio-http"]
stdin_open: true
ports:
- "9090:8080"
environment:
- AZURE_DEVOPS_ORGANIZATION=${AZURE_DEVOPS_ORGANIZATION}
- AZURE_DEVOPS_PAT=${AZURE_DEVOPS_PAT}
healthcheck:
test: ["CMD", "netstat", "-an", "|", "grep", ":8080.*LISTEN"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60ssrc/
├── main/java/com/mcp/server/
│ ├── tools/azuredevops/ # Tools (routers + leaf tools)
│ ├── services/ # Cliente HTTP unificado + helpers
│ └── transport/ # Capas de transporte
├── test/ # Tests unitarios
docker/ # Scripts Docker
scripts/curl/ # Scripts de validación cURL
api_doc/ # Documentación de APIs
Nota: el catálogo expuesto al cliente es el set de routers. Si agregas un tool nuevo “leaf”, normalmente también debes:
- Exponerlo como
operationdentro de un router existente (o crear un router nuevo si aplica). - Mantener la lista blanca de tools expuestos.
- Crear helper en
src/main/java/com/mcp/server/services/helpers/:
@Service
public class MyHelper {
private final AzureDevOpsClientService client;
public Map<String, Object> doSomething(String project) {
return client.getCoreApi(project, null, "myendpoint");
}
}- Crear tool en
src/main/java/com/mcp/server/tools/azuredevops/:
@Component
public class MyTool extends AbstractAzureDevOpsTool {
private final MyHelper helper;
@Override
public String getName() {
return "azuredevops_my_operation";
}
// Implementar métodos abstractos...
}- Crear script cURL en
scripts/curl/area/:
#!/bin/bash
source "$(dirname "$0")/../_env.sh"
# Validar endpoint usando curl_json
curl_json "${DEVOPS_BASE}/_apis/my/endpoint?api-version=${AZURE_DEVOPS_API_VERSION}"# Validar endpoint con cURL
./scripts/curl/area/my_endpoint.sh
# Rebuild de imagen Docker
./scripts/build-docker-image.sh --dockerfile Dockerfile --tag mcp-azure-devops:latest
# Smoke test de contenedor
./scripts/test-docker-image.sh
# Ejecutar en modo STDIO
docker run -i --env-file .env mcp-azure-devops:latest stdioEste proyecto está licenciado bajo la Licencia MIT - ver el archivo LICENSE para detalles.
Las contribuciones son bienvenidas. Por favor:
- Fork el proyecto
- Crea una branch para tu feature (
git checkout -b feature/AmazingFeature) - Commit tus cambios (
git commit -m 'Add some AmazingFeature') - Push a la branch (
git push origin feature/AmazingFeature) - Abre un Pull Request
Para más información sobre el protocolo MCP, visita Model Context Protocol Specification.