Skip to main content
Glama
OTRS MCP Server

OTRS MCP Server

OTRS MCP Server

Servidor Model Context Protocol (MCP) para integração com o OTRS (Open Ticket Request System).

Permite que assistentes de IA (Claude Desktop, VS Code, Kiro, agentes Python) criem, consultem, busquem e atualizem tickets no OTRS por uma interface padronizada. Acompanha uma API REST autenticada, painel administrativo em React, auditoria em SQLite e instrumentação OpenTelemetry.

Versão: 0.2.0 · Python: 3.12+ · Licença: Apache-2.0


Sumário


Related MCP server: tickiti-mcp

O que o servidor oferece

Três formas de consumir o mesmo backend OTRS:

Interface

Para quem

Autenticação

MCP (streamable-http)

Agentes de IA e clientes MCP remotos

API key obrigatória no header Authorization

MCP (stdio)

Cliente MCP local que sobe o processo

Nenhuma (sem rede; credenciais vêm do ambiente)

API REST (FastAPI)

Scripts, integrações, o próprio frontend

API key ou JWT

Painel web (React)

Administradores humanos

JWT (login com usuário e senha)

Operações OTRS cobertas: SessionCreate, TicketCreate, TicketGet (com ou sem AllArticles), TicketSearch, TicketUpdate, TicketHistoryGet.


Arquitetura

┌────────────────────────┐      ┌────────────────────────┐
│  Agente IA / cliente   │      │  Navegador (admin)     │
│  MCP                   │      │                        │
└───────────┬────────────┘      └───────────┬────────────┘
            │ Bearer sk-otrs-...            │ Bearer JWT
            ▼                               ▼
┌──────────────────────────────────────────────────────────┐
│        Nginx no host (nginx/mcp.conf) + Certbot          │
│  /otrs/mcp → 8001   /otrs/api/ → 3000   /otrs/ → SPA     │
└──────┬──────────────────┬──────────────────┬─────────────┘
       ▼                  ▼                  ▼
┌────────────┐     ┌────────────┐     ┌────────────┐
│ mcp-server │     │    api     │     │  frontend  │
│  FastMCP   │     │  FastAPI   │     │ React+Nginx│
│ :8001      │     │ :3000      │     │ :80        │
└─────┬──────┘     └─────┬──────┘     └────────────┘
      │                  │
      │                  ▼
      │        ┌──────────────────────┐
      │        │ SQLite (WAL)         │
      │        │ /data/otrs-mcp.db    │
      │        │ admin_users,api_keys,│
      │        │ api_usage,login_audit│
      │        └──────────────────────┘
      ▼                  ▼
┌──────────────────────────────────┐   ┌─────────────────────┐
│   Servidor OTRS                  │   │  otel-collector     │
│   (Generic Interface)            │   │  → Tempo / Mimir    │
└──────────────────────────────────┘   └─────────────────────┘

Serviços do Docker Compose

Serviço

Imagem / build

Porta publicada

Limites

api

Dockerfile.api (python:3.12.8-slim)

127.0.0.1:3000

1 CPU / 512M

mcp-server

Dockerfile (python:3.12.8-slim)

127.0.0.1:8001

1 CPU / 512M

frontend

frontend/Dockerfile (build Vite + Nginx)

127.0.0.1:8081

0.5 CPU / 128M

otel-collector

otel/opentelemetry-collector-contrib:0.108.0

127.0.0.1:4317 e :4318

0.5 CPU / 256M

Todas as portas ficam em 127.0.0.1. A exposição pública é feita pelo Nginx do host.


Pré-requisitos

  • Docker e Docker Compose

  • Servidor OTRS com Generic Interface habilitada

  • Para produção: Nginx e Certbot no host, domínio apontando para o servidor

Configurar o webservice no OTRS

  1. Vá em Administração → Web Services

  2. Crie ou edite um webservice expondo: SessionCreate, TicketCreate, TicketGet, TicketSearch, TicketUpdate, TicketHistoryGet

  3. Anote a URL: https://seu-otrs/otrs/nph-genericinterface.pl/Webservice/NomeDoWebservice

  4. Garanta que o usuário configurado tem permissão nas filas usadas


Início rápido

