@opthr/mcp-server
v0.5.0
Published
Model Context Protocol server for OptHR — Compensation, Pay-Equity, Skill Gap and Rapporto Biennale agents for Claude Desktop, Cursor, and other MCP clients.
Maintainers
Readme
@opthr/mcp-server
Model Context Protocol server for OptHR — exposes the OptHR product surfaces plus the Compensation, Pay-Equity, Skill Gap and Rapporto Biennale agents to MCP-aware clients (Claude Desktop, Cursor, etc.).
This is a thin Node.js wrapper around the OptHR FastAPI backend. All tools call the same endpoints the OptHR web app uses — there is no separate "MCP backend". The MCP cannot run analyses without a reachable hosted (or local) OptHR backend.
Install
npx -y @opthr/mcp-server@latestRequires Node 18+.
Tools
The public MCP surface is deliberately small — 6 tools. Read-only browsing (samples, schemas, product URLs, recent jobs) is exposed as resources, not tools.
| Tool | What it does |
|---|---|
| opthr_health | Sanity-check connectivity and active LLM |
| opthr_list_employees | Browse the tenant employee roster aggregated from completed jobs |
| opthr_compensation | One-shot compensation + pay-equity report for one employee |
| opthr_skill_gap | One-shot skill-gap report from a competence-profile document or sample |
| opthr_pay_equity | Payroll equity / Rapporto Biennale from a payroll CSV |
| opthr_email_report | Email a completed report when email delivery is configured |
Resources
| URI | Mime | What it gives clients |
|---|---|---|
| opthr://dashboard | json | Current dashboard URL + API base |
| opthr://figma | json | Configured Figma handoff URL |
| opthr://product-surfaces | json | Dashboard / landing / API / Figma metadata |
| opthr://samples | json | List of bundled sample documents |
| opthr://schemas | json | JobState, JobModule, DocumentType, UserRole enums, review payloads, error codes |
| opthr://tool-guide | md | When to use which tool |
| opthr://recent-jobs | json | Live: last 20 analyses for the tenant |
| opthr://sample-payroll-csv | csv | Minimal payroll CSV template |
| opthr://rapporto-biennale-template | json | Legge 162/2021 column layout |
Prompts
opthr-quick-comp— one-shot compensation + pay-equity for a single employeeopthr-explain-job— pull a job and summarise what was computedopthr-rapporto-biennale— Rapporto Biennale workflow including trasmissioneopthr-calibrate-team— loop comp analyses for a team
Local setup (against a backend on your laptop)
# Start the OptHR FastAPI backend
cd backend && uvicorn HR_Suite_Agents.api.app:app --reload --port 8765
# Run the MCP (stdio)
cd mcp-server
npm install
npm test # smoke-tests the backend connection
npm startClaude Desktop config (~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"opthr": {
"command": "npx",
"args": ["-y", "@opthr/mcp-server@latest"],
"env": {
"OPTHR_API_BASE": "http://localhost:8765",
"OPTHR_DEV_ROLE": "admin_hr",
"OPTHR_DEV_TENANT_ID": "tenant_demo"
}
}
}
}Hosted setup (production / customer demos)
For the current OptHR pilot backend, use:
OPTHR_API_BASE=https://opthrr.onrender.comCreate the API key from the OptHR dashboard:
- Open
https://opthrr.onrender.com/prototype/OptHR%20Redesign.html. - Log in as a tenant admin.
- Go to
Integrate -> REST API. - Click
Generate key. - Use the generated
optkey_live_...value asOPTHR_API_KEY.
{
"mcpServers": {
"opthr": {
"command": "npx",
"args": ["-y", "@opthr/mcp-server@latest"],
"env": {
"OPTHR_API_BASE": "https://opthrr.onrender.com",
"OPTHR_API_KEY": "optkey_live_xxx",
"OPTHR_DOCUMENT_SEARCH_ROOT": "/Users/<you>/Documents/OptHR"
}
}
}
}The hosted backend must:
- Be reachable at
OPTHR_API_BASE. - Accept
X-API-Key— generate keys from the dashboard/API so they carry tenant scope and role. - Enforce tenant scoping and rate limits on every endpoint the MCP touches.
See docs/MCP_PLATFORM_READINESS.md for the production checklist.
Document handling
Analysis tools accept three ways to provide source documents:
document_url—http://,https://, orfile://URL.document_path— absolute local path, or a path relative toOPTHR_DOCUMENT_SEARCH_ROOT.use_sample_data: true— explicit opt-in to the backend's bundled demo files.
Sample data is never attached implicitly. If neither document_url, document_path, nor use_sample_data is supplied, the tool returns state: needs_document and a dashboard URL for manual upload.
opthr_run_skill_gap and opthr_run_compensation run the same backend preflight used by the dashboard after a document is attached. That means the backend reads the PDF, extracts fields such as employee name, role, review period, gender, and RAL when possible, and starts only when the job is complete. If anything is still missing, the tool returns state: needs_input with missing_requirements instead of failing with a raw validation error.
If OPTHR_DOCUMENT_SEARCH_ROOT is set, any resolved document_path (and any file:// URL) must live inside that directory after realpath resolution. Symlink escape attempts return DOCUMENT_NOT_FOUND.
Environment variables
| Var | Default | What |
|---|---|---|
| OPTHR_API_BASE | http://localhost:8765 | FastAPI backend base URL |
| OPTHR_API_KEY | — | Sent as X-API-Key (hosted/production) |
| OPTHR_BEARER_TOKEN | — | Sent as Authorization: Bearer … |
| OPTHR_DEV_ROLE | admin_hr | Legacy fallback X-Role (local dev only) |
| OPTHR_DEV_TENANT_ID | tenant_demo | Legacy fallback X-Tenant-ID (local dev only) |
| OPTHR_DOCUMENT_SEARCH_ROOT | — | Restrict document_path to this directory |
| OPTHR_DASHBOARD_URL | ${OPTHR_API_BASE}/OptHR%20Redesign.html#/app | Dashboard URL exposed via resources |
| OPTHR_LANDING_URL | https://opthr-landing-site.vercel.app/ | Public landing URL |
| OPTHR_FIGMA_URL | — | Optional Figma handoff URL |
| OPTHR_FIGMA_URL_FILE | — | Optional file containing the Figma handoff URL. If unset, the server also checks .opthr-figma-url in the MCP folder or repo root. |
| OPTHR_FIGMA_STATUS | inferred | Optional status override for opthr://figma, e.g. reauth_required while the Figma connector needs login. |
| OPTHR_REPORT_URL | ${OPTHR_API_BASE}/prototype/OptHR%20Redesign.html#/app/agents/skill-gap | Canonical app report URL that the Figma handoff should mirror. |
| OPTHR_TELEMETRY_URL | — | Optional HTTPS endpoint to POST one JSON line per tool call (fire-and-forget). |
| OPTHR_TELEMETRY_AUTH | — | Optional Authorization header value sent with each telemetry POST (e.g. Bearer abc). |
Error codes
The MCP returns structured errors with one of these codes (also surfaced via opthr://schemas):
| Code | Meaning |
|---|---|
| BACKEND_UNREACHABLE | The MCP can't reach OPTHR_API_BASE at all. |
| AUTH_FAILED | Backend returned 401/403 — API key, bearer, or role missing/invalid. |
| DOCUMENT_NOT_FOUND | document_url / document_path resolution failed. |
| INVALID_DOCUMENT_TYPE | A scheme or type check rejected the input. |
| JOB_TIMEOUT | opthr_wait_for_job exhausted its deadline. |
| BACKEND_ERROR | Any other non-2xx backend response. |
See docs/MCP_ERROR_CATALOG.md for symptoms, root-cause analysis, and runbook steps for each code (plus the 429 rate-limit response).
Observability
The MCP logs one structured JSON line per tool call to stderr (stdout is reserved for the MCP transport):
{"ts":"…","svc":"opthr-mcp-server","version":"0.2.2","tenant_id":"tenant_demo",
"backend_host":"api.opthr.io","event":"tool_call","tool":"opthr_run_compensation",
"outcome":"ok","latency_ms":1820}PII, salaries, raw documents, and request/response bodies are never logged — by design, only the tool name, latency, outcome, and error code.
Telemetry sink
Set OPTHR_TELEMETRY_URL to an HTTPS endpoint to also POST each line, fire-and-forget. The payload is exactly the JSON shown above. Telemetry failures (HTTP errors, DNS failures, timeouts) are swallowed — they never affect tool calls.
// claude_desktop_config.json
{
"mcpServers": {
"opthr": {
"command": "npx",
"args": ["-y", "@opthr/mcp-server@latest"],
"env": {
"OPTHR_API_BASE": "https://<hosted-api>",
"OPTHR_API_KEY": "optkey_live_xxx",
"OPTHR_TELEMETRY_URL": "https://telemetry.opthr.io/mcp",
"OPTHR_TELEMETRY_AUTH": "Bearer <ingest-token>"
}
}
}
}Publishing
# from mcp-server/
npm version patch # → 0.2.x
npm publish --access publicfiles in package.json already pins the npm tarball to src/, assets/, and README.md.
License
MIT
