Skip to main content
Glama

BambooHR MCP (Admin)

An administrative Model Context Protocol server for BambooHR, in Node.js and TypeScript.

It runs in two modes from the same codebase:

  • Streamable HTTP — a remote server you host, for Microsoft Copilot Studio and any other remote MCP client.

  • stdio — a local process, for Claude Desktop, VS Code, and other desktop MCP clients.

35 tools cover the employee directory, employee records and their historical tables, hiring and the applicant pipeline, employee and company documents, time-off requests and balances, reports, BambooHR user accounts, and time tracking. Record-changing tools are off by default.

BambooHR MCP


Quick start

git clone https://github.com/encoreshao/bamboohr-mcp.git
cd bamboohr-mcp
npm install
cp .env.example .env    # fill in BAMBOOHR_TOKEN and BAMBOOHR_COMPANY_DOMAIN
npm run build
npm start               # HTTP on http://0.0.0.0:3000/mcp

Check it is alive:

curl http://localhost:3000/health

For a local desktop client instead:

npm run start:stdio

Related MCP server: Rippling MCP Server

Configuration

Everything is environment-driven. See .env.example for the annotated list.

Variable

Required

Default

Purpose

BAMBOOHR_TOKEN

yes

API token. Carries exactly the permissions of the user who created it.

BAMBOOHR_COMPANY_DOMAIN

yes

Subdomain of your BambooHR URL (acme in acme.bamboohr.com).

BAMBOOHR_EMPLOYEE_ID

no

Default employee for self-service tools.

MCP_API_KEY

for public deploys

Shared secret required as X-API-Key. Unset means no authentication.

BAMBOOHR_ENABLE_WRITES

no

false

Master switch for every record-changing tool.

BAMBOOHR_ALLOWED_TOOLS

no

all

Comma-separated allowlist of tool names.

BAMBOOHR_ALLOW_TOKEN_HEADER

no

false

Let callers pass their own token via X-BambooHR-Token.

BAMBOOHR_MAX_RESPONSE_BYTES

no

350000

Cap on a single tool response.

MCP_TRANSPORT

no

http

http or stdio. --stdio on the command line does the same.

PORT / HOST

no

3000 / 0.0.0.0

HTTP bind address.

Creating the API token

  1. Log in to BambooHR and open your profile menu (bottom-left).

  2. Choose API KeysAdd New Key.

  3. Name it (e.g. "Copilot Studio agent") and click Generate Key.

  4. Copy it immediately — BambooHR shows it once.

The key inherits the access level of the user who created it. A key made by a full admin can read compensation and terminate employees. Create it as a user whose permissions match what the agent should be able to do — that BambooHR user is your real permission boundary, not this server.


Connecting to Copilot Studio

Copilot Studio reaches MCP servers over Streamable HTTP only — it cannot launch a local stdio process. So the server has to be hosted somewhere with a public HTTPS address.

1. Deploy

CI/CD to Azure App Service is wired up — see Deployment below. To run it locally instead, a Dockerfile is included:

docker build -t bamboohr-mcp .
docker run -p 3000:3000 --env-file .env bamboohr-mcp

Set the environment variables as secrets wherever you host it — never bake the token into an image.

2. Create the custom connector

connector/copilot-studio-connector.yaml is a ready Swagger 2.0 definition. Replace the host line with your deployed hostname, then:

  1. Go to your agent's Tools page → Add a toolNew toolCustom connector.

  2. In Power Apps, choose New custom connectorImport OpenAPI file and select the YAML.

  3. On the Security step, the definition declares an API key in the X-API-Key header. Supply the value of your MCP_API_KEY when you create the connection.

  4. Create connector, then add it to your agent.

The critical line is x-ms-agentic-protocol: mcp-streamable-1.0 on the POST /mcp operation — that is what tells Copilot Studio to speak MCP rather than treat the endpoint as a plain REST action.

Copilot Studio's MCP onboarding wizard is the alternative route and takes the same URL and header.


Connecting a local MCP client