git clone https://github.com/eduardoantoniojunior/otrs-mcp-server.git
cd otrs-mcp-server
cp .env.example .env

Preencha o mínimo no .env:

OTRS_BASE_URL=https://seu-otrs/otrs/nph-genericinterface.pl/Webservice/MCPConnector
OTRS_USERNAME=seu-usuario
OTRS_PASSWORD=sua-senha

OTRS_ADMIN_USER=admin
OTRS_ADMIN_PASSWORD=escolha-uma-senha-forte
OTRS_JWT_SECRET=<saída de: python -c "import secrets; print(secrets.token_hex(32))">

Suba os containers:

docker compose up -d --build
docker compose ps
curl -s http://127.0.0.1:3000/api/health   # {"status":"ok"}

O usuário admin é criado no primeiro start, somente se ainda não existir nenhum e OTRS_ADMIN_PASSWORD estiver definido. Acesse o painel em http://127.0.0.1:8081 (ou pela URL pública do Nginx), faça login e crie sua primeira API key.

Rodar sem Docker

uv sync --extra dev

# API REST em :3000
uv run uvicorn otrs_mcp.api:app --port 3000 --reload

# MCP em HTTP na :8001
$env:OTRS_MCP_TRANSPORT="http"; uv run python -m otrs_mcp.main

# Frontend em :5173
cd frontend; npm ci; npm run dev

Como usar o MCP

O servidor fala dois transportes, definidos por OTRS_MCP_TRANSPORT:

  • stdio (padrão) — o cliente sobe o processo local e conversa por pipe. Não precisa de rede nem de API key.

  • http — Streamable HTTP em /mcp, para agentes remotos. Exige API key válida em toda requisição.

Autenticação no transporte HTTP

Requisições sem Authorization: Bearer sk-otrs-... são recusadas com 401 antes de qualquer tool executar:

{"error": "invalid_token", "error_description": "Authentication required"}

São recusadas as keys inexistentes, revogadas (active = 0) e vencidas (expires_at no passado). A validação usa a mesma tabela api_keys da API REST, e as permissões da key viram escopos:

Permissão da key

Tools liberadas

read

get_ticket, get_ticket_articles, search_tickets, get_ticket_history, list_customer_users e os resources

write

create_ticket, update_ticket

admin

todas

Uma key só de leitura que tente escrever recebe erro na tool, não na conexão:

Permissao 'write' necessaria. A API key possui: read

Claude Desktop / Kiro — remoto (HTTP)

{
  "mcpServers": {
    "otrs": {
      "url": "https://seu-dominio/otrs/mcp",
      "headers": {
        "Authorization": "Bearer sk-otrs-sua-api-key"
      }
    }
  }
}

O path /otrs/mcp corresponde ao nginx/mcp.conf deste repositório. Em domínio dedicado, use https://seu-dominio/mcp.

VS Code

{
  "servers": {
    "otrs": {
      "type": "http",
      "url": "https://seu-dominio/otrs/mcp",
      "headers": {
        "Authorization": "Bearer sk-otrs-sua-api-key"
      }
    }
  }
}

Local via stdio

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

Python SDK

import asyncio
from mcp.client.session import ClientSession
from mcp.client.streamable_http import streamablehttp_client


async def main() -> None:
    headers = {"Authorization": "Bearer sk-otrs-sua-api-key"}
    async with streamablehttp_client(
        "https://seu-dominio/otrs/mcp", headers=headers
    ) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()

            tools = await session.list_tools()
            print([t.name for t in tools.tools])

            result = await session.call_tool(
                "search_tickets",
                arguments={"state": "new", "limit": 5},
            )
            print(result)


asyncio.run(main())

Exemplos de pedidos ao agente

Com o MCP conectado, o modelo resolve pedidos em linguagem natural:

  • "Abra um ticket na fila Suporte com prioridade 4 high sobre a impressora do 3º andar"

  • "Quais tickets estão em aberto na fila Infraestrutura?"

  • "Mostre o histórico do ticket 4821"

  • "Mude o ticket 4821 para closed successful"

  • "Resuma a conversa do ticket 4821" — usa get_ticket_articles

  • "O que o cliente disse no ticket 4821?" — get_ticket_articles com sender_type=customer

