BambooHR MCP
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., "@BambooHR MCPShow me all employees in the Engineering department."
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.
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.

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/mcpCheck it is alive:
curl http://localhost:3000/healthFor a local desktop client instead:
npm run start:stdioRelated MCP server: Rippling MCP Server
Configuration
Everything is environment-driven. See .env.example for the annotated list.
Variable | Required | Default | Purpose |
| yes | — | API token. Carries exactly the permissions of the user who created it. |
| yes | — | Subdomain of your BambooHR URL ( |
| no | — | Default employee for self-service tools. |
| for public deploys | — | Shared secret required as |
| no |
| Master switch for every record-changing tool. |
| no | all | Comma-separated allowlist of tool names. |
| no |
| Let callers pass their own token via |
| no |
| Cap on a single tool response. |
| no |
|
|
| no |
| HTTP bind address. |
Creating the API token
Log in to BambooHR and open your profile menu (bottom-left).
Choose API Keys → Add New Key.
Name it (e.g. "Copilot Studio agent") and click Generate Key.
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-mcpSet 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:
Go to your agent's Tools page → Add a tool → New tool → Custom connector.
In Power Apps, choose New custom connector → Import OpenAPI file and select the YAML.
On the Security step, the definition declares an API key in the
X-API-Keyheader. Supply the value of yourMCP_API_KEYwhen you create the connection.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 |
| Search the directory by name, email, title, department, or location. Paged. |
| One employee's record, with an optional explicit field list. |
| Every field id in the account — discover before reading or writing. |
| Allowed values for list fields (department, division, location…). |
| Historical tables available on employee records. |
| Rows of one table: job history, compensation, employment status. |
| BambooHR user accounts and access levels — for access reviews. |
| Records inserted/updated/deleted since a timestamp. |
| Who is out over a date range, company-wide. |
| Requests filtered by range, status, employee, type — the approval queue. |
| Projected balances for an employee as of a date. |
| Configured time-off types. |
| Accrual policies. |
| Run a saved company report by ID. |
| Ad-hoc report over any field list. |
| Time-tracking projects and tasks. |
| Timesheet entries over a date range, one or many employees. |
| Job openings with applicant counts. |
| The candidate pipeline, filtered by job, status, or search. |
| Full detail of one application, including answers and status history. |
| Configured applicant statuses and their IDs. |
| An employee's document categories and file metadata. |
| Company-wide document categories and files. |
| Download a document, base64-encoded. Small files only. |
| How this server is configured and which tools it exposes. |
Write tools (only registered when BAMBOOHR_ENABLE_WRITES=true):
Tool | What it does |
| Create an employee record. |
| Update fields on an existing employee. |
| Append a promotion, raise, or status change to a historical table. |
| Approve, deny, or cancel a time-off request. |
| Log hours against a project and task. |
| Advance, reject, or hire a candidate. |
| Record interview feedback against an application. |
| Attach a document to an employee, supplied base64-encoded. |
| Rename, recategorise, or reshare a document. |
| 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 viaBAMBOOHR_ALLOW_TOKEN_HEADER.Read-only by default. Write tools are withheld from
tools/listentirely 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_BYTESreturns 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_TOOLSnarrows 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 tomain, 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 |
| Target Web App. Defaults to |
| Deployment slot. Defaults to |
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 CILayout:
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 testsThe 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.logcorrupts the stream. Diagnostics go toconsole.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()insrc/tools/index.tshandles this.
License
MIT. See LICENSE.
Contributors
Encore Shao (github.com/encoreshao)
This server cannot be installed
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 Servers
- Alicense-qualityDmaintenanceEnables 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.1081MIT
- AlicenseBqualityDmaintenanceConnects 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.19183MIT
- Alicense-qualityFmaintenanceEnables 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.10MIT
- Flicense-qualityDmaintenanceProvides 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.
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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