Hubstaff MCP Server

Hubstaff MCP Server

Read-only MCP server for Hubstaff Time Tracking and Tasks APIs, providing tasks, comments, hours, and project data. Supports OAuth 2.1 for ChatGPT integration.

Category
Visit Server

README

Hubstaff MCP Server

Ask ChatGPT about Hubstaff tasks, recent changes, and actual time spent—without handing your Hubstaff credentials to ChatGPT.

MCP Node.js Docker Read only

Hubstaff MCP is a read-only bridge between the official Hubstaff API v2 and ChatGPT or any other MCP client. It turns task and time-tracking data into a conversational interface while keeping Hubstaff secrets on your own server.

Try questions such as:

  • “How many hours did the team spend on this task last week?”
  • “Which tasks and time records changed today?”
  • “Show active tasks for this project and identify where each task came from.”
  • “Break down the time on task 123 by team member.”

The server is ready for ChatGPT Developer mode and supports OAuth 2.1, Dynamic Client Registration, Authorization Code + PKCE S256, short-lived JWT access tokens, and rotating refresh tokens. Every MCP tool is read-only.

Why use it

  • Ask questions instead of assembling reports. Tasks, updates, source metadata, and tracked time are available through one MCP connection.
  • Keep Hubstaff credentials off the client. ChatGPT authenticates to your MCP server, not directly to Hubstaff.
  • Choose the right Hubstaff credential. Organization tokens, PATs, OAuth refresh tokens, and short-lived access tokens are all supported.
  • Run it continuously. Token rotation is persisted safely, and the project includes Docker, health checks, Traefik labels, and GHCR publishing.
  • Stay read-only. The server does not create, update, or delete Hubstaff data.

Hosted endpoint

https://hubstuff-mcp.copperdiver.studio/mcp

Health check:

curl https://hubstuff-mcp.copperdiver.studio/health

Expected response:

{
  "status": "ok",
  "hubstaff_configured": true,
  "oauth_configured": true
}

The health endpoint never returns secrets.

How authentication works

There are two separate trust boundaries. Keeping them separate prevents most configuration mistakes.

flowchart LR
    Client[ChatGPT or MCP client]
    Server[Hubstaff MCP server]
    Hubstaff[Hubstaff API v2]

    Client -->|OAuth 2.1 token or MCP_AUTH_TOKEN| Server
    Server -->|Organization token, PAT/OAuth access token, or temporary access token| Hubstaff
  1. MCP client → this server controls who may call the MCP tools.
  2. This server → Hubstaff controls which Hubstaff data those tools can read.

ChatGPT never receives HUBSTAFF_* credentials. It receives only a token issued or accepted by this MCP server.

Authentication at a glance

Connection Best option Configuration
ChatGPT → MCP OAuth 2.1 with DCR and PKCE OAUTH_*
Traditional MCP client → MCP Static bearer token MCP_AUTH_TOKEN
MCP → one Hubstaff organization Organization Access Token HUBSTAFF_ORGANIZATION_TOKEN
MCP → a personal Hubstaff account Personal Access Token HUBSTAFF_AUTH_MODE=pat, HUBSTAFF_REFRESH_TOKEN
MCP → Hubstaff OAuth application OAuth refresh token HUBSTAFF_AUTH_MODE=oauth, refresh token, client ID, and client secret
MCP → Hubstaff for a short test Existing access token HUBSTAFF_ACCESS_TOKEN

Configure one primary Hubstaff authentication method. If HUBSTAFF_ORGANIZATION_TOKEN is present, it takes precedence over access and refresh tokens.

Authenticate MCP clients

Option A: OAuth 2.1 for ChatGPT

This is the recommended setup for ChatGPT Work and ChatGPT Developer mode. The built-in authorization server provides:

  • OAuth discovery metadata;
  • Dynamic Client Registration;
  • Authorization Code flow;
  • PKCE S256;
  • audience-bound JWT access tokens;
  • rotating refresh grants;
  • issuer, audience, expiry, and scope validation.

Configure the public HTTPS origin and a dedicated login for the MCP authorization page:

OAUTH_ISSUER=https://hubstuff-mcp.copperdiver.studio
OAUTH_RESOURCE=https://hubstuff-mcp.copperdiver.studio/mcp
OAUTH_USERNAME=admin
OAUTH_PASSWORD=replace_with_at_least_16_random_characters
OAUTH_SIGNING_SECRET=replace_with_at_least_32_random_characters
OAUTH_STATE_PATH=/data/oauth-state.json

OAUTH_USERNAME and OAUTH_PASSWORD protect this MCP server. They are not Hubstaff account credentials. The current implementation uses one shared MCP login, which is appropriate for a private deployment but not a multi-tenant public service.

The server publishes:

/.well-known/oauth-protected-resource
/.well-known/oauth-protected-resource/mcp
/.well-known/oauth-authorization-server
/oauth/register
/oauth/authorize
/oauth/token

Option B: static bearer token

For scripts, internal agents, or MCP clients that can send a fixed authorization header:

MCP_AUTH_TOKEN=replace_with_at_least_32_random_characters