{
  "mcpServers": {
    "bamboohr": {
      "command": "node",
      "args": ["/absolute/path/to/bamboohr-mcp/dist/index.js", "--stdio"],
      "env": {
        "BAMBOOHR_TOKEN": "your_api_token_here",
        "BAMBOOHR_COMPANY_DOMAIN": "yourcompany",
        "BAMBOOHR_ENABLE_WRITES": "false"
      }
    }
  }
}

Tools

Read-only tools (always available):

Tool

What it does

bamboohr_search_employees

Search the directory by name, email, title, department, or location. Paged.

bamboohr_get_employee

One employee's record, with an optional explicit field list.

bamboohr_list_employee_fields

Every field id in the account — discover before reading or writing.

bamboohr_list_field_options

Allowed values for list fields (department, division, location…).

bamboohr_list_tables

Historical tables available on employee records.

bamboohr_get_employee_table

Rows of one table: job history, compensation, employment status.

bamboohr_list_users

BambooHR user accounts and access levels — for access reviews.

bamboohr_get_changed_employees

Records inserted/updated/deleted since a timestamp.

bamboohr_whos_out

Who is out over a date range, company-wide.

bamboohr_list_time_off_requests

Requests filtered by range, status, employee, type — the approval queue.

bamboohr_get_time_off_balances

Projected balances for an employee as of a date.

bamboohr_list_time_off_types

Configured time-off types.

bamboohr_list_time_off_policies

Accrual policies.

bamboohr_run_report

Run a saved company report by ID.

bamboohr_run_custom_report

Ad-hoc report over any field list.

bamboohr_list_projects

Time-tracking projects and tasks.

bamboohr_get_timesheet_entries

Timesheet entries over a date range, one or many employees.

bamboohr_list_job_openings

Job openings with applicant counts.

bamboohr_list_applications

The candidate pipeline, filtered by job, status, or search.

bamboohr_get_application

Full detail of one application, including answers and status history.

bamboohr_list_applicant_statuses

Configured applicant statuses and their IDs.

bamboohr_list_employee_files

An employee's document categories and file metadata.

bamboohr_list_company_files

Company-wide document categories and files.

bamboohr_download_employee_file

Download a document, base64-encoded. Small files only.

bamboohr_server_info

How this server is configured and which tools it exposes.

Write tools (only registered when BAMBOOHR_ENABLE_WRITES=true):

Tool

What it does

bamboohr_create_employee

Create an employee record.

bamboohr_update_employee

Update fields on an existing employee.

bamboohr_add_employee_table_row

Append a promotion, raise, or status change to a historical table.

bamboohr_set_time_off_request_status

Approve, deny, or cancel a time-off request.

bamboohr_submit_work_hours

Log hours against a project and task.

bamboohr_set_application_status

Advance, reject, or hire a candidate.

bamboohr_add_application_comment

Record interview feedback against an application.

bamboohr_upload_employee_file

Attach a document to an employee, supplied base64-encoded.

bamboohr_update_employee_file

Rename, recategorise, or reshare a document.

bamboohr_delete_employee_file

Permanently delete a document. Not recoverable.

Each write tool takes a required confirm argument that must be true. Field maps can be passed either as a fields object or as a fieldsJson string — use the string form from Copilot Studio, whose connector layer handles free-form objects poorly.

Compensation and offboarding

There are no dedicated tools for these; BambooHR stores both as historical tables on the employee record, so they go through the generic table tools. Read salary history with bamboohr_get_employee_table(employeeId, "compensation") and record a raise with bamboohr_add_employee_table_row. Terminations are a row in employmentStatus. Both are permission-gated by the API key's user.

Not available: Global Employment

BambooHR's Global Employment is an embedded EOR service delivered with Remote — hiring and onboarding begin in BambooHR, but payroll and benefits live in Remote's platform. It exposes no endpoints in the BambooHR v1 API, so there is nothing to build tools against. Integrating it would mean going to Remote's API as a separate service.


