OTRS MCP Server
This server is an MCP integration layer that lets AI assistants and REST clients manage OTRS tickets through a standardized interface with authentication, admin tooling, and observability.
Create OTRS tickets with title, body, optional queue, priority, state, type, and customer user
Fetch ticket details, including optional dynamic fields and extended data
Search tickets by customer, queue, state, priority, title, with sorting, ordering, and limits
Update existing tickets: change title, queue, priority, state, customer user, or owner
Retrieve full ticket history
Access tickets as MCP resources (e.g., otrs://ticket/{id}, otrs://search/tickets)
Administer API keys, admin users, audit logs, login audit, and usage metrics via REST admin endpoints
Monitor activity and metrics through the REST API and an administrative React dashboard
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@OTRS MCP Servercreate a new ticket in the Raw queue titled 'Printer not working' with description 'HP LaserJet in office 3 is offline' and priority '3 normal'"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
OTRS MCP Server
Servidor Model Context Protocol (MCP) para integracao com o OTRS (Open Ticket Request System).
Permite que assistentes de IA (como Claude Desktop, VS Code, agentes Python) criem, consultem, busquem e atualizem tickets no OTRS por meio de uma interface padronizada. Inclui API REST autenticada, painel administrativo React com dashboard de metricas, observabilidade via OpenTelemetry e deploy em producao com HTTPS, systemd, backup automatico e protecao contra ataques.
Sumario
Related MCP server: tickiti-mcp
Arquitetura
┌──────────────────────────┐ ┌──────────────────────────┐
│ Agente IA │ │ Navegador Admin │
│ (Claude Desktop, Python) │ │ https://seu-dominio │
└──────────┬───────────────┘ └──────────┬───────────────┘
│ HTTPS + API Key │ HTTPS + JWT
▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ Nginx (SSL via Certbot/Let's Encrypt) │
│ + Fail2ban (protecao contra brute-force) │
│ Porta 443 — Reverse Proxy │
└──────┬────────────────────┬────────────────────┬─────────────┘
│ /mcp │ /api/* │ /
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ MCP Server │ │ API REST │ │ Frontend │
│ (FastMCP) │ │ (FastAPI) │ │ (React) │
│ 127.0.0.1 │ │ 127.0.0.1 │ │ 127.0.0.1 │
│ :8001 │ │ :3000 │ │ :8080 │
└─────┬──────┘ └──────┬─────┘ └────────────┘
│ │
└──────────┬──────────┘
▼
┌──────────────────────┐ ┌──────────────────────┐
│ SQLite (WAL) │ │ OTel Collector │
│ /data/otrs-mcp.db │ │ → Tempo / Mimir │
└──────────┬───────────┘ └───────────────────────┘
▼
┌──────────────────────┐
│ Servidor OTRS │
│ (Generic Interface) │
└──────────────────────┘Servicos Docker
Servico | Tecnologia | Porta | CPU/Mem | Descricao |
| Python / FastAPI | 127.0.0.1:3000 | 1 CPU / 512M | Backend REST + auth + SQLite |
| Python / FastMCP | 127.0.0.1:8001 | 1 CPU / 512M | MCP Streamable HTTP |
| React / Nginx Alpine | 127.0.0.1:8080 | 0.5 CPU / 128M | Dashboard administrativo (SPA) |
| OTel Contrib | 127.0.0.1:4317-4318 | 0.5 CPU / 256M | Coleta traces e envia para Tempo/Mimir |
Funcionalidades
MCP Server
Criar, buscar, visualizar e atualizar tickets no OTRS
Acessar historico completo de tickets
Transporte Streamable HTTP (remoto) e stdio (local)
Retry automatico com backoff exponencial (3 tentativas)
Gerenciamento automatico de sessoes OTRS com asyncio.Lock
Seguranca
Autenticacao por API key (
sk-otrs-...) com SHA-256 hashingAutenticacao JWT (HS256, claims iat/jti/exp) para painel administrativo
Token refresh automatico (renova 10 min antes de expirar)
Rate limiting por API key (configuravel por token)
Protecao brute-force no login (5 falhas em 15min = lockout, persistido no SQLite)
Fail2ban no Nginx (bloqueia IPs com muitas falhas via iptables)
Security headers (CSP, X-Frame-Options DENY, X-Content-Type-Options, Referrer-Policy, Permissions-Policy)
CORS restrito (allow_headers limitado a Authorization + Content-Type)
Validacao de entrada centralizada (ticket_id regex, Pydantic com min/max em todos os campos)
Erros OTRS sanitizados (detalhes internos nao expostos ao cliente)
HTTPS via Nginx + Certbot (Let's Encrypt)
Containers Docker non-root, imagens pinadas, portas 127.0.0.1 only
Limites de CPU/memoria por container
Painel Administrativo
Dashboard com graficos de atividade (barras por dia, ultimos 14 dias)
Distribuicao de uso por tool e ranking de top agents
Metricas: success rate, chamadas 24h, tokens ativos, logins falhados
Alertas de seguranca (logins falhados, tokens expirando, tokens expirados, tokens nunca usados)
Gerenciamento de API keys (criar, revogar, filtros, rate limit, indicadores de expiracao)
Gerenciamento de usuarios administradores (com confirmacao de exclusao)
Audit Log completo (todas as operacoes de ticket registram agent + api_key, filtros, export CSV/JSON)
Login Audit (tentativas de login com IP, user agent, export CSV/JSON)
Client MCP Wizard (configuracoes prontas para Claude Desktop, VS Code, Python, cURL)
Pagina de configuracoes e status de conexao OTRS
Observabilidade
Auto-instrumentacao Python zero-code (FastAPI, httpx, SQLite3, logging) via
opentelemetry-instrumentInstrumentacao frontend (fetch, document load) via
@opentelemetry/sdk-trace-webOTel Collector sidecar no Docker Compose para enviar traces para Tempo/Mimir
Ativavel/desativavel via variaveis de ambiente (sem overhead quando desabilitado)
Infraestrutura de Producao
Systemd service (boot automatico, restart on failure)
Script de deploy (git pull + build + healthcheck + limpeza de imagens)
Backup automatico do SQLite (diario, 7 dias de retencao, compressao gzip)
Log rotation para Docker
Health check externo com webhook de alerta (Slack/Discord/Teams)
Suporte a subpath para dominio compartilhado entre multiplos MCPs
Pre-requisitos
Docker e Docker Compose instalados no servidor
Nginx instalado no servidor host (para reverse proxy HTTPS)
Certbot instalado (para certificado SSL Let's Encrypt)
Dominio apontando para o IP do servidor (registro A no DNS)
Servidor OTRS com Generic Interface configurada
Configuracao do OTRS
Acesse Administracao -> Web Services no OTRS
Crie/verifique um webservice com estas operacoes:
SessionCreate,TicketCreate,TicketGet,TicketSearch,TicketUpdate,TicketHistoryGet
Anote a URL:
https://seu-otrs/otrs/nph-genericinterface.pl/Webservice/NomeDoWebserviceGaranta que o usuario tem permissoes para tickets e Generic Interface
Deploy
1. Clonar e configurar
git clone https://github.com/eduardoantoniojunior/otrs-mcp-server.git
cd otrs-mcp-server
cp .env.example .envEdite o .env:
# OTRS (obrigatorio)
OTRS_BASE_URL=https://seu-otrs/otrs/nph-genericinterface.pl/Webservice/MCPConnector
OTRS_USERNAME=seu-usuario
OTRS_PASSWORD=sua-senha
# Seguranca (obrigatorio em producao)
OTRS_ENV=production
OTRS_JWT_SECRET=gere-com-python-c-import-secrets-print-secrets-token-hex-32
OTRS_ADMIN_USER=admin
OTRS_ADMIN_PASSWORD=MUDE_ESTA_SENHA
# CORS (ajuste para seu dominio)
OTRS_CORS_ORIGINS=https://seu-dominio2. Subir os containers
docker compose up -d --buildVerifique:
docker compose ps
curl -s http://127.0.0.1:3000/api/health
curl -s http://127.0.0.1:8080 | head -53. Configurar Nginx (HTTPS)
Edite nginx/mcp.conf e substitua SEU_DOMINIO pelo seu dominio real. Depois:
sudo cp nginx/mcp.conf /etc/nginx/sites-available/mcp.conf
sudo ln -s /etc/nginx/sites-available/mcp.conf /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d seu-dominio4. Verificar
curl https://seu-dominio/api/healthURLs de acesso
Servico | URL |
Painel Administrativo |
|
API REST |
|
MCP Endpoint |
|
Deploy com Subpath
Quando varios MCPs compartilham o mesmo dominio, cada um pode ficar num subpath diferente.
Exemplo: https://mcp.dominio.com/otrs/
Como ativar
No
.env, defina o subpath:
VITE_BASE_PATH=/otrs/Rebuild o frontend (o subpath e aplicado no build):
docker compose build frontend
docker compose up -dNo
nginx/mcp.conf:Comente todo o MODO 1 (dominio dedicado)
Descomente todo o MODO 2 (subpath)
Substitua
/otrspelo subpath desejado
Recarregue o Nginx:
sudo cp nginx/mcp.conf /etc/nginx/sites-available/mcp.conf
sudo nginx -t && sudo systemctl reload nginxURLs com subpath
Servico | URL |
Painel Administrativo |
|
API REST |
|
MCP Endpoint |
|
Como desativar (voltar para dominio dedicado)
Remova
VITE_BASE_PATHdo.env(ou defina como/)Rebuild:
docker compose build frontend && docker compose up -dNo
nginx/mcp.conf, comente MODO 2 e descomente MODO 1
Producao
5. Instalar como servico systemd
sudo cp deploy/otrs-mcp.service /etc/systemd/system/otrs-mcp.service
sudo systemctl daemon-reload
sudo systemctl enable otrs-mcp
sudo systemctl start otrs-mcpComandos:
sudo systemctl status otrs-mcp # Status
sudo systemctl restart otrs-mcp # Restart
sudo journalctl -u otrs-mcp -f # Logs tempo real
sudo journalctl -u otrs-mcp --since "1h" # Logs recentes6. Configurar Fail2ban
sudo apt install fail2ban
sudo cp deploy/fail2ban/jail.local /etc/fail2ban/jail.local
sudo cp deploy/fail2ban/filter.d/* /etc/fail2ban/filter.d/
sudo systemctl restart fail2ban
sudo systemctl enable fail2banJails configuradas:
Jail | Trigger | Ban |
| 5 falhas de login em 5 min | 15 min |
| 20 erros 401/403 em 1 min | 10 min |
| 10 scans (wp-admin, .env, .git) em 5 min | 1 hora |
Verificar:
sudo fail2ban-client status # Listar jails
sudo fail2ban-client status otrs-mcp-login # IPs banidos
sudo fail2ban-client set otrs-mcp-login unbanip 1.2.3.4 # Desbanir7. Configurar backup automatico
chmod +x deploy/deploy.sh deploy/backup.sh deploy/healthcheck.sh
# Backup diario as 3h
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/otrs-mcp-server/deploy/backup.sh >> /var/log/otrs-mcp-backup.log 2>&1") | crontab -
# Health check a cada 5 minutos
(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/otrs-mcp-server/deploy/healthcheck.sh") | crontab -Backup: SQLite consistente via sqlite3.backup(), compressao gzip, retencao 7 dias.
8. Configurar log rotation
sudo cp deploy/otrs-mcp.logrotate /etc/logrotate.d/otrs-mcpOu globalmente no Docker (/etc/docker/daemon.json):
{
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}9. Health check com alertas (opcional)
Configure HEALTHCHECK_WEBHOOK_URL no .env para receber alertas via webhook quando um servico cair:
HEALTHCHECK_WEBHOOK_URL=https://hooks.slack.com/services/XXX/YYY/ZZZO script deploy/healthcheck.sh verifica API, MCP e Frontend a cada 5 min, envia alerta na primeira falha e notifica recuperacao.
Script de deploy
Para atualizacoes futuras:
./deploy/deploy.sh --pull # Git pull + build + restart + healthcheck
./deploy/deploy.sh # Apenas rebuild + restartConfiguracao
Variaveis de Ambiente
OTRS (obrigatorio)
Variavel | Descricao |
| URL completa do webservice OTRS |
| Usuario do OTRS |
| Senha do OTRS |
OTRS (opcional)
Variavel | Padrao | Descricao |
|
| Verificar certificados SSL |
|
| Timeout HTTP em segundos |
|
| Logging de debug |
|
| Fila padrao para tickets |
|
| Estado padrao |
|
| Prioridade padrao |
| `` | Tipo padrao |
| (derivado) | URL da interface web OTRS |
| `` | Filas validas (dropdown no painel, separadas por virgula) |
| `` | Tipos validos (dropdown no painel, separados por virgula) |
Autenticacao
Variavel | Padrao | Descricao |
|
| Ambiente ( |
| (gerado) | Secret para assinatura JWT (min 32 chars) |
|
| Tempo de vida do JWT (8 horas) |
|
| Usuario admin padrao |
| -- | Senha do admin padrao (obrigatorio) |
MCP Server
Variavel | Padrao | Descricao |
|
| Transporte: |
|
| Host do MCP server (modo http) |
|
| Porta do MCP server (modo http) |
Banco de Dados
Variavel | Padrao | Descricao |
|
| Caminho do SQLite |
|
| Log de atividade MCP |
|
| Maximo de eventos no JSON |
CORS
Variavel | Padrao | Descricao |
|
| Origens permitidas (separadas por virgula) |
Frontend
Variavel | Padrao | Descricao |
|
| Subpath do deploy (ex: |
OpenTelemetry (opcional)
Variavel | Padrao | Descricao |
| -- | URL OTLP HTTP do Tempo/Mimir (ex: |
| -- | URL publica do collector para browser traces (ex: |
API Keys
API keys autenticam agentes externos (Claude Desktop, bou-vigilante, scripts).
Criar via painel:
Login em
https://seu-dominioIr em MCP Tokens
Clicar em Create Token
Definir nome, agent, permissoes (
read/write), rate limit e expiracaoCopiar a chave gerada (exibida apenas uma vez)
Formato: sk-otrs-{64 caracteres hex}
Rate Limit: Configuravel por token (requests/minuto). Use 0 para ilimitado (recomendado para agentes automatizados).
Uso do MCP Server
Claude Desktop (Streamable HTTP remoto)
{
"mcpServers": {
"otrs": {
"url": "https://seu-dominio/mcp",
"headers": {
"Authorization": "Bearer sk-otrs-sua-api-key-aqui"
}
}
}
}Se usando subpath:
{
"mcpServers": {
"otrs": {
"url": "https://mcp.dominio.com/otrs/mcp",
"headers": {
"Authorization": "Bearer sk-otrs-sua-api-key-aqui"
}
}
}
}VS Code / Kiro
{
"servers": {
"otrs": {
"type": "http",
"url": "https://seu-dominio/mcp",
"headers": {
"Authorization": "Bearer sk-otrs-sua-api-key-aqui"
}
}
}
}Python SDK
from mcp.client.streamable_http import streamablehttp_client
from mcp.client.session import ClientSession
async def main():
headers = {"Authorization": "Bearer sk-otrs-sua-api-key"}
async with streamablehttp_client(
"https://seu-dominio/mcp", headers=headers
) as (r, w, _):
async with ClientSession(r, w) as session:
await session.initialize()
tools = await session.list_tools()
result = await session.call_tool(
"search_tickets",
arguments={"state": "new", "limit": 5}
)stdio (local, sem rede)
{
"mcpServers": {
"otrs": {
"command": "uv",
"args": ["run", "python", "-m", "otrs_mcp.main"],
"cwd": "/caminho/para/otrs-mcp-server",
"env": {
"OTRS_BASE_URL": "https://seu-otrs/otrs/nph-genericinterface.pl/Webservice/MCPConnector",
"OTRS_USERNAME": "usuario",
"OTRS_PASSWORD": "senha",
"OTRS_MCP_TRANSPORT": "stdio"
}
}
}
}Referencia da API REST
Autenticacao
Todos os endpoints (exceto /api/health) requerem:
Authorization: Bearer <api-key-ou-jwt>Endpoints de tickets: API key ou JWT
Endpoints admin (
/api/admin/*): apenas JWT
Endpoints
Publico
Metodo | Rota | Descricao |
|
| Health check |
Tickets (API key ou JWT)
Metodo | Rota | Permissao | Descricao |
|
| read | Buscar tickets (filtros: queue, state, priority, title, customer_user, customer_id) |
|
| read | Detalhes do ticket |
|
| write | Criar ticket |
|
| write | Atualizar ticket |
|
| read | Historico do ticket |
Atividade (API key ou JWT)
Metodo | Rota | Permissao | Descricao |
|
| read | Log de atividade |
|
| read | Resumo de metricas |
|
| write | Limpar atividade |
Configuracao (API key ou JWT)
Metodo | Rota | Descricao |
|
| Filas e tipos validos |
Administracao (apenas JWT)
Metodo | Rota | Descricao |
|
| Login (retorna JWT) |
|
| Renovar JWT (token refresh) |
|
| Dados do admin logado |
|
| Criar admin |
|
| Listar admins |
|
| Remover admin |
|
| Criar API key |
|
| Listar API keys |
|
| Revogar key |
|
| Remover key |
|
| Atividade detalhada dos agentes |
|
| Log de tentativas de login |
|
| Metricas diarias (graficos dashboard) |
Referencia das Tools MCP
Tool | Descricao | Parametros |
| Criar ticket |
|
| Detalhes do ticket |
|
| Buscar tickets |
|
| Atualizar ticket |
|
| Historico |
|
Referencia dos Resources MCP
URI | Descricao |
| Dados do ticket em JSON |
| Historico do ticket |
| 20 tickets mais recentes |
Painel Administrativo
Pagina | Funcionalidade |
Dashboard | Graficos de atividade (barras 14 dias), distribuicao por tool, ranking top agents, metricas 24h, success rate, alertas de seguranca |
MCP Tokens | CRUD de API keys com filtros (busca, permissao, status), rate limit, indicadores de expiracao/never used, confirmacao detalhada |
Admin Users | Gerenciamento de administradores com confirmacao de exclusao |
Client MCP Wizard | Configuracoes prontas para Claude Desktop, VS Code, cURL, Python SDK |
Audit Log | Log completo de todas as operacoes (agent, api_key, ticket_id, duracao), filtros e export CSV/JSON |
Login Audit | Tentativas de login (sucesso/falha, IP, user agent), stats, export CSV/JSON |
Settings | Status de conexao OTRS, filas e tipos configurados, versao do servidor |
Seguranca
Camadas de protecao
Camada | Implementacao |
Rede | Portas Docker em 127.0.0.1 only, Nginx com HTTPS (Certbot), |
Firewall | Fail2ban com 3 jails: login brute-force, API abuse, bot/scanner detection |
Autenticacao | JWT (HS256 + iat/jti) para admin, API keys (SHA-256) para agentes, dual auth nos endpoints |
Brute-force | 5 falhas em 15min = lockout por IP e username, persistido no SQLite |
Rate limiting | Por API key, configuravel (requests/minuto) |
Headers HTTP | CSP, X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy, Permissions-Policy |
CORS | Origins configuravel, allow_headers restrito a Authorization + Content-Type |
Validacao | ticket_id regex centralizado, Pydantic com min/max em todos os campos, erros OTRS sanitizados |
Docker | Non-root (user otrs), multi-stage build, imagens pinadas (python:3.12.8, nginx:1.27, uv:0.5) |
Recursos | Limites CPU/memoria por container, request body size limit (1MB no Nginx) |
Auditoria | Todas as operacoes de ticket registram agent + api_key no SQLite, login audit com IP e user agent |
Frontend | JWT expirado validado no bootstrap, token refresh automatico, ErrorBoundary, cache limpo no logout, AbortController timeout |
Observabilidade (OpenTelemetry)
O projeto inclui instrumentacao completa via OpenTelemetry para traces e metricas.
Arquitetura
Backend (api/mcp) ──[gRPC:4317]──→ OTel Collector ──[OTLP HTTP]──→ Tempo/Mimir
Frontend (browser) ──[HTTP:4318]──→ OTel Collector ──[OTLP HTTP]──→ Tempo/MimirO que e instrumentado
Componente | Instrumentacao | Tipo |
API REST | FastAPI, httpx, SQLite3, logging | Zero-code ( |
MCP Server | httpx, logging | Zero-code ( |
Frontend | fetch (API calls), document load | SDK ( |
Habilitar
No
.env, defina o endpoint do seu Tempo:
OTEL_TEMPO_ENDPOINT=http://172.31.x.x:4318
VITE_OTEL_ENDPOINT=https://seu-dominio/otelRebuild e restart:
docker compose down
docker compose up -d --buildCopiar o nginx atualizado (tem o proxy
/otel/para o collector):
sudo cp nginx/mcp.conf /etc/nginx/sites-available/mcp.conf
sudo nginx -t && sudo systemctl reload nginxVerificar no Grafana Explore (Tempo):
TraceQL: { resource.service.name = "otrs-mcp-api" }Desabilitar
Se OTEL_TEMPO_ENDPOINT nao estiver definido, o collector opera sem destino.
Se VITE_OTEL_ENDPOINT nao estiver definido, o frontend nao envia traces.
O opentelemetry-instrument opera em modo noop quando nao ha exporter configurado (sem overhead).
Estrutura do Projeto
otrs-mcp-server/
├── src/otrs_mcp/ # Pacote Python principal
│ ├── __init__.py # API publica (v0.2.0)
│ ├── main.py # Entry point MCP (stdio/http)
│ ├── config.py # Configuracao (pydantic-settings)
│ ├── client.py # Cliente HTTP OTRS com retry + asyncio.Lock
│ ├── tools.py # 5 Tools MCP com validacao
│ ├── resources.py # 3 Resources MCP
│ ├── api.py # Backend REST (FastAPI) com audit trail
│ ├── auth.py # JWT (iat/jti) + API key + rate limiting
│ ├── database.py # SQLite WAL (schema + CRUD + metricas diarias)
│ ├── validation.py # Validacao centralizada (ticket_id)
│ ├── activity.py # Monitoramento de atividade (JSON)
│ ├── constants.py # Prioridades e estados validos
│ ├── exceptions.py # Excecoes customizadas
│ └── routes/
│ └── admin.py # Login (brute-force SQLite), refresh, keys, users, audit, metrics
├── frontend/ # React 19 + TypeScript + TailwindCSS
│ ├── src/
│ │ ├── pages/ # Login, ApiKeys, AuditLog, LoginAudit, Settings, ClientWizard
│ │ ├── components/ # Dashboard (graficos CSS), Layout
│ │ ├── contexts/ # AuthContext (JWT refresh automatico)
│ │ ├── hooks/ # useTickets, useHealth, etc.
│ │ ├── services/api.ts # HTTP client (timeout, logout centralizado, subpath-aware)
│ │ ├── telemetry.ts # OpenTelemetry Web SDK (fetch + document load)
│ │ └── types/ # TypeScript types
│ ├── Dockerfile # Node 20 build + Nginx 1.27 Alpine serve
│ ├── nginx.conf # Security headers (CSP, X-Frame-Options, etc.)
│ ├── vite.config.ts # Vite config (base path via VITE_BASE_PATH)
│ └── package.json # React 19, Vite 6, TanStack Query 5, OTel Web SDK
├── nginx/
│ └── mcp.conf # Nginx vhost (MODO 1: raiz, MODO 2: subpath comentado)
├── otel/
│ └── otel-collector.yaml # Config OTel Collector (OTLP → Tempo/Mimir)
├── deploy/
│ ├── otrs-mcp.service # Systemd service unit
│ ├── deploy.sh # Script deploy (pull + build + healthcheck)
│ ├── backup.sh # Backup SQLite (sqlite3.backup, gzip, 7 dias)
│ ├── healthcheck.sh # Health check externo com webhook de alerta
│ ├── otrs-mcp.logrotate # Log rotation para Docker
│ └── fail2ban/
│ ├── jail.local # Config fail2ban (3 jails)
│ └── filter.d/
│ ├── otrs-mcp-login.conf # Filtro login brute-force
│ ├── otrs-mcp-api.conf # Filtro API abuse
│ └── nginx-botsearch.conf # Filtro bot/scanner
├── tests/
│ ├── unit/ # 41 testes unitarios (pytest)
│ └── integration/ # Testes de integracao
├── docker-compose.yml # 4 servicos (api, mcp, frontend, otel-collector)
├── Dockerfile # MCP server (python:3.12.8, non-root, OTel zero-code)
├── Dockerfile.api # API REST (python:3.12.8, non-root, OTel zero-code)
├── pyproject.toml # Dependencias, build, CLI scripts
├── .env.example # Template de variaveis
└── AGENTS.md # Guia para agentes de IADesenvolvimento
Setup local
git clone https://github.com/eduardoantoniojunior/otrs-mcp-server.git
cd otrs-mcp-server
# Python (backend)
uv sync --extra dev
# Frontend
cd frontend && npm ciTestes
# Unitarios (41 testes)
uv run pytest tests/unit/ -v
# Com cobertura
uv run pytest tests/unit/ --cov=src/otrs_mcp --cov-report=term-missingFormatacao e lint
uv run black src/
uv run isort src/
uv run mypy src/Rodar local (desenvolvimento)
# API REST (porta 3000)
uv run uvicorn otrs_mcp.api:app --port 3000 --reload
# MCP Server (porta 8001)
OTRS_MCP_TRANSPORT=http uv run python -m otrs_mcp.main
# Frontend (porta 5173, hot reload)
cd frontend && npm run devCLI entry points
otrs-mcp-server # Inicia o MCP server
otrs-mcp-api # Inicia a API RESTSolucao de Problemas
Problema | Solucao |
SSL error ao conectar no OTRS | Defina |
HTTP 301 redirect | Use URL HTTPS completa no |
Auth 401 na API | Verifique API key (ativa? expirada? permissoes?) |
Rate limit 429 | Aumente o rate limit do token ou use |
Login bloqueado (429) | Brute-force lockout. Espere 15 min ou verifique no Login Audit |
MCP connection refused | Verifique se o container |
Frontend 404 no F5 | Verifique que o Nginx faz proxy para a porta 8080 |
Certificado SSL expirado | Execute |
IP banido pelo fail2ban |
|
Container sem memoria | Ajuste limites em |
Traces nao aparecem no Grafana | Verifique |
Frontend com subpath errado | Verifique |
Logs
docker compose logs -f # Todos os containers
docker compose logs -f api # API REST
docker compose logs -f mcp-server # MCP Server
docker compose logs -f frontend # Frontend
docker compose logs -f otel-collector # OpenTelemetry Collector
sudo journalctl -u otrs-mcp -f # Systemd service
sudo tail -f /var/log/nginx/error.log # Nginx
sudo fail2ban-client status otrs-mcp-login # Fail2banLicenca
Apache-2.0
Available Tools
5 toolscreate_ticketC
Create a new ticket in OTRS
| Name | Required | Description | Default |
|---|---|---|---|
| body | Yes | ||
| queue | No | ||
| state | No | ||
| title | Yes | ||
| priority | No | ||
| ticket_type | No | ||
| customer_user | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden of disclosing side effects. It only names the action without noting that a persistent OTRS record is created, whether calls are idempotent, whether authentication is required, or what errors may occur.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single 7-word sentence with zero filler and the verb front-loaded. It is appropriately brief, though it borders on under-specification rather than genuine explanatory economy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
A 7-parameter tool with no annotations and no parameter descriptions needs far more than a purpose statement; the description omits the required title/body contract and how optional fields are validated. The presence of an output schema covers return values, which keeps this from a 1, but an agent still lacks essential calling context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% across 7 parameters, so the description must compensate, but it names no parameters at all. An agent is left with bare parameter names (body, queue, state, priority, ticket_type, customer_user) plus title, with no format, allowable-value, or domain guidance.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb ('Create') and resource ('a new ticket in OTRS'), and the word 'new' cleanly separates it from the update_ticket sibling. It is accurate and unambiguous, though it adds only the 'in OTRS' context beyond what the tool name already communicates.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance on when to use this tool versus get_ticket, search_tickets, or update_ticket. No prerequisites, ordering constraints, or when-not-to-use conditions are mentioned; usage context must be entirely inferred from the tool name.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_ticketB
Get ticket details from OTRS
| Name | Required | Description | Default |
|---|---|---|---|
| ticket_id | Yes | ||
| include_extended_data | No | ||
| include_dynamic_fields | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the burden of indicating safety; the word 'Get' clearly conveys a read-only retrieval with no mutation, which is useful. However, it adds no detail about access requirements, behavior of the include flags, or potential response size, though some of this is covered by the output schema.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single sentence, front-loaded with the verb and object, with no filler. It is appropriately sized for a simple retrieval tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
The output schema covers return shape, so that burden is lifted. Still, with no annotations and sibling tools present, the description doesn't clarify when to choose this tool or how extended data and dynamic fields affect the result, leaving clear gaps in context.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the description adds no parameter-specific meaning beyond the raw schema titles. The required ticket_id is inferable from tool name, but include_extended_data and include_dynamic_fields are not explained, making this less than adequate.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly identifies a get operation on ticket details from OTRS, which is the correct resource and verb. It does not explicitly contrast with siblings such as get_ticket_history or search_tickets, so it stops short of the top score.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is given about when to use this tool over search_tickets, get_ticket_history, or other siblings. The meaning is inferable but not stated; an agent is not told that this is for fetching a single known ticket by ID.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_ticket_historyC
Get ticket history from OTRS
| Name | Required | Description | Default |
|---|---|---|---|
| ticket_id | Yes |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are present, so the description carries the full burden. It indicates a read operation but does not disclose what 'history' includes, whether it returns change logs versus messages, what auth or prerequisites are needed, or how results are ordered/limited.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single efficient sentence with no filler. It could carry more useful detail, but it is well-structured and front-loaded for a simple tool.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With only one parameter and an output schema available, the basic invocation path is reasonably clear. However, the description lacks enough detail about what constitutes ticket history and under what circumstances to choose this over get_ticket, leaving some ambiguity.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0%, and the description does not explain the ticket_id parameter beyond its name and string type. The parameter name is fairly self-explanatory, but the description adds no semantic value to compensate for the missing schema documentation.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a clear verb ('Get'), a specific resource ('ticket history'), and the system ('OTRS'). It is distinguishable from siblings like get_ticket by name and resource, though it does not explicitly contrast itself with them.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
There is no guidance on when to use this tool versus get_ticket or search_tickets. The intended context is implied by the name and description, but no exclusions, alternatives, or conditions are provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
search_ticketsC
Search for tickets in OTRS
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | ||
| queue | No | ||
| state | No | ||
| title | No | ||
| sort_by | No | Age | |
| order_by | No | Down | |
| priority | No | ||
| customer_id | No | ||
| customer_user | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations exist, so the description carries the full disclosure burden, yet it reveals nothing about match semantics, how filters combine, the default sort (Age/Down), or limit/pagination behavior. The output schema helps with return shape but not with behavioral traits an agent needs to predict the tool's effect.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The single sentence is lean and front-loaded with the core action, but for a tool with 9 optional parameters, this brevity is under-specification rather than efficient structure. There are no wasted words, yet no additional context is packed into the available space.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given moderate complexity (9 optional filters, no annotations, 0% schema coverage), the description is far too thin to support correct invocation. An agent would not know how filters behave, what values queue/state accept, or how sorting works; the output schema covers only what is returned, not how to call the tool properly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% across 9 parameters, and the description does nothing to compensate. It never mentions the filter parameters, does not explain valid values for sort_by/order_by, and does not clarify the distinction between customer_id and customer_user, leaving the agent to guess parameter meaning.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Search') and resource ('tickets in OTRS'), clearly identifying the tool's core action. The sibling tools use distinct verbs (create, get, update, get_history), so search is implicitly differentiated as the query-multiple-tickets operation, though no explicit contrast is given.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
No guidance is provided on when to use this tool versus alternatives. An agent must infer that get_ticket retrieves a single ticket while search_tickets finds tickets by criteria, but nothing states this, and no exclusions or alternative recommendations are offered.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
update_ticketC
Update an existing ticket in OTRS
| Name | Required | Description | Default |
|---|---|---|---|
| owner | No | ||
| queue | No | ||
| state | No | ||
| title | No | ||
| priority | No | ||
| ticket_id | Yes | ||
| customer_user | No |
Output Schema
| Name | Required | Description |
|---|---|---|
No output parameters | ||
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations provided, the description must disclose behavioral details on its own. It communicates that the tool mutates an existing ticket, but it does not explain whether updates are partial, what fields are affected, what authorization is needed, or how invalid ticket IDs are handled.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single front-loaded sentence with no filler or redundancy. It is concise, though its brevity contributes to the under-specification penalized in other dimensions.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 7 parameters and no annotation or schema descriptions, one short sentence is insufficient. The description covers the core operation but omits field-level update semantics, optionality behavior, and error conditions; the presence of an output schema only accounts for return values, not input behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 0% and the description adds no parameter information. The 7 parameters, including optional nullable fields like state, priority, and owner, are left entirely to the agent to infer from their titles, so the description fails to compensate for the schema's lack of explanations.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific action and resource: 'Update an existing ticket in OTRS'. It clearly indicates this tool modifies an already-created ticket, which distinguishes it from create_ticket and the read-only siblings, but it does not enumerate the updatable fields or explicitly name alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is only implied: use this tool when an existing OTRS ticket needs to be modified. There is no explicit guidance on when to prefer it over create_ticket or get_ticket, and no conditions, prerequisites, or exclusions are provided.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.2.0- First observed
create_ticket - First observed
get_ticket - First observed
get_ticket_history - First observed
search_tickets - First observed
update_ticket
TDQS
Scored across 5 tools
Each tool targets a distinct operation on tickets: create, retrieve individual details, search, update, and retrieve history. There is no meaningful overlap between get_ticket and get_ticket_history because one returns current state and the other returns the audit trail.
All tools follow a consistent verb_noun pattern with snake_case: create_ticket, get_ticket, search_tickets, update_ticket, get_ticket_history. The only minor variation is the plural 'tickets' in search_tickets, which is natural and does not break the pattern.
Five tools is well-scoped for a ticket management server. Each tool represents a core operation without unnecessary redundancy, making the set easy to navigate.
The tool surface covers the essential ticket lifecycle: creation, retrieval, search, update, and history lookup. For OTRS, this is a complete and practical set with no obvious dead ends.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
MCP server enabling AI agents to manage Bitrix24 features via standardized protocol
AI-native helpdesk hosted in Germany: tickets, replies, KPIs and knowledge base over MCP.
MCP server unifying ERPs, CRMs, APIs and knowledge base for Claude, ChatGPT and Gemini.
- mttrlyOAuthcom.mttrly
AI-powered incident management and server monitoring via MCP.
Related MCP Servers
- AlicenseCqualityCmaintenanceAn MCP server that enables AI assistants to interact with JIRA, allowing for querying issue details, creating and updating work items, and managing attachments through a standardized interface.124MIT

tickiti-mcpofficial
AlicenseBqualityBmaintenanceAn MCP server that exposes the Tickiti helpdesk API to AI assistants, enabling ticket management and helpdesk operations via natural language.11MIT- AlicenseAqualityDmaintenanceMCP server for Otobo ITSM enabling AI assistants to search, create, update, and manage tickets via the Generic Interface REST API.1081MIT
- AlicenseNot gradedqualityBmaintenanceAn MCP server that connects AI assistants to Zammad, providing tools for managing tickets, users, organizations, and attachments.38AGPL 3.0