Tempo MCP Server

Um servidor MCP para gerenciar registros de trabalho do Tempo no Jira. Ele se conecta aos serviços do Jira e do Tempo usando tokens de API e variáveis de ambiente.

Documentação

MseeP.ai Security Assessment Badge

Tempo MCP Server

Um servidor Model Context Protocol (MCP) para gerenciar apontamentos de horas (worklogs) do Tempo no Jira. Este servidor fornece ferramentas para registrar tempo e gerenciar apontamentos através da API do Tempo, tornando-o acessível pelo Claude, Cursor e outros clientes compatíveis com MCP.

npm version License: MIT

Recursos

  • Consultar Apontamentos: Obtenha todos os apontamentos para um intervalo de datas específico
  • Criar Apontamento: Registre horas em issues do Jira
  • Criação em Lote: Crie múltiplos apontamentos em uma única operação
  • Editar Apontamento: Modifique tempo gasto, datas e descrições
  • Excluir Apontamento: Remova apontamentos existentes
  • Relatório de Dias Ausentes: Encontre dias úteis onde você registrou menos do que o esperado (usa o agendamento do usuário do Tempo, então feriados e dias não úteis são ignorados automaticamente)
  • Análise de Apontamentos: Agregue horas por issue, conta, dia, semana ou mês com totais e percentuais

Requisitos do Sistema

  • Node.js 18+ (LTS recomendado) — necessário apenas para os modos stdio locais
  • Instância do Jira Cloud
  • Token da API do Tempo
  • Token da API do Jira (não necessário ao usar autenticação OAuth 2.0 PKCE)

Opções de Uso

Existem três formas de usar este servidor MCP:

  1. Remoto / Cloudflare Workers (sem instalação) — hospede uma vez, compartilhe com sua equipe. Cada usuário gera sua própria URL através de uma página de configuração e a cola no Claude.ai ou ChatGPT. Funciona na web e no celular.
  2. NPX: Execute diretamente com npx no seu laptop, sem necessidade de clonar.
  3. Clone Local: Clone o repositório para desenvolvimento ou personalização.

Se você só quer usar o servidor, a opção 1 é a mais fácil e também funciona em celulares. Se você é um mantenedor implantando para sua equipe, veja o guia de implantação remota.

Opção 1: Remoto (Cloudflare Workers)

Para usuários finais

Quando sua equipe tiver o servidor implantado, o fluxo é:

  1. Abra https://<your-deployment>.workers.dev/setup.
  2. Cole seu token da API do Tempo, URL base do Jira, token da API do Jira e e-mail do Jira. Clique em Gerar URL MCP.
  3. A página retorna uma URL pessoal como https://<your-deployment>.workers.dev/mcp/u_<random>. Copie-a.
  4. Em Claude.ai → Configurações → Conectores → Adicionar conector personalizado, cole a URL.
  5. O conector sincroniza entre web, desktop e mobile (iOS/Android).

Para ChatGPT: ative Configurações → Apps → Avançado → Modo desenvolvedor (Pro/Plus/Business+), depois adicione a URL como um servidor MCP personalizado. Contas Plus/Pro podem ler; ferramentas de escrita (criar/editar apontamentos) exigem Business+ conforme os níveis da OpenAI.

A URL contém suas credenciais — trate-a como uma senha, não compartilhe nem a envie para repositórios.

Implantação remota (Cloudflare Workers)

Hospedagem gratuita no Cloudflare Workers. Cerca de 5–10 minutos do clone até a URL ativa. Qualquer pessoa pode fazer um fork e auto-hospedar — sem necessidade de coordenação com o upstream.

Pré-requisitos

  • Uma conta Cloudflare (o plano gratuito é suficiente).
  • Node.js 18+ e npm localmente — usados apenas para a CLI wrangler; o runtime do Worker em si não executa Node.

Configuração inicial

git clone https://github.com/ivelin-web/tempo-mcp-server.git
cd tempo-mcp-server
npm install

# 1. Log in to Cloudflare (opens browser).
npx wrangler login

# 2. Create your own KV namespace for per-user credentials.
npx wrangler kv namespace create USERS

⚠️ Se você fez fork do repositório: o wrangler.jsonc kv_namespaces[0].id commitado pertence à conta Cloudflare do mantenedor upstream. Substitua-o pelo id que o passo 2 acabou de retornar, caso contrário wrangler deploy falhará com KV namespace … is not valid. IDs de namespace KV são identificadores públicos por conta, não são segredos, mas cada conta tem o seu.