Retornos incluem links prontos para a interface do OTRS: get_ticket, update_ticket, create_ticket e get_ticket_history retornam WebURL; get_ticket e get_ticket_history também retornam HistoryWebURL; search_tickets retorna WebSearchURL e TicketWebURLs.


Tools MCP

Tool

Parâmetros

Observações

create_ticket

title, body, queue?, priority?, state?, customer_user?, ticket_type?

Aplica os defaults de OTRS_DEFAULT_*; valida prioridade. Retorna WebURL

get_ticket

ticket_id, include_dynamic_fields?, include_extended_data?, include_articles?, article_limit?, article_order?, article_sender_type?

ticket_id precisa ser numérico. Retorna WebURL e HistoryWebURL. Com include_articles=True, também Articles (whitelist) e ArticleCount

get_ticket_articles

ticket_id, limit? (default 20), order? (asc|desc), sender_type?

Retorna só a lista de artigos (Subject, Body, SenderType, IsVisibleForCustomer, CreateTime) + WebURL/HistoryWebURL. Use quando o pedido é sobre conteúdo/conversa do ticket

search_tickets

customer_user?, customer_id?, queue?, state?, priority?, title?, limit?, sort_by?, order_by?

title aceita * como curinga (convertido para %). Retorna WebSearchURL e TicketWebURLs

update_ticket

ticket_id, title?, queue?, priority?, state?, customer_user?, owner?

Envia só os campos preenchidos. Retorna WebURL

get_ticket_history

ticket_id

Retorna WebURL e HistoryWebURL. Traz apenas eventos (mudanças de estado, atribuições, ArticleID), sem corpo dos artigos — use get_ticket_articles para o conteúdo

list_customer_users

limit? (default 200)

Deriva a lista de tickets recentes

Prioridades aceitas (src/otrs_mcp/constants.py): 1 very low, 2 low, 3 normal, 4 high, 5 very high.

Campos retornados por artigo (whitelist em constants.py::ARTICLE_WHITELIST_FIELDS): ArticleID, Subject, Body, SenderType, ArticleType, CommunicationChannel, IsVisibleForCustomer, CreateTime, ChangeTime, From, To, Cc, ContentType, Charset, MimeType. Campos internos do OTRS (MessageID, InReplyTo, References, etc.) são filtrados.

Cada chamada é registrada em activity.json com tool, status, duração e ticket_id. Campos chamados password são removidos antes de gravar.


Resources MCP

URI

Conteúdo

otrs://ticket/{ticket_id}

Ticket completo em JSON (metadados; use a tool get_ticket com include_articles=True para o corpo)

otrs://ticket/{ticket_id}/articles

Últimos 20 artigos do ticket em ordem descendente

otrs://ticket/{ticket_id}/history

Histórico do ticket (eventos, sem corpo)

otrs://search/tickets

Os 20 tickets mais recentes


API REST

Base: https://seu-dominio/otrs/api (ou http://127.0.0.1:3000/api local).

Autenticação por header, exceto no health check:

Authorization: Bearer <api-key-ou-jwt>

Público

Método

Rota

Descrição

GET

/api/health

Health check

Tickets — API key ou JWT

Método

Rota

Permissão

GET

/api/tickets

read

GET

/api/tickets/{id}

read

GET

/api/tickets/{id}/articles

read

POST

/api/tickets

write

PUT

/api/tickets/{id}

write

GET

/api/tickets/{id}/history

read

GET

/api/config

autenticado

Filtros de GET /api/tickets: customer_user, customer_id, queue, state, priority, title, limit (1–200), sort_by, order_by.

Query params de GET /api/tickets/{id}: include_articles (bool), article_limit (1–200), article_order (asc|desc), article_sender_type (ex.: customer, agent, system).

Query params de GET /api/tickets/{id}/articles: limit (1–200, default 20), order (asc|desc), sender_type.

Atividade — API key ou JWT

Método

Rota

Permissão

GET

/api/activity

autenticado

GET

/api/activity/summary

autenticado

DELETE

/api/activity

write

Administração — somente JWT

Método

Rota

Descrição

POST

/api/admin/login

Login, devolve JWT

POST

/api/admin/refresh

Renova o JWT sem pedir senha

GET

/api/admin/me

