@codegame.dev/careerflow-mcp 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +398 -0
  3. package/dist/client.js +110 -0
  4. package/dist/client.js.map +1 -0
  5. package/dist/config.js +37 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/helpers.js +192 -0
  8. package/dist/helpers.js.map +1 -0
  9. package/dist/index.js +194 -0
  10. package/dist/index.js.map +1 -0
  11. package/dist/setup.js +296 -0
  12. package/dist/setup.js.map +1 -0
  13. package/dist/tools/admin.js +232 -0
  14. package/dist/tools/admin.js.map +1 -0
  15. package/dist/tools/benefit-funds.js +243 -0
  16. package/dist/tools/benefit-funds.js.map +1 -0
  17. package/dist/tools/chat-extra.js +172 -0
  18. package/dist/tools/chat-extra.js.map +1 -0
  19. package/dist/tools/chat.js +125 -0
  20. package/dist/tools/chat.js.map +1 -0
  21. package/dist/tools/cms.js +145 -0
  22. package/dist/tools/cms.js.map +1 -0
  23. package/dist/tools/crm-admin.js +269 -0
  24. package/dist/tools/crm-admin.js.map +1 -0
  25. package/dist/tools/crm.js +632 -0
  26. package/dist/tools/crm.js.map +1 -0
  27. package/dist/tools/dev-tickets.js +94 -0
  28. package/dist/tools/dev-tickets.js.map +1 -0
  29. package/dist/tools/forms.js +105 -0
  30. package/dist/tools/forms.js.map +1 -0
  31. package/dist/tools/hr.js +517 -0
  32. package/dist/tools/hr.js.map +1 -0
  33. package/dist/tools/kanban-extra.js +403 -0
  34. package/dist/tools/kanban-extra.js.map +1 -0
  35. package/dist/tools/misc.js +159 -0
  36. package/dist/tools/misc.js.map +1 -0
  37. package/dist/tools/projects.js +138 -0
  38. package/dist/tools/projects.js.map +1 -0
  39. package/dist/tools/tasks.js +342 -0
  40. package/dist/tools/tasks.js.map +1 -0
  41. package/dist/tools/time.js +129 -0
  42. package/dist/tools/time.js.map +1 -0
  43. package/dist/tools/workplace.js +110 -0
  44. package/dist/tools/workplace.js.map +1 -0
  45. package/dist/tools/workspace.js +199 -0
  46. package/dist/tools/workspace.js.map +1 -0
  47. package/package.json +61 -0