# 3. Generate and store the encryption key.
#    Used to AES-GCM-encrypt per-user credentials in KV.
openssl rand -base64 48 | npx wrangler secret put ENCRYPTION_KEY

# 4. (Optional) Pin the CORS origin. Defaults to "*". Set it if you only
#    want browsers from a specific app to call the Worker.
echo "https://claude.ai" | npx wrangler secret put ALLOWED_ORIGIN

# 5. Deploy.
npm run remote:deploy
# → outputs https://tempo-mcp-server.<your-account>.workers.dev

Visite /setup na URL implantada para cadastrar seu primeiro usuário.

Atualizando uma implantação existente

Após puxar novos commits do upstream:

npm install              # picks up any new deps
npm run remote:deploy    # ships the new Worker bundle

Segredos e dados KV persistem entre implantações. compatibility_date e compatibility_flags em wrangler.jsonc estão fixados, então o comportamento não muda silenciosamente quando o Cloudflare lança mudanças no runtime.

Desenvolvimento local

cp .dev.vars.example .dev.vars
# edit .dev.vars and put a real ENCRYPTION_KEY (any value works locally)
npm run remote:dev
# → http://localhost:8787 with a mock KV; data is wiped between sessions

Outros scripts úteis:

  • npm run remote:typecheck — verifica tipos do bundle do Worker (usa tsconfig.worker.json).
  • npm run remote:tail — transmite logs ao vivo do Worker implantado.

Solução de problemas

  • KV namespace … is not valid — kv_namespaces[0].id em wrangler.jsonc está vazio (ou errado). Execute npx wrangler kv namespace create USERS e cole o novo id.
  • ENCRYPTION_KEY is not defined em tempo de execução — o segredo não foi definido. Execute novamente o passo 3.
  • Rate limit binding … not available — o plano da sua conta não inclui a API de Limitação de Taxa do Workers. Ou faça upgrade, ou remova o bloco ratelimits em wrangler.jsonc e a chamada SETUP_RATE_LIMITER.limit(...) em src/remote/worker.ts.
  • 404 de /mcp/u_… — o id do usuário é desconhecido (ou nunca existiu). O Worker retorna 404 por design para ids inválidos/ausentes; peça ao usuário para executar /setup novamente.
  • Usuários existentes de repente não conseguem conectar — a causa mais provável é uma ENCRYPTION_KEY rotacionada; blobs AES-GCM existentes não podem ser descriptografados com a nova chave. Veja o aviso abaixo.

Como funciona

Armazenamento de credenciais: o handler POST /setup criptografa os dados do formulário com AES-GCM usando ENCRYPTION_KEY e os armazena no KV sob user:u_<random>. Cada requisição MCP lê e descriptografa esse registro, constrói um McpServer para aquela única requisição e despacha via createMcpHandler oficial do Cloudflare. Nenhuma credencial é mantida em memória entre requisições; nenhum Durable Object é usado.

Trate ENCRYPTION_KEY como algo de longa duração. Rotacioná-la invalida todos os registros de usuários existentes (a tag AES-GCM não validará com a nova chave), e todos os seus usuários precisarão executar /setup novamente. Escolha uma chave de openssl rand -base64 48 uma vez e nunca a altere.

Modelo de autenticação: a URL /mcp/u_<id> é a credencial. O id base64url de 22 caracteres carrega ~128 bits de entropia. Nunca retornamos 401 para esse caminho (o Claude.ai web tem bugs conhecidos com o fluxo 401-depois-OAuth), e retornamos 404 para ids desconhecidos. Isso corresponde ao padrão de token-URL usado pelo Zapier MCP, Pipedream MCP e similares.

Proteções já implementadas:

  • Limite de taxa por IP (5 req/min) em POST /setup, via binding nativo de Rate Limiting do Cloudflare.
  • Cache-Control: no-store nas respostas de /setup para que a página de sucesso (que contém a URL MCP) e a re-renderização de erro (que ecoa tokens de volta) nunca fiquem em cache.
  • Referrer-Policy: no-referrer em todas as páginas HTML para que a URL MCP não vaze via cabeçalhos de referrer.