Admin autenticado

POST GET DELETE

/api/admin/users[/{id}]

CRUD de administradores

POST GET

/api/admin/keys

Criar e listar API keys

PATCH

/api/admin/keys/{id}/revoke

Desativar key

DELETE

/api/admin/keys/{id}

Remover key

GET

/api/admin/activity

Auditoria de uso (SQLite)

GET

/api/admin/login-audit

Tentativas de login

GET

/api/admin/metrics/daily

Métricas por dia (days, 1–90)

Exemplo com cURL

KEY="sk-otrs-sua-api-key"
BASE="https://seu-dominio/otrs/api"

# Buscar tickets novos
curl -s -H "Authorization: Bearer $KEY" "$BASE/tickets?state=new&limit=5"

# Ler o corpo dos artigos de um ticket
curl -s -H "Authorization: Bearer $KEY" \
  "$BASE/tickets/4821/articles?limit=10&order=desc&sender_type=customer"

# Criar ticket
curl -s -X POST "$BASE/tickets" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Impressora sem toner","body":"3o andar, sala 12","queue":"Suporte","priority":"3 normal"}'

# Fechar ticket
curl -s -X PUT "$BASE/tickets/4821" \
  -H "Authorization: Bearer $KEY" \
  -H "Content-Type: application/json" \
  -d '{"state":"closed successful"}'

Códigos de erro

Código

Significado

401

Token ausente, inválido, expirado ou key revogada

403

Permissão insuficiente para a operação

404

Ticket não encontrado

422

Validação falhou (ticket_id não numérico, prioridade inválida)

429

Rate limit da API key, ou lockout de login

502

Erro na comunicação com o OTRS

503

OTRS indisponível ou API ainda inicializando


Configuração

Todas as variáveis usam o prefixo OTRS_ e são lidas do ambiente (pydantic-settings).

OTRS — obrigatórias

Variável

Descrição

OTRS_BASE_URL

URL completa do webservice

OTRS_USERNAME

Usuário do OTRS

OTRS_PASSWORD

Senha do OTRS

Faltando qualquer uma, o processo falha no start com OTRSValidationError.

OTRS — opcionais

Variável

Padrão

Descrição

OTRS_VERIFY_SSL

true

Verificação de certificado

OTRS_TIMEOUT

30

Timeout HTTP (s)

OTRS_DEBUG

false

Log de debug por requisição

OTRS_DEFAULT_QUEUE

Raw

Fila padrão

OTRS_DEFAULT_STATE

new

Estado padrão

OTRS_DEFAULT_PRIORITY

3 normal

Prioridade padrão

OTRS_DEFAULT_TYPE

vazio

Tipo padrão (omitido se vazio)

OTRS_WEB_BASE_URL

derivado

Base da interface web, usada nos links WebURL

OTRS_VALID_QUEUES

vazio

Filas do dropdown do painel (separadas por vírgula)

OTRS_VALID_TYPES

vazio

Tipos do dropdown do painel

Quando OTRS_WEB_BASE_URL não é informado, ele é derivado de OTRS_BASE_URL cortando em /nph-genericinterface.pl.

Autenticação

Variável

Padrão

Descrição

OTRS_ENV

development

Com production, OTRS_JWT_SECRET passa a ser obrigatório

OTRS_JWT_SECRET

gerado

Segredo HS256. Sem ele em dev, um aleatório é gerado e os tokens morrem a cada restart

OTRS_JWT_EXPIRE_MINUTES

480

Validade do JWT

OTRS_ADMIN_USER

admin

Admin criado no primeiro start

OTRS_ADMIN_PASSWORD

—

Sem isso, nenhum admin é criado automaticamente

MCP, banco e CORS

Variável

Padrão

Descrição

OTRS_MCP_TRANSPORT

stdio

stdio ou http. Com http, a API key passa a ser exigida

OTRS_MCP_HOST

0.0.0.0

Bind do MCP em modo http

OTRS_MCP_PORT

8001

Porta do MCP em modo http

OTRS_MCP_ISSUER_URL

https://otrs-mcp.local

Valor de issuer_url exigido pelas AuthSettings do SDK; não há OAuth externo

OTRS_DB_PATH

/data/otrs-mcp.db