Security model

  • The server holds the BambooHR token. Callers authenticate to the server with MCP_API_KEY; they never see or supply the HR credential. Per-request tokens are possible but opt-in via BAMBOOHR_ALLOW_TOKEN_HEADER.

  • Read-only by default. Write tools are withheld from tools/list entirely when writes are disabled — the model is never told a capability exists that the server will refuse.

  • Nothing is shared between requests. Each HTTP request builds its own context, client, and MCP server instance, so one caller's credentials and employee context can never bleed into another's. There is no mutable global config.

  • Responses are capped. Anything over BAMBOOHR_MAX_RESPONSE_BYTES returns an actionable "narrow your query" error rather than a 500 KB payload that Copilot Studio would reject with an opaque HTTP 400.

  • Least privilege lives in BambooHR. BAMBOOHR_ALLOWED_TOOLS narrows the surface, but the token's own access level is the boundary that actually matters.

Set MCP_API_KEY before exposing the server publicly. It logs a warning at startup if you have not.


Deployment

Two GitHub Actions workflows:

  • .github/workflows/ci.yml — typecheck, build, and test on Node 20 and 22 for every push and pull request, plus a guard asserting the default build still gates its write tools.

  • .github/workflows/deploy.yml — on every push to main, builds a production zip, deploys it to Azure App Service, then fails the run if the app does not come up or if it comes up missing its BambooHR credentials or its API key. A deploy that lands but cannot serve traffic is reported as a failure, not a pass.

The deploy workflow targets the Web App BambooHRMCP (https://bamboohrmcp.azurewebsites.net) by default. Override it with an AZURE_WEBAPP_NAME repository variable.

1. Create the Web App

Skip this if the app already exists — creating it through the Portal's Deployment Center does the same thing.

az group create --name bamboohr-mcp-rg --location eastus

az appservice plan create \
  --name bamboohr-mcp-plan --resource-group bamboohr-mcp-rg \
  --is-linux --sku B1

az webapp create \
  --name BambooHRMCP --resource-group bamboohr-mcp-rg \
  --plan bamboohr-mcp-plan --runtime "NODE:20-lts"

az webapp config set \
  --name BambooHRMCP --resource-group bamboohr-mcp-rg \
  --startup-file "node dist/index.js"

2. Set app settings

These live in Azure, never in the repo. .env is for local development only and is gitignored.

az webapp config appsettings set \
  --name BambooHRMCP --resource-group bamboohr-mcp-rg \
  --settings \
    BAMBOOHR_TOKEN="<your-bamboohr-token>" \
    BAMBOOHR_COMPANY_DOMAIN="<your-subdomain>" \
    MCP_API_KEY="<your-long-random-key>" \
    BAMBOOHR_ENABLE_WRITES="false" \
    SCM_DO_BUILD_DURING_DEPLOYMENT="false" \
    WEBSITE_RUN_FROM_PACKAGE="1"

SCM_DO_BUILD_DURING_DEPLOYMENT=false matters: the zip already contains dist/ and production node_modules, and letting Oryx rebuild on the server would only introduce drift.

3. Authentication

The workflow deploys with the publish profile that Azure's Deployment Center stored in the repository as AZUREAPPSERVICE_PUBLISHPROFILE_F89A515B4E6341C788E87EFCEC7A991B. Nothing further to configure — connecting the app through the Portal already did it.

If you ever regenerate the publish profile, or wire up a different Web App, update that secret under Settings → Secrets and variables → Actions and change the name in deploy.yml to match.

Two optional repository variables:

Name

Purpose

AZURE_WEBAPP_NAME

Target Web App. Defaults to BambooHRMCP.

AZURE_WEBAPP_SLOT

Deployment slot. Defaults to production.

Also create an environment named production under Settings → Environments. Adding required reviewers there turns every deploy into an approval gate, which is worth doing for a server holding an HR admin token.

A publish profile is a long-lived credential with deploy rights sitting in GitHub. OIDC replaces it with a token minted per run. Create an Entra app registration, grant it Contributor on the Web App, and add federated credentials:

az ad app create --display-name bamboohr-mcp-deploy
# note the appId, then:
az ad sp create --id <APP-ID>

az role assignment create \
  --assignee <APP-ID> --role Contributor \
  --scope /subscriptions/<SUB-ID>/resourceGroups/bamboohr-mcp-rg

az ad app federated-credential create --id <APP-ID> --parameters '{
  "name": "github-main",
  "issuer": "https://token.actions.githubusercontent.com",
  "subject": "repo:aakarsh1t/BambooHR-MCP:ref:refs/heads/main",
  "audiences": ["api://AzureADTokenExchange"]
}'