Limites a conhecer:

  • Plano gratuito do Workers: 100k requisições/dia, 50ms de CPU por requisição (somos limitados por I/O, confortável).
  • Plano gratuito do KV: 100k leituras/dia, 1k escritas/dia. A configuração escreve uma vez por usuário; leituras acontecem por chamada MCP.
  • O Worker suporta apenas autenticação básica do Jira (token clássico de API + e-mail). Bearer e o fluxo OAuth 2.0 PKCE são apenas para stdio — bearer requer roteamento de URL de gateway que o Worker ainda não faz, e PKCE precisa de um callback de navegador que o Worker não pode hospedar.

Opção 2: Uso via NPX

A forma mais fácil de usar este servidor é via npx, sem instalação:

Conectando ao Claude Desktop (Método NPX)

  1. Abra seu arquivo de configuração do cliente MCP:

    • Claude Desktop (macOS): ~/Library/Application Support/Claude/claude_desktop_config.json
    • Claude Desktop (Windows): %APPDATA%\Claude\claude_desktop_config.json
  2. Adicione a seguinte configuração:

{
  "mcpServers": {
    "Jira_Tempo": {
      "command": "npx",
      "args": ["-y", "@ivelin-web/tempo-mcp-server"],
      "env": {
        "TEMPO_API_TOKEN": "your_tempo_api_token_here",
        "JIRA_API_TOKEN": "your_jira_api_token_here",
        "JIRA_EMAIL": "your_email@example.com",
        "JIRA_BASE_URL": "https://your-org.atlassian.net"
      }
    }
  }
}
  1. Reinicie seu cliente Claude Desktop

Instalação com um Clique para Cursor

Install MCP Server

Opção 3: Clone Local do Repositório

Instalação

# Clone the repository
git clone https://github.com/ivelin-web/tempo-mcp-server.git
cd tempo-mcp-server

# Install dependencies
npm install

# Build TypeScript files
npm run build

Executando Localmente

Existem duas formas de executar o servidor localmente:

1. Usando o MCP Inspector (para desenvolvimento e depuração)

npm run inspect

2. Usando Node diretamente

Você pode executar o servidor diretamente com Node apontando para o arquivo JavaScript compilado:

Conectando ao Claude Desktop (Método Local)

  1. Abra seu arquivo de configuração do cliente MCP
  2. Adicione a seguinte configuração:
{
  "mcpServers": {
    "Jira_Tempo": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/tempo-mcp-server/build/index.js"],
      "env": {
        "TEMPO_API_TOKEN": "your_tempo_api_token_here",
        "JIRA_API_TOKEN": "your_jira_api_token_here",
        "JIRA_EMAIL": "your_email@example.com",
        "JIRA_BASE_URL": "https://your-org.atlassian.net"
      }
    }
  }
}
  1. Reinicie seu cliente Claude Desktop

Obtendo Tokens de API

  1. Token da API do Tempo:

    • Vá para Tempo > Configurações > Integração de API
    • Crie um novo token de API com Acesso personalizado e selecione no mínimo:
      • Apontamentos (Visualizar + Gerenciar) — para todas as ferramentas de apontamento
      • Esquemas (Visualizar) — necessário para getMissingWorklogDays (lê o agendamento do usuário)
      • Contas (Visualizar) — apenas se seus apontamentos usarem contas do Tempo
      • Equipes (Visualizar) — apenas se você usar os filtros program / team (cobre Equipes e Programas)
    • O Tempo não permite editar escopos em um token existente; crie um novo se precisar adicionar escopos depois.
  2. Token da API do Jira:

    • Vá para Tokens de API do Atlassian
    • Clique em "Criar token de API" (o fluxo clássico, sem escopos). É isso que funciona com a autenticação basic pronta para uso.
    • Não use "Criar token de API com escopos" — esses tokens devem ser enviados através da URL de gateway do Atlassian (https://api.atlassian.com/ex/jira/{cloudId}/...) com o cloud ID, que o caminho de autenticação basic deste servidor não roteia atualmente. Eles falharão com 401 contra a URL do seu site. Se você só tiver um token com escopos disponível (por exemplo, sua organização desativou tokens clássicos), use o fluxo OAuth 2.0 PKCE — ele roteia pelo gateway automaticamente.

Variáveis de Ambiente

O servidor requer as seguintes variáveis de ambiente:

TEMPO_API_TOKEN           # Your Tempo API token
JIRA_API_TOKEN            # Your Jira API token (required for basic and bearer auth)
JIRA_EMAIL                # Your Jira account email (required for basic auth)
JIRA_BASE_URL             # Your Jira instance URL (e.g., https://your-org.atlassian.net)
JIRA_AUTH_TYPE            # Optional: 'basic' (default), 'bearer', or 'oauth'
JIRA_OAUTH_CLIENT_ID      # OAuth 2.0 client ID (required for oauth auth)
JIRA_OAUTH_CLIENT_SECRET  # OAuth 2.0 client secret (required for oauth auth)
JIRA_TEMPO_ACCOUNT_CUSTOM_FIELD_ID     # Optional: Custom field ID for Tempo accounts

Você pode defini-las no seu ambiente ou fornecê-las na configuração do cliente MCP.

Tipos de Autenticação

O servidor suporta três métodos de autenticação para a API do Jira:

Autenticação Básica (padrão)

Usa e-mail e token de API. Este é o método tradicional:

{
  "env": {
    "JIRA_API_TOKEN": "your_api_token",
    "JIRA_EMAIL": "your_email@example.com",
    "JIRA_AUTH_TYPE": "basic"
  }
}

Autenticação com Token Bearer (OAuth 2.0)

Para usuários que desejam usar tokens OAuth 2.0 com escopos para maior segurança:

{
  "env": {
    "JIRA_API_TOKEN": "your_oauth_access_token",
    "JIRA_AUTH_TYPE": "bearer"
  }
}

Nota: Ao usar autenticação bearer, JIRA_EMAIL não é necessário, pois o usuário é identificado pelo token.

Autenticação OAuth 2.0 PKCE

Algumas organizações Atlassian restringem o acesso a tokens de API por política administrativa, o que faz a autenticação básica e bearer falharem. O tipo oauth implementa o fluxo completo de código de autorização OAuth 2.0 com PKCE e funciona independentemente de restrições de token de API — os tokens são de curta duração e renovados automaticamente, sem gerenciamento manual.

No primeiro uso, uma janela do navegador abre para você autorizar o acesso. Os tokens são armazenados localmente em ~/.tempo-mcp-server/tokens.json e renovados automaticamente.

  1. Crie um aplicativo OAuth 2.0 no Console de Desenvolvedor Atlassian com os escopos read:jira-user e read:jira-work e http://localhost:7788/callback como URL de callback.

  2. Configure o servidor:

{
  "env": {
    "JIRA_BASE_URL": "https://your-org.atlassian.net",
    "JIRA_AUTH_TYPE": "oauth",
    "JIRA_OAUTH_CLIENT_ID": "your_client_id",
    "JIRA_OAUTH_CLIENT_SECRET": "your_client_secret"
  }
}

Nota: JIRA_API_TOKEN e JIRA_EMAIL não são necessários ao usar autenticação oauth.

Configuração de Conta do Tempo

Se sua instância do Tempo exigir que apontamentos sejam vinculados a contas, defina o ID do campo personalizado que contém as informações da conta:

JIRA_TEMPO_ACCOUNT_CUSTOM_FIELD_ID=10234

Para encontrar seu ID de campo personalizado:

  1. Vá para Configurações do Jira → Issues → Campos Personalizados
  2. Encontre seu campo de conta do Tempo e anote o ID da URL ou da configuração do campo

Visualizando Apontamentos de Outros Usuários

As ferramentas de leitura (retrieveWorklogs, getWorklogAnalytics, getMissingWorklogDays) usam por padrão os apontamentos do próprio dono do token, mas aceitam filtros opcionais para direcionar outras pessoas — útil para administradores, gerentes de projeto e líderes de equipe:

  • users — array de e-mails, nomes de exibição ou accountIds do Jira (ex.: ["ivan@company.com", "Maria Petrova"])
  • program — nome ou id do Programa do Tempo; expande para todos os membros atuais das equipes do programa
  • team — nome ou id da Equipe do Tempo; expande para todos os membros atuais da equipe

Os filtros combinam como uma união. O Tempo aplica permissões no lado do servidor: a API omite silenciosamente apontamentos que o dono do token não tem permissão de ver (nenhum erro é retornado). Para visualizar outros usuários, você precisa de:

  1. Um Papel de Permissão do Tempo com Visualizar Apontamentos concedido ao dono do token (Tempo > Configurações > Papéis de Permissão) — "Completo" para todos, ou "Restrito" + equipes selecionadas
  2. Permissão Navegar Projetos do Jira nos projetos relevantes
  3. O escopo Equipes no token da API do Tempo se você usar os filtros program / team getWorklogAnalytics também suporta groupBy: "user" — combinado com program, ele produz um relatório de horas por pessoa em uma única chamada. getMissingWorklogDays com um filtro retorna um relatório por usuário dos dias com tempo ausente (visualizar as agendas de outros usuários também é controlado por permissões).

Ferramentas Disponíveis

retrieveWorklogs

Busca apontamentos para o usuário configurado (ou outros usuários via filtros) entre as datas de início e fim.

Parameters:
- startDate: String (YYYY-MM-DD)
- endDate: String (YYYY-MM-DD)
- users: String[] (optional) — emails, display names, or accountIds
- program: String (optional) — Tempo Program name or id
- team: String (optional) — Tempo Team name or id

createWorklog

Cria um novo apontamento para um problema específico do Jira.

Parameters:
- issueKey: String (e.g., "PROJECT-123")
- timeSpentHours: Number (positive)
- date: String (YYYY-MM-DD)
- description: String (optional)
- startTime: String (HH:MM format, optional)

bulkCreateWorklogs

Cria vários apontamentos em uma única operação.

Parameters:
- worklogEntries: Array of {
    issueKey: String
    timeSpentHours: Number
    date: String (YYYY-MM-DD)
    description: String (optional)
    startTime: String (HH:MM format, optional)
  }

editWorklog

Modifica um apontamento existente.

Parameters:
- worklogId: String
- timeSpentHours: Number (positive)
- description: String (optional)
- date: String (YYYY-MM-DD, optional)
- startTime: String (HH:MM format, optional)

deleteWorklog

Remove um apontamento existente.

Parameters:
- worklogId: String

getMissingWorklogDays

Relata os dias úteis em um intervalo de datas em que o usuário registrou menos tempo do que o esperado. As horas esperadas por dia vêm da agenda do usuário no Tempo, portanto feriados, dias não úteis e agendas de meio período são respeitados automaticamente. Com users / program / team, ele verifica outras pessoas e retorna um relatório por usuário (ordenado pelas horas ausentes mais altas).

Parameters:
- startDate: String (YYYY-MM-DD)
- endDate: String (YYYY-MM-DD)
- minHoursPerDay: Number (optional) — override the per-day threshold;
                                      non-working days are still skipped
- users: String[] (optional) — emails, display names, or accountIds
- program: String (optional) — Tempo Program name or id
- team: String (optional) — Tempo Team name or id

Escopo do Tempo necessário: o TEMPO_API_TOKEN deve incluir o escopo Schemes (cobre Workload Schemes, Holiday Schemes, User Schedule) além de Worklogs. O Tempo não permite modificar escopos em um token existente — se o seu token atual tiver apenas Worklogs, crie um novo em Tempo > Settings > API Integration.

getWorklogAnalytics

Agrega apontamentos em um intervalo de datas e retorna horas, contagem de apontamentos e porcentagem por grupo, ordenados por horas em ordem decrescente. Combine groupBy: "user" com program / team / users para um relatório por pessoa.

Parameters:
- startDate: String (YYYY-MM-DD)
- endDate: String (YYYY-MM-DD)
- groupBy: "issue" | "account" | "user" | "day" | "week" | "month" (optional, default "issue")
- users: String[] (optional) — emails, display names, or accountIds
- program: String (optional) — Tempo Program name or id
- team: String (optional) — Tempo Team name or id

Estrutura do Projeto

tempo-mcp-server/
├── src/                  # Source code
│   ├── authors.ts        # Author filter resolution (users/program/team → accountIds)
│   ├── config.ts         # Configuration management
│   ├── index.ts          # MCP server implementation
│   ├── jira.ts           # Jira API integration
│   ├── oauth.ts          # OAuth 2.0 PKCE flow and token management
│   ├── tools.ts          # Tool implementations
│   ├── types.ts          # TypeScript types and schemas
│   └── utils.ts          # Utility functions
├── build/                # Compiled JavaScript (generated)
├── tsconfig.json         # TypeScript configuration
└── package.json          # Project metadata and scripts

Solução de Problemas

Se você encontrar problemas:

  1. Verifique se todas as variáveis de ambiente estão configuradas corretamente
  2. Confirme se os tokens de API do Jira e do Tempo têm as permissões corretas
  3. Verifique a saída do console para mensagens de erro
  4. Tente executar com o inspetor: npm run inspect

Licença

MIT

Créditos

Este servidor implementa a especificação Model Context Protocol criada pela Anthropic.