Caminho do SQLite

OTRS_ACTIVITY_FILE

/data/activity.json

Log de atividade do MCP

OTRS_ACTIVITY_MAX_EVENTS

1000

Eventos mantidos no JSON

OTRS_CORS_ORIGINS

http://localhost:5173,http://localhost:8080

Origens permitidas

Frontend e telemetria

Variável

Padrão

Descrição

VITE_BASE_PATH

/

Subpath do build (ex.: /otrs/). Aplicado em build time

VITE_OTEL_ENDPOINT

vazio

Collector para traces do browser

OTEL_TEMPO_ENDPOINT

vazio

Destino OTLP do collector


API keys

Formato: sk-otrs- seguido de 64 caracteres hex. A chave é exibida uma única vez, na criação; o banco guarda apenas o SHA-256 e um prefixo de 12 caracteres para identificação.

Criar pelo painel: MCP Tokens → Create Token. Ou via API:

curl -s -X POST "$BASE/admin/keys" \
  -H "Authorization: Bearer $JWT" \
  -H "Content-Type: application/json" \
  -d '{"name":"Agente Suporte","agent_name":"suporte-bot","permissions":["read","write"],"rate_limit":100,"expires_in_days":90}'

Campo

Regra

permissions

read, write, admin. admin satisfaz qualquer verificação

rate_limit

Requisições por minuto, 1–10000

expires_in_days

1–365, ou omitido para não expirar

Uma key é rejeitada se não existir, estiver revogada (active = 0) ou vencida.


Painel administrativo

React 19 + Vite 6 + TanStack Query 5 + Tailwind.

Página

Função

Dashboard

Métricas de uso, atividade por dia, distribuição por tool, ranking de agentes

MCP Tokens

Criar, revogar e remover API keys; permissões, rate limit, expiração

Admin Users

Gerenciar administradores

Audit Log

Operações registradas em api_usage, com agente, key, ticket e duração

Login Audit

Tentativas de login com IP e user agent

Client MCP Wizard

Gera a configuração pronta para Claude Desktop, VS Code, Python e cURL

Settings

Estado da conexão com o OTRS, filas e tipos configurados

Também há componentes de ticket (TicketList, TicketDetail, TicketForm) para operar tickets pelo painel.


Segurança

Camada

Implementação

Rede

Portas dos containers em 127.0.0.1; exposição via Nginx com TLS (Certbot)

Senhas

bcrypt via passlib para admins

API keys

SHA-256 no banco, valor bruto nunca persistido

MCP HTTP

TokenVerifier valida a API key em cada requisição; RequireAuthMiddleware responde 401 antes de executar tool. Escopo read/write verificado por tool

JWT

HS256 com sub, username, exp, iat, jti, type

Produção

OTRS_ENV=production sem OTRS_JWT_SECRET aborta o start

Brute-force

5 falhas em 15 min por usuário ou por IP → 429, avaliado sobre login_audit

Rate limit

Janela de 60 s por API key sobre api_usage; rate_limit 0 libera

Permissões

require_permission("read"/"write") em cada rota de ticket

Headers

X-Content-Type-Options, X-Frame-Options: DENY, X-XSS-Protection, Referrer-Policy, Cache-Control: no-store; Nginx adiciona Permissions-Policy

CORS

Origens por OTRS_CORS_ORIGINS, allow_headers restrito a Authorization e Content-Type

Validação

ticket_id por regex ^\d{1,20}$, prioridade contra lista fechada, Pydantic com min_length/max_length

Sanitização

Erros do OTRS viram 502 "Erro na comunicacao com o OTRS"; detalhes só no log

Sessão OTRS

SessionCreate protegido por asyncio.Lock; requisições seguintes enviam apenas SessionID

Auditoria

Toda operação de ticket grava agente, key, ticket e duração em api_usage

Containers

Usuário não-root otrs, multi-stage, imagens pinadas (python:3.12.8, uv:0.5), limites de CPU e memória

Fail2ban

Jails em deploy/fail2ban/ para login, abuso de API e varredura de bots

Segredos ficam apenas em variáveis de ambiente. record_activity e record_tool_call descartam campos password antes de gravar.


Observabilidade

Backend instrumentado sem alteração de código: os Dockerfiles usam opentelemetry-instrument como wrapper, com instrumentações de FastAPI, httpx, SQLite3 e logging.

api / mcp-server ──gRPC:4317──▶ otel-collector ──OTLP HTTP──▶ Tempo / Mimir
browser ─────────HTTP:4318────▶ otel-collector

Para habilitar o envio, defina no .env:

OTEL_TEMPO_ENDPOINT=http://IP-DO-GRAFANA:4318
VITE_OTEL_ENDPOINT=https://seu-dominio/otrs/otel

Rebuild (VITE_OTEL_ENDPOINT entra no build do frontend):

docker compose up -d --build

Consulta no Grafana Explore (Tempo):

{ resource.service.name = "otrs-mcp-api" }

Sem OTEL_EXPORTER_OTLP_ENDPOINT alcançável, o opentelemetry-instrument opera em modo noop. Sem VITE_OTEL_ENDPOINT, o frontend não envia traces.


Deploy em produção

Nginx

nginx/mcp.conf vem configurado para domínio compartilhado, servindo este projeto sob o subpath /otrs/:

Rota

Destino

/otrs/mcp

127.0.0.1:8001/mcp (match exato, sem trailing slash)

/otrs/api/

127.0.0.1:3000/api/

/otrs/otel/

127.0.0.1:4318/

/otrs/

frontend

/

outro serviço em 127.0.0.1:9090

Ajuste server_name e, se for usar subpath, defina VITE_BASE_PATH=/otrs/ antes do build do frontend. 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-dominio

Confira a porta do frontend antes de recarregar: o Compose publica 127.0.0.1:8081, e o vhost do repositório aponta para 8080. Veja Limitações conhecidas.

Systemd, backup e monitoramento

sudo cp deploy/otrs-mcp.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now otrs-mcp

chmod +x deploy/deploy.sh deploy/backup.sh deploy/healthcheck.sh
(crontab -l 2>/dev/null; echo "0 3 * * * /opt/otrs-mcp-server/deploy/backup.sh") | crontab -
(crontab -l 2>/dev/null; echo "*/5 * * * * /opt/otrs-mcp-server/deploy/healthcheck.sh") | crontab -

sudo cp deploy/otrs-mcp.logrotate /etc/logrotate.d/otrs-mcp

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

Atualizações posteriores: ./deploy/deploy.sh --pull.


Estrutura do projeto

otrs-mcp-server/
├── src/otrs_mcp/
│   ├── main.py           # Entry point MCP (stdio / streamable-http)
│   ├── tools.py          # 5 tools MCP
│   ├── resources.py      # 3 resources MCP
│   ├── mcp_auth.py       # TokenVerifier de API key + escopos do MCP
│   ├── api.py            # API REST + middleware de security headers
│   ├── routes/admin.py   # Login, refresh, users, keys, auditoria, métricas
│   ├── auth.py           # JWT, API key, rate limit, permissões
│   ├── database.py       # SQLite WAL: schema e CRUD
│   ├── client.py         # Cliente HTTP do OTRS com sessão e retry
│   ├── config.py         # Configuração via pydantic-settings
│   ├── validation.py     # validate_ticket_id
│   ├── activity.py       # Atividade em JSON
│   ├── constants.py      # Prioridades e estados válidos
│   └── exceptions.py     # Exceções do domínio
├── frontend/             # React 19 + Vite 6 + Tailwind
├── nginx/mcp.conf        # Vhost do host
├── otel/                 # Config do collector
├── deploy/               # systemd, scripts, logrotate, fail2ban
├── tests/unit/           # 41 testes
├── docker-compose.yml
├── Dockerfile            # MCP server
└── Dockerfile.api        # API REST

Tabelas do SQLite

Tabela

Conteúdo

admin_users

Administradores e hash bcrypt

api_keys

Keys com hash, permissões, rate limit, expiração, contador de uso

api_usage

Auditoria de operações; base do rate limit e das métricas

login_audit

Tentativas de login; base do lockout de brute-force


Desenvolvimento

uv sync --extra dev

uv run pytest tests/unit/ -v
uv run pytest tests/unit/ --cov=src/otrs_mcp --cov-report=term-missing

uv run black src/
uv run isort src/
uv run mypy src/

Entry points instalados: otrs-mcp-server (MCP) e otrs-mcp-api (REST).


Limitações conhecidas

Pontos que valem atenção antes de expor o serviço:

  1. Divergência de porta do frontend. O Compose publica 127.0.0.1:8081:80; o vhost faz proxy_pass para 127.0.0.1:8080. Alinhe um dos dois, senão /otrs/ responde 502.

  2. Rate limit conta operações registradas. A janela usa as linhas de api_usage, gravadas pela API REST após o sucesso da operação. As tools MCP registram atividade em activity.json, não em api_usage, então o rate limit de uma key usada só via MCP não é acionado.

  3. usage_count infla no uso via MCP. O token é validado em cada requisição HTTP do transporte streamable-http, e uma única sessão MCP gera várias requisições. O contador da key sobe mais rápido do que o número de tools chamadas.

  4. Lockout de login por IP e usuário. Como a contagem considera o username, tentativas repetidas contra um usuário existente podem bloquear temporariamente o login legítimo dele. O desbloqueio é por tempo (15 min).

  5. SQLite sem criptografia em repouso. Hashes de senha e de key ficam em /data/otrs-mcp.db. Proteja o volume e os backups.


Solução de problemas

Sintoma

O que verificar

Erro de SSL ao falar com o OTRS

OTRS_VERIFY_SSL=false para certificado interno

Redirect 301 do OTRS

Use a URL HTTPS completa em OTRS_BASE_URL

Start falha com OTRSValidationError

Falta OTRS_BASE_URL, OTRS_USERNAME ou OTRS_PASSWORD

Start falha pedindo JWT secret

OTRS_ENV=production exige OTRS_JWT_SECRET

Login sempre inválido no primeiro uso

Nenhum admin criado: defina OTRS_ADMIN_PASSWORD e recrie o container

401 na API

Key revogada, expirada ou header ausente

403 na API

Falta read ou write na key

401 no /mcp

Cliente MCP sem Authorization, ou key inválida/revogada/vencida

Permissao 'write' necessaria na tool

A key só tem read; crie outra com write

MCP recusa toda key

O container mcp-server precisa do volume app_data:/data e de OTRS_DB_PATH para ler a tabela api_keys

429 na API

Rate limit da key; aumente o valor ou use 0

429 no login

Lockout de brute-force; espere 15 min e confira o Login Audit

422 em rota de ticket

ticket_id precisa ser só dígitos

502 em /otrs/

Porta do frontend divergente (8080 vs 8081)

Traces ausentes no Grafana

Confira OTEL_TEMPO_ENDPOINT e docker compose logs otel-collector

SPA quebrada em subpath

Rebuild com VITE_BASE_PATH correto

docker compose logs -f api
docker compose logs -f mcp-server
docker compose logs -f otel-collector
sudo journalctl -u otrs-mcp -f
sudo tail -f /var/log/nginx/mcp-admin-error.log

Licença

Apache-2.0

Available Tools

5 tools
create_ticketC

Create a new ticket in OTRS

Inputs: body (required), title (required), queue, state, +3 more. Returns: object

get_ticketB

Get ticket details from OTRS

Inputs: ticket_id (required), include_extended_data, include_dynamic_fields. Returns: object

get_ticket_historyC

Get ticket history from OTRS

Inputs: ticket_id (required). Returns: object

search_ticketsC

Search for tickets in OTRS

Inputs: limit, queue, state, title, +5 more. Returns: object

update_ticketC

Update an existing ticket in OTRS

Inputs: ticket_id (required), owner, queue, state, +3 more. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.3/5.0

    Scored across 5 tools

    Disambiguation5/5

    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.

    Naming Consistency5/5

    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.

    Tool Count5/5

    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.

    Completeness5/5

    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

    ActivitySlowing
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      C
      quality
      C
      maintenance
      An 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.
      12
      4
      MIT
    • A
      license
      B
      quality
      B
      maintenance
      An MCP server that exposes the Tickiti helpdesk API to AI assistants, enabling ticket management and helpdesk operations via natural language.
      11
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      An MCP server that connects AI assistants to Zammad, providing tools for managing tickets, users, organizations, and attachments.
      49
      AGPL 3.0