@@ -0,0 +1,192 @@
1
+ import { z } from "zod";
2
+ import { ApiError } from "./client.js";
3
+ /**
4
+ * Shared shapes and formatting used across tool modules.
5
+ */
6
+ export const companyIdSchema = z
7
+ .number()
8
+ .int()
9
+ .optional()
10
+ .describe("Company id. Optional when the server is configured with CAREERFLOW_COMPANY_ID; " +
11
+ "required otherwise, or to act in a different company.");
12
+ /**
13
+ * Resolves the company for a call. Kept in one place so every tool behaves the
14
+ * same way when the id is omitted, and so the error names the fix.
15
+ */
16
+ export function resolveCompanyId(client, provided) {
17
+ const companyId = provided ?? client.defaultCompanyId;
18
+ if (companyId === undefined) {
19
+ throw new Error("No company id given and CAREERFLOW_COMPANY_ID is not set. " +
20
+ "Pass company_id, or set CAREERFLOW_COMPANY_ID on the server.");
21
+ }
22
+ return companyId;
23
+ }
24
+ export function textResult(value) {
25
+ const text = typeof value === "string" ? value : JSON.stringify(value, null, 2);
26
+ return { content: [{ type: "text", text }] };
27
+ }
28
+ export function errorResult(error) {
29
+ const message = error instanceof ApiError ? error.message : error instanceof Error ? error.message : String(error);
30
+ return { content: [{ type: "text", text: `Error: ${message}` }], isError: true };
31
+ }
32
+ /**
33
+ * Wraps a tool handler so a thrown error becomes a readable tool result rather
34
+ * than a transport-level failure the model cannot interpret or recover from.
35
+ */
36
+ export function safeHandler(handler) {
37
+ return async (args) => {
38
+ try {
39
+ return await handler(args);
40
+ }
41
+ catch (error) {
42
+ return errorResult(error);
43
+ }
44
+ };
45
+ }
46
+ /**
47
+ * Trims a task down to the fields an assistant actually reasons about. The raw
48
+ * rows carry ~40 columns of bookkeeping (activity flags, snapshot ids, ordering
49
+ * numbers) that would otherwise crowd out the useful content of a result.
50
+ */
51
+ export function summariseTask(task) {
52
+ return {
53
+ id: task.id,
54
+ title: task.title,
55
+ description: task.description || undefined,
56
+ project_id: task.project_id,
57
+ project_title: task.project_title || task.project_name || undefined,
58
+ board_id: task.board_id,
59
+ board_name: task.board_name || undefined,
60
+ is_completed: Boolean(task.is_completed),
61
+ priority: task.priority,
62
+ start_date: task.start_date || undefined,
63
+ due_date: task.due_date || undefined,
64
+ completion_date: task.completion_date || undefined,
65
+ time_estimate_minutes: task.time_estimate_minutes ?? undefined,
66
+ time_minutes: task.time_minutes ?? undefined,
67
+ review_status: task.review_status || undefined,
68
+ review_score: task.review_score ?? undefined,
69
+ is_archived: Boolean(task.is_archived),
70
+ members: Array.isArray(task.members)
71
+ ? task.members.map((m) => ({
72
+ user_id: m.user_id ?? m.id,
73
+ name: [m.firstname, m.lastname].filter(Boolean).join(" ") || m.staff_number_with_prefix
74
+ }))
75
+ : undefined,
76
+ labels: Array.isArray(task.labels)
77
+ ? task.labels.map((l) => ({ id: l.id, title: l.title }))
78
+ : undefined
79
+ };
80
+ }
81
+ export function summariseUser(user) {
82
+ return {
83
+ id: user.id,
84
+ name: [user.firstname, user.lastname].filter(Boolean).join(" ") || undefined,
85
+ staff_number: user.staff_number_with_prefix || user.staff_number || undefined,
86
+ email: user.email || undefined,
87
+ mobile: user.mobile || undefined,
88
+ type: user.type,
89
+ company_id: user.company_id ?? undefined,
90
+ position: user.position || user.job_title || undefined
91
+ };
92
+ }
93
+ /**
94
+ * Fetches a single task by id alone.
95
+ *
96
+ * `kanbans/tasks/get` needs project_id and board_id alongside the task id, which
97
+ * a caller working from a task number will not have. Task search accepts a bare
98
+ * id and returns the same row plus its project/board, so it is used as the
99
+ * lookup and the ids come back with the task.
100
+ *
101
+ * The read matters for more than convenience: `kanbans/tasks/edit` replaces the
102
+ * whole row, so any field omitted falls back to a default (is_completed -> false,
103
+ * priority -> "medium"). Every edit reads the task first and merges onto it — see
104
+ * `mergeTaskEdit` — so a partial change cannot silently clear untouched fields.
105
+ */
106
+ export async function fetchTask(client, companyId, taskId) {
107
+ const response = await client.get("kanbans/tasks/search", {
108
+ company_id: companyId,
109
+ task_id: taskId,
110
+ limit: 1
111
+ });
112
+ const { items: tasks } = extractList(response.data, "tasks");
113
+ const task = tasks.find((t) => Number(t.id) === Number(taskId)) ?? tasks[0];
114
+ if (!task || task.id === undefined) {
115
+ throw new Error(`Task ${taskId} was not found, or is not visible to this token.`);
116
+ }
117
+ return task;
118
+ }
119
+ /**
120
+ * Builds the full body for `kanbans/tasks/edit` by layering the requested
121
+ * changes over the task's current values, so unmentioned fields survive.
122
+ */
123
+ export function mergeTaskEdit(current, companyId, changes) {
124
+ const labelIds = Array.isArray(current.labels)
125
+ ? current.labels.map((l) => l.id).join(",")
126
+ : undefined;
127
+ const base = {
128
+ company_id: companyId,
129
+ project_id: current.project_id,
130
+ board_id: current.board_id,
131
+ task_id: current.id,
132
+ title: current.title,
133
+ description: current.description ?? "",
134
+ is_completed: Boolean(current.is_completed),
135
+ start_date: current.start_date ?? null,
136
+ due_date: current.due_date ?? null,
137
+ priority: current.priority ?? "medium",
138
+ time_estimate_minutes: current.time_estimate_minutes ?? null,
139
+ label_ids: labelIds
140
+ };
141
+ for (const [key, value] of Object.entries(changes)) {
142
+ if (value !== undefined) {
143
+ base[key] = value;
144
+ }
145
+ }
146
+ return base;
147
+ }
148
+ /**
149
+ * The authenticated user's id, cached for the process lifetime.
150
+ *
151
+ * Several endpoints scope a read to one user and reject an unscoped
152
+ * company-wide query unless the caller holds an admin role, so tools pass this
153
+ * to mean "mine". The token maps to a fixed user, so the lookup is done once.
154
+ */
155
+ let cachedUserId;
156
+ export async function currentUserId(client) {
157
+ if (cachedUserId !== undefined) {
158
+ return cachedUserId;
159
+ }
160
+ const response = await client.get("users/profile", {});
161
+ const id = response.data?.id;
162
+ if (id !== undefined) {
163
+ cachedUserId = Number(id);
164
+ }
165
+ return cachedUserId;
166
+ }
167
+ /**
168
+ * Pulls a list and its total out of a response payload.
169
+ *
170
+ * The API nests both inside `data` — the rows under an endpoint-specific key and
171
+ * the count under `total` — rather than putting a count on the envelope, so the
172
+ * key is named per call site and the total read from beside it.
173
+ */
174
+ export function extractList(data, key) {
175
+ if (Array.isArray(data)) {
176
+ return { items: data };
177
+ }
178
+ if (data && typeof data === "object") {
179
+ const record = data;
180
+ const items = Array.isArray(record[key]) ? record[key] : [];
181
+ const total = typeof record.total === "number" ? record.total : undefined;
182
+ return { items, total };
183
+ }
184
+ return { items: [] };
185
+ }
186
+ /** The API takes booleans as 1/0 in form fields. */
187
+ export function boolParam(value) {
188
+ if (value === undefined)
189
+ return undefined;
190
+ return value ? 1 : 0;
191
+ }
192
+ //# sourceMappingURL=helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"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"}
package/dist/index.js ADDED
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { loadConfig } from "./config.js";
6
+ import { CareerFlowClient } from "./client.js";
7
+ import { registerTaskTools } from "./tools/tasks.js";
8
+ import { registerWorkspaceTools } from "./tools/workspace.js";
9
+ import { registerTimeTools } from "./tools/time.js";
10
+ import { registerChatTools } from "./tools/chat.js";
11
+ import { registerChatExtraTools } from "./tools/chat-extra.js";
12
+ import { registerWorkplaceTools } from "./tools/workplace.js";
13
+ import { registerKanbanExtraTools } from "./tools/kanban-extra.js";
14
+ import { registerProjectTools } from "./tools/projects.js";
15
+ import { registerAdminTools } from "./tools/admin.js";
16
+ import { registerCrmTools } from "./tools/crm.js";
17
+ import { registerCrmAdminTools } from "./tools/crm-admin.js";
18
+ import { registerHrTools } from "./tools/hr.js";
19
+ import { registerBenefitFundTools } from "./tools/benefit-funds.js";
20
+ import { registerFormTools } from "./tools/forms.js";
21
+ import { registerCmsTools } from "./tools/cms.js";
22
+ import { registerDevTicketTools } from "./tools/dev-tickets.js";
23
+ import { registerMiscTools } from "./tools/misc.js";
24
+ const require = createRequire(import.meta.url);
25
+ let version = "unknown";
26
+ try {
27
+ version = require("../package.json").version;
28
+ }
29
+ catch {
30
+ // package.json sits outside dist/; missing it only affects the reported version
31
+ }
32
+ /**
33
+ * Tool groups, in registration order. Each entry may hold several modules that
34
+ * belong to one user-facing group — e.g. `crm` spans contacts/deals/activities
35
+ * and CRM configuration.
36
+ *
37
+ * CAREERFLOW_GROUPS (allowlist) and CAREERFLOW_EXCLUDE_GROUPS (denylist) take
38
+ * comma-separated names from here. Useful in clients that load every tool's
39
+ * schema into the model's context: a token scoped to one domain can register
40
+ * only that domain's tools.
41
+ */
42
+ const toolGroups = [
43
+ { name: "workspace", description: "whoami, companies, users", register: registerWorkspaceTools },
44
+ { name: "tasks", description: "tasks and comments", register: registerTaskTools },
45
+ { name: "kanban", description: "boards, labels, checklists, attachments", register: registerKanbanExtraTools },
46
+ { name: "projects", description: "projects and membership", register: registerProjectTools },
47
+ { name: "time", description: "timers and time entries", register: registerTimeTools },
48
+ {
49
+ name: "chat",
50
+ description: "chats, groups, messages",
51
+ register: (server, client) => {
52
+ registerChatTools(server, client);
53
+ registerChatExtraTools(server, client);
54
+ }
55
+ },
56
+ { name: "workplace", description: "announcements, surveys, attendance", register: registerWorkplaceTools },
57
+ { name: "admin", description: "company and user administration", register: registerAdminTools },
58
+ {
59
+ name: "crm",
60
+ description: "contacts, deals, activities, pipelines",
61
+ register: (server, client) => {
62
+ registerCrmTools(server, client);
63
+ registerCrmAdminTools(server, client);
64
+ }
65
+ },
66
+ { name: "hr", description: "payroll, reviews, notes, careers, onboarding, cafeteria", register: registerHrTools },
67
+ { name: "benefit-funds", description: "loans, lotteries, membership", register: registerBenefitFundTools },
68
+ { name: "forms", description: "forms and submissions", register: registerFormTools },
69
+ { name: "cms", description: "posts and custom pages", register: registerCmsTools },
70
+ { name: "dev-tickets", description: "support tickets", register: registerDevTicketTools },
71
+ { name: "misc", description: "addresses, surveys, announcements, billing", register: registerMiscTools }
72
+ ];
73
+ const USAGE = `CareerFlow MCP server — connects an AI assistant to CareerFlow.
74
+
75
+ Usage:
76
+ npx @codegame.dev/careerflow-mcp Start the MCP server over stdio (what a client runs)
77
+ npx @codegame.dev/careerflow-mcp init Guided setup: asks for your token, then wires up your client
78
+ npx @codegame.dev/careerflow-mcp --help Show this help
79
+ npx @codegame.dev/careerflow-mcp --version Show the version
80
+
81
+ init options (all optional; safe to share — the token is always prompted for
82
+ separately, hidden, and never read from the command line):
83
+ --base-url <url> CareerFlow host, e.g. https://app.example.com
84
+ --name <name> Server name in the client config (default: careerflow)
85
+ --company-id <id> Default company, so tools need no company_id
86
+ --client <id> claude-code, claude-desktop, zcode, cursor, or other
87
+
88
+ Example one-liner for onboarding pages:
89
+ npx @codegame.dev/careerflow-mcp init --base-url https://app.example.com --name acme --client claude-code
90
+
91
+ Environment:
92
+ CAREERFLOW_TOKEN Required. Personal access token (Settings > Access Tokens)
93
+ CAREERFLOW_BASE_URL Required. Your CareerFlow API root
94
+ CAREERFLOW_COMPANY_ID Optional. Default company, so tools need no company_id
95
+ CAREERFLOW_TIMEOUT_MS Optional. Request timeout, default 30000
96
+ CAREERFLOW_GROUPS Optional. Comma-separated tool groups to register
97
+ (${toolGroups.map((g) => g.name).join(", ")})
98
+ CAREERFLOW_EXCLUDE_GROUPS Optional. Comma-separated groups to leave out
99
+
100
+ Docs: https://www.npmjs.com/package/@codegame.dev/careerflow-mcp`;
101
+ /** Parses CAREERFLOW_GROUPS / CAREERFLOW_EXCLUDE_GROUPS into a group allowlist, or dies naming the valid groups. */
102
+ function selectGroups(env) {
103
+ const parse = (raw) => (raw ?? "")
104
+ .split(",")
105
+ .map((name) => name.trim().toLowerCase())
106
+ .filter(Boolean);
107
+ const include = parse(env.CAREERFLOW_GROUPS);
108
+ const exclude = parse(env.CAREERFLOW_EXCLUDE_GROUPS);
109
+ const valid = new Set(toolGroups.map((g) => g.name));
110
+ const unknown = [...include, ...exclude].filter((name) => !valid.has(name));
111
+ if (unknown.length) {
112
+ throw new Error(`Unknown tool group(s): ${unknown.join(", ")}. Valid groups: ${[...valid].join(", ")}.`);
113
+ }
114
+ let selected = toolGroups;
115
+ if (include.length) {
116
+ const allow = new Set(include);
117
+ selected = selected.filter((g) => allow.has(g.name));
118
+ if (!selected.length) {
119
+ throw new Error("CAREERFLOW_GROUPS left no tool groups to register.");
120
+ }
121
+ }
122
+ if (exclude.length) {
123
+ const deny = new Set(exclude);
124
+ selected = selected.filter((g) => !deny.has(g.name));
125
+ if (!selected.length) {
126
+ throw new Error("CAREERFLOW_EXCLUDE_GROUPS left no tool groups to register.");
127
+ }
128
+ }
129
+ return selected;
130
+ }
131
+ async function serve() {
132
+ const config = loadConfig();
133
+ const client = new CareerFlowClient(config);
134
+ const groups = selectGroups(process.env);
135
+ const server = new McpServer({
136
+ name: "careerflow",
137
+ version
138
+ }, {
139
+ instructions: "Tools for CareerFlow, a workplace platform: tasks and kanban boards, projects, time " +
140
+ "tracking, chat, CRM (contacts/deals/activities), HR (payroll, disciplinary and " +
141
+ "administrative notes, careers, onboarding), the benefit fund (loans and lotteries), " +
142
+ "forms, CMS, and company/user administration.\n\n" +
143
+ "Guidance:\n" +
144
+ "- Call whoami first when a request depends on who 'I' or 'me' is, or before any tool " +
145
+ "that takes company_id — whoami's response already includes every company the user " +
146
+ "belongs to (and resolves company_id outright if there's only one), so don't call a " +
147
+ "company-scoped tool without a company_id and wait for it to fail first.\n" +
148
+ "- Task writes need project and board ids: resolve them with list_projects and list_boards.\n" +
149
+ "- CRM writes need pipeline and stage ids: resolve them with crm_list_pipelines and crm_list_stages.\n" +
150
+ "- Resolve people to user ids with find_user or list_project_members before assigning work.\n" +
151
+ "- Some tools have a real, hard-to-undo effect and say so in their own description — " +
152
+ "send_message, create_administrative_note, and message_form_submission deliver to real " +
153
+ "people; create_user, set_user_status, and set_user_role change real accounts; " +
154
+ "request_benefit_fund_loan, update_benefit_fund_loan_status, " +
155
+ "pay_benefit_fund_loan_installment, and respond_to_lottery_win touch real money. Confirm " +
156
+ "details with the user before calling any of these.\n" +
157
+ "- A 403 means the token lacks a scope; tell the user which scope to add in " +
158
+ "Settings > Access Tokens rather than retrying."
159
+ });
160
+ for (const group of groups) {
161
+ group.register(server, client);
162
+ }
163
+ const transport = new StdioServerTransport();
164
+ await server.connect(transport);
165
+ // stdout carries the MCP protocol stream, so anything human-facing must go to
166
+ // stderr or it corrupts the transport
167
+ console.error(`careerflow-mcp connected (api: ${config.baseUrl}, groups: ${groups.map((g) => g.name).join(", ")})`);
168
+ }
169
+ const [, , command] = process.argv;
170
+ if (command === "--help" || command === "-h") {
171
+ console.log(USAGE);
172
+ }
173
+ else if (command === "--version" || command === "-v") {
174
+ console.log(version);
175
+ }
176
+ else if (command === "init") {
177
+ import("./setup.js")
178
+ .then(({ runSetup }) => runSetup(process.argv.slice(3)))
179
+ .catch((error) => {
180
+ console.error(`careerflow-mcp init failed: ${error.message}`);
181
+ process.exit(1);
182
+ });
183
+ }
184
+ else if (command === undefined) {
185
+ serve().catch((error) => {
186
+ console.error(`careerflow-mcp failed to start: ${error.message}`);
187
+ process.exit(1);
188
+ });
189
+ }
190
+ else {
191
+ console.error(`Unknown command: ${command}\n\n${USAGE}`);
192
+ process.exit(1);
193
+ }
194
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}