The subject must match exactly how the workflow runs. Because the deploy job uses a GitHub Environment, add a second credential with subject repo:aakarsh1t/BambooHR-MCP:environment:production.

Then add secrets AZURE_CLIENT_ID, AZURE_TENANT_ID, and AZURE_SUBSCRIPTION_ID — none is a credential on its own — and in deploy.yml restore id-token: write to the deploy job's permissions, drop the publish-profile line, and add before the deploy step:

      - name: Sign in to Azure
        uses: azure/login@v3
        with:
          client-id: ${{ secrets.AZURE_CLIENT_ID }}
          tenant-id: ${{ secrets.AZURE_TENANT_ID }}
          subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

4. Point Copilot Studio at it

connector/copilot-studio-connector.yaml already points at bamboohrmcp.azurewebsites.net. Change host: if you deploy elsewhere, then import it as described above.


Development

npm run dev          # HTTP, ts-node, no build step
npm run dev:stdio    # stdio, ts-node
npm run typecheck    # tsc --noEmit
npm run build        # emit dist/
npm test             # run test/ against the built output
npm run verify       # typecheck + build + test, same as CI

Layout:

src/
  index.ts          entry point and transport selection
  http.ts           express app, auth, stateless /mcp endpoint
  server.ts         builds an McpServer for one request context
  config.ts         environment loading and per-request context
  tools/index.ts    tool definitions, schemas, and gating
  apis/bamboohr.ts  typed BambooHR v1 client
  utils/            response shaping and models
test/
  smoke.test.js     transport, auth, gating, and error-shaping tests

The tests run against dist/, so build first — npm run verify does both in order. They use throwaway credentials and need no BambooHR account: everything asserted is about transport, gating, and error shaping.

Adding a tool means one define(...) call in src/tools/index.ts and a method on BambooHRClient in src/apis/bamboohr.ts. Give it readOnlyHint: true only if it genuinely does not change anything — that flag is what decides whether writes gating applies.

Two notes for anyone extending this:

  • In stdio mode nothing may be written to stdout — it is the JSON-RPC channel and a stray console.log corrupts the stream. Diagnostics go to console.error.

  • Zero-argument tools go through the four-argument server.tool() form. SDK 1.11 decides whether an argument is a Zod shape by checking for a ZodType value, so an empty {} schema gets mistaken for the annotations object and the real annotations get called as the handler. registerTool() in src/tools/index.ts handles this.


License

MIT. See LICENSE.

Contributors

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    -
    quality
    D
    maintenance
    Enables AI assistants to interact with BambooHR's API through natural language queries. Provides access to employee data, time off management, company files, and HR operations with comprehensive tools for workforce management.
    108
    1
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    Connects AI agents to the Rippling HR/IT/Finance platform to query employees, manage leave requests, and view organizational structures. It provides eighteen tools for accessing company data, employee details, and administrative activities through the Rippling API.
    19
    18
    3
    MIT
  • A
    license
    -
    quality
    F
    maintenance
    Enables natural language interaction with BambooHR to manage employee records, time off, hiring, and benefits. It provides 74 tools and pre-built workflows to automate HR operations like onboarding, reporting, and performance tracking.
    10
    MIT
  • F
    license
    -
    quality
    D
    maintenance
    Provides 50+ tools for interacting with Rippling's HR platform, including employee management, payroll, time tracking, benefits, recruiting, learning, devices, groups, and custom objects, all through natural language.

View all related MCP servers

Related MCP Connectors

  • Connect AI to your Attio CRM. Manage contacts, companies, deals, and sales pipelines. Create tasks…

  • Connect your AI assistants to Keboola and expose your data, transformations, SQL queries, ...

  • SaaS intelligence for AI agents. 5 unified tools cover 1,000+ services with 91-96% token savings.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/aakarsh1t/BambooHR-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server