Send it as:

Authorization: Bearer <MCP_AUTH_TOKEN>

OAuth and MCP_AUTH_TOKEN may be enabled at the same time. If neither is configured, /mcp returns a configuration error rather than exposing an unprotected endpoint.

Authenticate with Hubstaff

The supported Hubstaff features use the official API v2. PAT and OAuth credentials need the following scope:

hubstaff:read

Option 1: Organization Access Token

Recommended for a long-running service dedicated to one organization. An Organization Access Token is a long-lived bearer token prefixed with hsoat_. It does not require a browser flow or token exchange.

Create it in Hubstaff:

Settings → Organization → API tokens

Assign the token to a member with access to the data the server should expose, then configure:

HUBSTAFF_ORGANIZATION_TOKEN=hsoat_replace_me

HUBSTAFF_ACCESS_TOKEN=
HUBSTAFF_REFRESH_TOKEN=
HUBSTAFF_CLIENT_ID=
HUBSTAFF_CLIENT_SECRET=

Why choose it:

  • no refresh-token rotation;
  • the acting organization member can be reassigned;
  • expiration can be set to 30, 60, or 90 days, or never;
  • well suited to shared automation and always-on services.

Option 2: Personal Access Token

A PAT is a good fit for a personal integration, internal service, or CI job. Hubstaff displays one value when the PAT is created: that value is a refresh token, not an access token.

Create a PAT under:

Hubstaff Account → Personal access tokens

Select hubstaff:read, then configure:

HUBSTAFF_AUTH_MODE=pat
HUBSTAFF_REFRESH_TOKEN=replace_with_your_pat

HUBSTAFF_ORGANIZATION_TOKEN=
HUBSTAFF_ACCESS_TOKEN=
HUBSTAFF_CLIENT_ID=
HUBSTAFF_CLIENT_SECRET=

The server exchanges the PAT for a short-lived access token, reuses that token until it is close to expiry, refreshes it when necessary, and atomically stores the rotated token pair under TOKEN_CACHE_PATH.

Do not share the same PAT between multiple applications. Each refresh rotates the token, so another consumer may invalidate the value this server expects.

Do not bind the PAT to DPoP. This server currently uses bearer tokens and does not generate a fresh DPoP proof for every request.

Option 3: Hubstaff OAuth application

Use this mode when you already have a Hubstaff OAuth application and refresh token:

HUBSTAFF_AUTH_MODE=oauth
HUBSTAFF_REFRESH_TOKEN=replace_with_oauth_refresh_token
HUBSTAFF_CLIENT_ID=replace_with_hubstaff_client_id
HUBSTAFF_CLIENT_SECRET=replace_with_hubstaff_client_secret

HUBSTAFF_ORGANIZATION_TOKEN=
HUBSTAFF_ACCESS_TOKEN=

The client ID and secret belong to Hubstaff. They are unrelated to the OAuth server that protects this MCP endpoint.

Option 4: existing access token

For a short-lived test or debugging session:

HUBSTAFF_ACCESS_TOKEN=replace_with_short_lived_access_token

HUBSTAFF_ORGANIZATION_TOKEN=
HUBSTAFF_REFRESH_TOKEN=

The server cannot refresh this token. Use an Organization Access Token or PAT for a permanent deployment.

Connect ChatGPT Work

  1. Confirm the public endpoint uses HTTPS and /health reports oauth_configured: true.

  2. In ChatGPT, open Settings → Security and login and enable Developer mode.

  3. Open the custom apps or connectors page and add:

    https://hubstuff-mcp.copperdiver.studio/mcp
    
  4. Choose OAuth if ChatGPT asks for an authentication method.

  5. Complete the authorization page with the server's OAUTH_USERNAME and OAUTH_PASSWORD.

  6. Start a new conversation and ask ChatGPT to list Hubstaff organizations.

If an authorization page expires, close it and start Connect or Retry again. Authorization requests are short-lived and single-use.

MCP tools

Tool Returns
hubstaff_capabilities Supported data sources, known API limits, and whether comments or audit history are available
hubstaff_list_organizations Organizations visible to the configured Hubstaff credential
hubstaff_list_tasks Organization tasks filtered by status, project, or assignee
hubstaff_get_task A full task record plus project_type, integration_id, and remote_id source metadata
hubstaff_recent_updates Recently changed tasks and optionally changed activity records
hubstaff_task_hours Total tracked time for a task and a per-user breakdown
hubstaff_list_audit_log_entries Organization audit events; requires Hubstaff Enterprise and elevated permissions

All tools are declared read-only, non-destructive, and idempotent.

Quick start

Run from source

Requirements: Node.js 22 or later.

git clone https://github.com/copperdiver/mcp-hubstuff.git
cd mcp-hubstuff
cp .env.example .env
npm ci
npm run build
npm test
npm start

The service listens on port 3000 by default:

http://localhost:3000/mcp
http://localhost:3000/health

Run the GHCR image

The image is published as:

ghcr.io/copperdiver/mcp-hubstuff:latest

If the package is private, sign in with a GitHub token that has read:packages:

