@codegame.dev/careerflow-mcp
v1.9.0
Published
MCP server exposing CareerFlow tasks, CRM, HR, and workplace tools to AI assistants
Readme
CareerFlow MCP Server
Connect an AI assistant to CareerFlow so it can read and manage your tasks, track your time, message colleagues, work CRM deals, handle HR requests, and more — on your behalf.
Works with any MCP-capable client — Claude Code, Claude Desktop, and others.
The server holds no privileges of its own. It calls the same REST API the web app uses, authenticated as you, so your existing role still decides what it can do — and the token's permissions narrow that further.
Quick start
The one-command way
npx @codegame.dev/careerflow-mcp initThe setup wizard asks for your CareerFlow host and access token, detects which MCP clients you have installed, and writes the configuration into Claude Code, Claude Desktop, ZCode (Z.ai), or Cursor for you — or prints a ready-to-paste JSON block for any other client. Token input is hidden as you type; it is never passed on the command line.
Flags prefill the answers, so an onboarding page can print a one-liner per company or client — only the token is still asked for (hidden):
npx @codegame.dev/careerflow-mcp init --base-url https://careerflow.codegame.dev/site-api --name acme --client claude-code
npx @codegame.dev/careerflow-mcp init --base-url https://careerflow.codegame.dev/site-api --name acme --company-id 24 --client zcode--base-url— the CareerFlow host, so it is never typed by hand--name— the server's name in the client config (defaultcareerflow); use one name per company or install, e.g.acme,acme-staging--company-id— optional default company--client—claude-code,claude-desktop,zcode,cursor, orother
None of these flags is secret, so the one-liner is safe to publish. The access token is never accepted as a flag — argv is readable by other processes on the machine — so the wizard always prompts for it with hidden input.
Removing it again
init is reversible. The remove command deletes the server entry it created
(no token needed, so it works in scripts too):
npx @codegame.dev/careerflow-mcp remove --client claude-code --name acme
npx @codegame.dev/careerflow-mcp remove --client zcode --name acmeWithout flags it asks which client to clean up. For Claude Code it runs
claude mcp remove --scope user; for Claude Desktop, ZCode, and Cursor it
deletes the entry from the client's JSON config, leaving everything else in the
file untouched. Other clients: delete the pasted block by hand.
The manual way
Create an access token.
In CareerFlow: Settings → Access Tokens → Create access token.
Give it a name, an expiry, and only the permissions the assistant needs.
The token is shown once. It is stored hashed and cannot be recovered afterwards. Copy it before closing the dialog.
Add it to your client.
Claude Code
claude mcp add careerflow \
--env CAREERFLOW_BASE_URL=https://careerflow.codegame.dev/site-api \
--env CAREERFLOW_TOKEN=cf_pat_xxxxxxxx \
--env CAREERFLOW_COMPANY_ID=24 \
-- npx -y @codegame.dev/careerflow-mcpClaude Desktop — add to claude_desktop_config.json:
{
"mcpServers": {
"careerflow": {
"command": "npx",
"args": ["-y", "@codegame.dev/careerflow-mcp"],
"env": {
"CAREERFLOW_BASE_URL": "https://careerflow.codegame.dev/site-api",
"CAREERFLOW_TOKEN": "cf_pat_xxxxxxxx",
"CAREERFLOW_COMPANY_ID": "24"
}
}
}
}Config file locations:
- macOS —
~/Library/Application Support/Claude/claude_desktop_config.json - Windows —
%APPDATA%\Claude\claude_desktop_config.json - Linux —
~/.config/Claude/claude_desktop_config.json
ZCode (Z.ai) — add to ~/.zcode/cli/config.json (note the nested
mcp.servers, different from most clients' mcpServers):
{
"mcp": {
"servers": {
"careerflow": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@codegame.dev/careerflow-mcp"],
"env": {
"CAREERFLOW_BASE_URL": "https://careerflow.codegame.dev/site-api",
"CAREERFLOW_TOKEN": "cf_pat_xxxxxxxx",
"CAREERFLOW_COMPANY_ID": "24"
}
}
}
}
}For a project-shared server use <repo>/.zcode/config.json instead — but the
token would then sit in a versioned file; prefer user scope.
Restart the client. Ask it "what are my tasks?" to confirm the connection.
Configuration
| Variable | Required | Description |
| --- | --- | --- |
| CAREERFLOW_BASE_URL | yes | API root, e.g. https://api.example.com |
| CAREERFLOW_TOKEN | yes | Your personal access token (cf_pat_…) |
| CAREERFLOW_COMPANY_ID | no | Default company, so tools need no company_id per call |
| CAREERFLOW_TIMEOUT_MS | no | Request timeout, default 30000 |
| CAREERFLOW_GROUPS | no | Register only these comma-separated tool groups |
| CAREERFLOW_EXCLUDE_GROUPS | no | Register everything except these comma-separated groups |
| CAREERFLOW_ALL_TOOLS | no | Set to 1 to register every tool, even ones your role or token cannot reach |
| CAREERFLOW_KEEP_READ_TOOLS | no | Set to 1 to keep the read-only tools run_company_sql otherwise replaces |
The token is read only from the environment, never from command-line arguments — argv is readable by other processes on the same machine.
What you can ask for
Daily work
- "What am I working on today?" · "Anything overdue?"
- "Mark the login-bug task done."
- "Start a timer on task 412." · "Log 45 minutes on the report task."
- "Add a comment on task 88 saying the API is now deployed."
- "Add a checklist to task 12 with three items." · "Attach this link to task 40."
Supervising a team
- "What is Sara working on?" · "Show everything pending review."
- "Create a task 'Prepare Q3 report' in Sprint 12, assign it to Ali, due Friday, high priority."
- "Move task 91 to Done." · "Accept task 77 and score it 4.5."
- "Create a project called 'Website Redesign' and add Sara as manager."
Sales & CRM
- "Show my open deals." · "Create a lead for Acme Corp from the website form."
- "Move the Acme deal to Negotiation and set the amount to $12,000."
- "Log a call with the Beta Inc contact." · "What's in my pipeline this month?"
HR & people ops
- "Show my latest payroll slip." · "Accept my performance review."
- "List open job applications for the Backend Engineer posting."
- "Am I registered for the current loan lottery?" · "Check in my mood: 4, feeling good today."
- "Reserve lunch for tomorrow."
Around the work
- "Any new announcements?" · "Message Reza that the deploy finished."
- "Create a chat group for the launch team with Sara, Ali, and Reza."
- "Open a support ticket about the slow dashboard load."
Naming: the API vs the UI
The tools keep the backend API's names, and the UI names the same things differently — easy to trip over when relaying what a user asked for:
| API (and these tools) | UI |
| --- | --- |
| project of type special | "project" (پروژه) |
| project of type normal — or any non-special type | "board" (تابلو) |
| board — a column of tasks inside a project | "list" (لیست) |
So when a user asks for a "board", they usually mean an API project (find it
with list_projects, checking each row's type); when they ask for a "list",
they mean an API board (list_boards). The server's instructions string
spells this out to the model too.
Tools
167 tools. The everyday surface (only part of it can be registered):
| Group | Tools |
| --- | --- |
| Identity & structure | whoami, list_companies, list_projects, list_boards, list_project_members, find_user |
| Tasks | list_my_tasks, search_tasks, get_task, create_task, update_task, complete_task, assign_task, move_task, review_task, list_task_comments, add_task_comment |
| Kanban (boards, labels, checklists, attachments) | create_board, edit_board, archive_board, list_labels, create_label, delete_label, add_task_checklist, add_checklist_item, complete_checklist_item, delete_checklist, add_task_link_attachment, delete_task_attachment, archive_task, duplicate_task, list_recurring_tasks, create_recurring_task, delete_recurring_task, list_kanban_activity |
| Projects | create_project, update_project, archive_project, project_stats, invite_project_member, authorize_project_member |
| Time | start_timer, stop_timer, log_time, list_task_time |
| Chat | list_chats, read_messages, send_message, create_chat_group, edit_chat_group, manage_chat_group_members, list_chat_group_members, leave_chat_group, mute_chat, edit_chat_message, delete_chat_message, mark_message_seen |
| Workplace | list_announcements, list_surveys, list_my_attendance |
| Company & user admin | create_user, update_user, set_user_status, set_user_role, get_user, list_roles, get_organization_chart, get_company, list_organization_units |
| CRM — contacts, deals, activities | crm_search_contacts, crm_get_contact, crm_create_contact, crm_update_contact, crm_delete_contact, crm_contact_timeline, crm_search_deals, crm_get_deal, crm_create_deal, crm_update_deal, crm_delete_deal, crm_deal_stage_summary, crm_add_deal_next_step, crm_list_deal_line_items, crm_add_deal_line_item, crm_delete_deal_line_item, crm_search_activities, crm_create_activity, crm_complete_activity, crm_delete_activity, crm_dashboard_summary, crm_dashboard_pipeline |
| CRM — pipelines & config | crm_list_pipelines, crm_create_pipeline, crm_delete_pipeline, crm_list_stages, crm_create_stage, crm_delete_stage, crm_list_lead_sources, crm_create_lead_source, crm_list_tags, crm_create_tag, crm_assign_tag, crm_list_custom_fields, crm_create_custom_field, crm_settings |
| HR — payroll, discipline, careers, onboarding, cafeteria | list_my_payrolls, respond_to_payroll, list_my_performance_reviews, respond_to_performance_review, list_disciplinary_notes, estimate_disciplinary_penalty, create_disciplinary_note, list_administrative_notes, get_administrative_note, create_administrative_note, list_careers, list_career_requests, update_career_request_status, get_onboarding_checklist, set_onboarding_stage, list_food_schedules, reserve_food, list_my_food_reservations, mood_checkin |
| Benefit fund (loans & lotteries) | benefit_fund_my_dashboard, benefit_fund_hr_dashboard, list_benefit_fund_loan_types, list_benefit_fund_loans, request_benefit_fund_loan, update_benefit_fund_loan_status, pay_benefit_fund_loan_installment, list_benefit_fund_lotteries, register_for_lottery, respond_to_lottery_win, benefit_fund_settings, list_benefit_fund_membership_requests, request_benefit_fund_membership, review_benefit_fund_membership_request |
| Forms | list_forms, get_form, list_form_submissions, update_form_submission_status, message_form_submission |
| CMS | list_posts, get_post, create_post, update_post, delete_post, list_custom_pages, get_custom_page |
| Dev tickets (support) | list_dev_tickets, get_dev_ticket, create_dev_ticket, reply_to_dev_ticket |
| Misc (addresses, surveys, announcements, billing) | list_my_addresses, add_address, create_survey, delete_survey, list_survey_answers, answer_survey, create_announcement, delete_announcement, list_transactions, list_payment_orders |
| Company SQL (reporting views) | list_company_sql_schema, run_company_sql |
Registering only the tool groups you need
Every registered tool's schema lands in the model's context, so a client that loads all 167 tools spends real tokens on descriptions before you ask anything. If the assistant only works one domain — a sales assistant, a task bot — register just that slice:
{
"mcpServers": {
"careerflow": {
"command": "npx",
"args": ["-y", "@codegame.dev/careerflow-mcp"],
"env": {
"CAREERFLOW_BASE_URL": "https://careerflow.codegame.dev/site-api",
"CAREERFLOW_TOKEN": "cf_pat_xxxxxxxx",
"CAREERFLOW_GROUPS": "workspace,tasks,time"
}
}
}
}Group names (matching rows in the table above, crm covering both CRM rows,
chat both chat modules):
workspace, tasks, kanban, projects, time, chat, workplace,
admin, crm, hr, benefit-funds, forms, cms, dev-tickets, misc,
company-sql
CAREERFLOW_GROUPS is an allowlist; CAREERFLOW_EXCLUDE_GROUPS removes the
named groups from the full set. An unknown name fails at startup with the valid
list. Group filtering is a context-size control only — the token's scopes still
decide what actually works.
Tools you cannot use are not registered
On top of the group filter, the server asks the API at startup what your role in
the company and your token's scopes actually allow (GET users/capabilities),
and registers only the tools that pass. A sales rep never sees the payroll
tools; a token scoped to tasks:read never sees create_task. The startup line
on stderr says how many were hidden:
careerflow-mcp connected (api: …, groups: …, tools: 57 registered / 110 hidden by role + token scopes)This is a context-size and clarity measure, not a security boundary — the API
enforces the same rules on every call either way. It degrades safely: an older
API without the endpoint, an unreachable host, or a company that cannot be
resolved (no CAREERFLOW_COMPANY_ID and no company on your account) registers
everything, exactly as before. Set CAREERFLOW_ALL_TOOLS=1 to opt out.
Because the filter is computed once at startup, a role or token change takes effect when the client restarts the server.
If you can run company SQL, the read-only tools step aside
run_company_sql answers any question the v_ai_* views cover — with filters,
joins and aggregates no fixed tool offers — so for the users who have it (the
company owner, a full-access role, CareerFlow staff) the 54 read-only tools that
duplicate it are not registered. On an owner account that is 167 tools down to
113, and "which projects have the most overdue tasks" becomes one query instead
of a listing tool that cannot answer it at all.
That includes the listings a write uses to resolve an id — list_projects,
list_boards, search_tasks, find_user, crm_list_pipelines,
crm_search_contacts and the rest. Each is backed by a view, so for these users
looking up an id is simply another query, and the assistant is told which views
to use for it.
What stays, SQL or no SQL:
- everything the views do not cover — payroll and performance rows (only their groups are exposed), the whole benefit fund, dev tickets, billing, addresses, CRM settings
- chat, which is a live conversation rather than a report
whoami, and the SQL tools themselves
Users who cannot run SQL keep every read tool. Set CAREERFLOW_KEEP_READ_TOOLS=1
to keep them anyway.
Permissions
Each token carries an explicit permission list. A tool call that needs a permission the token lacks fails with a message naming the missing one — create a new token with it added. The full list is shown on the token creation screen in Settings → Access Tokens → Create access token; it's summarized here too.
Every scope comes in a :read/:write pair; some also accept a :own suffix
(e.g. tasks:write:own) that narrows the write to records the token's owner
created, owns, or is assigned to — ask CareerFlow support or check the token
creation screen if you want that narrower grant.
| Group | Scopes | Covers |
| --- | --- | --- |
| Personal | profile, files | your own profile, sessions, addresses, onboarding; file/image/video uploads |
| Work | tasks, projects, time, chat | kanban boards/tasks/checklists/labels, projects & membership, time tracking, chat & groups |
| CRM | crm_contacts, crm_deals, crm_activities, crm_settings | contacts/leads, deals, activities, and CRM configuration (pipelines, stages, tags, custom fields) |
| Company | company, users, roles, attendance | company profile & org chart, user admin, roles, attendance |
| HR | surveys, announcements, forms, payroll, performance, benefit_fund, notes, careers | surveys, announcements, form builder, payroll/performance slips, benefit fund loans & lotteries, disciplinary/administrative notes, recruiting |
| Content | cms | posts, custom pages, categories |
| Billing | billing, dev_tickets | payment/product orders, transactions, support tickets |
| Platform (staff-only) | admin | charts, logs, and other platform-admin surfaces — only issuable to CareerFlow staff accounts |
Grant the narrowest set that does the job. A read-only assistant needs only
profile:read, tasks:read, and projects:read.
The Company SQL tools are gated on top of scopes: the API re-authorizes each call and only a company owner or a full-access user can run one.
Security notes
- A token acts as you, limited to its permissions. Treat it like a password.
- It cannot exceed your own role — the API enforces the same checks as the web app.
- A token cannot create, edit, or manage another token — token management is a session-only action in the web app, so a leaked token cannot widen itself.
- Revoke any token you no longer use: Settings → Access Tokens → Revoke. Revocation takes effect immediately.
- Several tools deliver something real and cannot be undone from inside the
tool call —
send_message,create_administrative_note,message_form_submissionsend to real people;create_user,set_user_status,set_user_rolechange real accounts;request_benefit_fund_loan,update_benefit_fund_loan_status,pay_benefit_fund_loan_installment, andrespond_to_lottery_wintouch real money. Their descriptions ask the assistant to confirm details with you first — grant the underlying:writescope only if you want that capability at all.
Running from source
git clone https://gitlab.com/dev.codegames/careerflow-mcp
cd careerflow-mcp
npm install
npm run buildPoint your client at the built entry point:
{
"mcpServers": {
"careerflow": {
"command": "node",
"args": ["/absolute/path/to/careerflow-mcp/dist/index.js"],
"env": { "CAREERFLOW_BASE_URL": "…", "CAREERFLOW_TOKEN": "…" }
}
}
}Docker
docker build -t careerflow-mcp .{
"mcpServers": {
"careerflow": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "CAREERFLOW_BASE_URL",
"-e", "CAREERFLOW_TOKEN",
"-e", "CAREERFLOW_COMPANY_ID",
"careerflow-mcp"
],
"env": {
"CAREERFLOW_BASE_URL": "https://careerflow.codegame.dev/site-api",
"CAREERFLOW_TOKEN": "cf_pat_xxxxxxxx",
"CAREERFLOW_COMPANY_ID": "24"
}
}
}
}-i is required: the server speaks MCP over stdin/stdout.
Troubleshooting
"CareerFlow rejected the access token" — the token is revoked, expired, or mistyped. Check it in Settings → Access Tokens, and create a new one if needed.
"The access token does not carry the scope this action needs" — the token is missing a permission. Create a new one with that permission added.
"Company ID is not valid" — your role does not permit that read, or
CAREERFLOW_COMPANY_ID points at a company you do not belong to. Run whoami to
see which company you are in.
Tools do not appear — restart the client after editing its config. Verify the server starts on its own:
CAREERFLOW_TOKEN=cf_pat_xxx CAREERFLOW_BASE_URL=https://host npx @codegame.dev/careerflow-mcpIt should print careerflow-mcp connected (api: …) to stderr and wait. Ctrl-C to exit.
Requirements
- Node.js 18 or newer
- A CareerFlow instance with personal access tokens enabled (migration 99 applied)
Development
npm run dev # tsc --watch
npm run typecheck # types only
npm run format # prettier
npm run smoke # boot the built server and verify tool registrationPublishing
npm run prepublishOnly (typecheck, build, smoke) runs automatically on
npm publish. On GitLab, the publish job in .gitlab-ci.yml publishes when
a v* tag is pushed and the job is confirmed manually; it needs NPM_TOKEN
(set it as a masked CI/CD variable under Settings > CI/CD > Variables):
git tag v1.1.0 && git push origin v1.1.0
# then confirm the `publish` job in CI/CD > PipelinesOr publish from a machine that is already verified with npm: npm publish.
Compatibility
This client tracks the CareerFlow REST API. Behaviours it deliberately works around, worth knowing before changing a tool:
kanbans/tasks/edit,crm/deals/edit, andusers/editall replace the whole row — a field left out falls back to a default (or null) rather than keeping its current value.update_task,crm_update_deal, andupdate_usereach read the record first and merge onto it, so a partial change cannot silently clear untouched fields. Apply the same read-then-merge pattern to any other endpoint discovered to have this behavior.- Several endpoints reject an unscoped company-wide read unless the caller holds an admin role, so tools pass the caller's own user id to mean "mine".
- A few
searchendpoints require a filter the API otherwise has no "all" value for — e.g.careers/requests/searchrequiresstatus.list_career_requestsreflects that by making the field required rather than optional.