echo "$GHCR_TOKEN" | docker login ghcr.io -u YOUR_GITHUB_USERNAME --password-stdin
docker pull ghcr.io/copperdiver/mcp-hubstuff:latest

Skip docker login if the package is public.

Run the container:

docker run --rm \
  --env-file .env \
  -p 3000:3000 \
  -v hubstaff_mcp_data:/data \
  ghcr.io/copperdiver/mcp-hubstuff:latest

Run with Docker Compose and Traefik

The included compose.yml expects an external Docker network named proxy and a Traefik certificate resolver named le.

docker network create proxy
docker compose up -d --build

Adjust the hostname and Traefik labels before deploying under a different domain.

Complete .env example

# Choose one Hubstaff authentication method.
HUBSTAFF_AUTH_MODE=pat
HUBSTAFF_REFRESH_TOKEN=replace_with_your_pat
HUBSTAFF_ORGANIZATION_TOKEN=
HUBSTAFF_ACCESS_TOKEN=
HUBSTAFF_CLIENT_ID=
HUBSTAFF_CLIENT_SECRET=

# Client authentication for the MCP endpoint.
MCP_AUTH_TOKEN=replace_with_at_least_32_random_characters

# OAuth 2.1 for ChatGPT.
OAUTH_ISSUER=https://hubstuff-mcp.copperdiver.studio
OAUTH_RESOURCE=https://hubstuff-mcp.copperdiver.studio/mcp
OAUTH_USERNAME=admin
OAUTH_PASSWORD=replace_with_at_least_16_random_characters
OAUTH_SIGNING_SECRET=replace_with_at_least_32_random_characters
OAUTH_STATE_PATH=/data/oauth-state.json

# Runtime and Hubstaff endpoints.
PORT=3000
HUBSTAFF_API_BASE=https://api.hubstaff.com
HUBSTAFF_TOKEN_URL=https://account.hubstaff.com/access_tokens
TOKEN_CACHE_PATH=/data/token.json
REQUEST_TIMEOUT_MS=30000

Never commit .env. The repository excludes it from Git and from the Docker build context.

Persistent data

Two files must survive container recreation:

Path Purpose
/data/token.json Current Hubstaff access token, rotated refresh token, and expiry time
/data/oauth-state.json Registered OAuth clients, authorization codes, and MCP refresh grants

The included Compose configuration mounts a named volume at /data.

Security

  • All MCP tools are read-only.
  • The public MCP endpoint requires OAuth, a static bearer token, or both.
  • MCP access tokens are checked for issuer, audience, expiry, and scope.
  • PKCE S256 protects the authorization-code exchange.
  • OAuth callback URLs are restricted to trusted HTTPS callbacks and approved local development URLs.
  • Secret values are never returned by /health.
  • .env, OAuth state, and token-cache files must never be committed.
  • Secrets are not baked into the Docker image.
  • The container runs as the unprivileged app user.

For a multi-user deployment, replace the shared OAUTH_USERNAME and OAUTH_PASSWORD with a real identity provider and maintain a separate Hubstaff credential mapping for each user.

Hubstaff API limitations

  • The Activity API accepts at most seven days per request. hubstaff_task_hours splits longer ranges automatically.
  • hubstaff_task_hours accepts a maximum range of 183 days.
  • Detailed activity history depends on Hubstaff retention and your organization plan.
  • The public Hubstaff API v2 does not expose task comments. For an integrated task, use project_type and remote_id to query the source system directly.
  • The Audit Log API is available only on Hubstaff Enterprise and requires an Owner or Organization Manager with permission to view other members' data. Audit events do not contain task comments.
  • All results remain limited by the permissions of the member represented by the configured Hubstaff credential.

Troubleshooting

hubstaff_configured: false

No Hubstaff organization token, access token, or refresh token is configured. Update .env, then recreate the container:

docker compose up -d --force-recreate

PAT returns invalid_grant or 401

The PAT may have been refreshed by another application, which invalidated the value stored by this server. Create a dedicated PAT for this deployment and make sure /data/token.json is stored on persistent storage.

Authorization request expired

Authorization requests are single-use and expire quickly. Close the old page, start Connect or Retry in ChatGPT, and submit the newly opened form once.

ChatGPT does not show the custom app option

Use the ChatGPT web app, enable Developer mode, confirm your plan supports custom apps, and ask the workspace administrator to allow developer-mode apps if necessary.

Task comments are required

The public Hubstaff API v2 has no task-comments endpoint. Call hubstaff_get_task, inspect project_type and remote_id, then query the source system—for example GitHub, Jira, or Asana. Adding a scope cannot create an endpoint that Hubstaff does not expose.

Audit Log returns 403

Audit Log access requires Hubstaff Enterprise. The credential must also represent an Owner or Organization Manager with permission to view other members' data.

Inspect the container

docker compose ps
docker compose logs --tail=100
curl http://localhost:3000/health

Development

npm ci
npm run typecheck
npm test
npm run build

The project uses TypeScript, Express, the official MCP TypeScript SDK, JOSE/JWT, Vitest, and a multi-stage Docker build.

Documentation

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured