add_project_user
Add a user to a Kanboard project by specifying project ID, user ID, and optional role (project-manager, project-member, or project-viewer).
Instructions
Add a user to a Kanboard project with the given role. Role defaults to 'project-member' if not specified. Use list_project_users to find user ids and list_projects to find project ids.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | Yes | Numeric project id. | |
| user_id | Yes | Numeric user id to add to the project. | |
| role | No | Role to assign: 'project-manager', 'project-member' (default), or 'project-viewer'. | project-member |
Implementation Reference
- src/handler/kanboard.ts:376-393 (handler)Core handler method `addProjectUser` that calls Kanboard's JSON-RPC `addProjectUser` API. Takes project_id, user_id, and optional role (defaults to 'project-member'). Throws KanboardApiError if Kanboard returns false.
/** * Adds a user to a project with the given role. * @throws {KanboardApiError} when Kanboard returns false. */ public async addProjectUser(input: { project_id: number; user_id: number; role?: "project-manager" | "project-member" | "project-viewer" | undefined; }): Promise<void> { const params = { project_id: input.project_id, user_id: input.user_id, role: input.role ?? "project-member", }; const raw = await this.#apiClient.call("addProjectUser", params); this.#logger.debug({ method: "addProjectUser" }, "addProjectUser OK"); decodeMutation("addProjectUser", raw); } - src/tools/add-project-user.ts:17-28 (schema)Zod input schema `AddProjectUserInput` — validates project_id (positive int), user_id (positive int), and role (enum: project-manager/member/viewer, defaults to 'project-member').
export const AddProjectUserInput = z .object({ project_id: z.number().int().positive().describe("Numeric project id."), user_id: z.number().int().positive().describe("Numeric user id to add to the project."), role: z .enum(["project-manager", "project-member", "project-viewer"]) .default("project-member") .describe( "Role to assign: 'project-manager', 'project-member' (default), or 'project-viewer'.", ), }) .strict(); - src/tools/add-project-user.ts:58-88 (handler)Tool definition `addProjectUserTool` with name 'add_project_user', description, input schema, and handler that parses input, delegates to `deps.handler.addProjectUser()`, and returns a success result with content and structuredContent.
export const addProjectUserTool = { name: "add_project_user", description: "Add a user to a Kanboard project with the given role. " + "Role defaults to 'project-member' if not specified. " + "Use list_project_users to find user ids and list_projects to find project ids.", inputSchema: AddProjectUserInput, handler: async (raw: unknown, deps: ToolDeps): Promise<AddProjectUserResult> => { const input = AddProjectUserInput.parse(raw); await deps.handler.addProjectUser({ project_id: input.project_id, user_id: input.user_id, role: input.role, }); return { content: [ { type: "text", text: `User ${String(input.user_id)} added to project ${String(input.project_id)} as ${input.role}.`, }, ], structuredContent: { user_id: input.user_id, project_id: input.project_id, role: input.role, }, }; }, }; - src/tools/index.ts:159-197 (registration)Tool registered in `allTools` array at position 0 (alphabetically first), ensuring it gets mounted on the MCP server via `registerTools()`.
export const allTools: readonly ToolDef[] = [ addProjectUserTool, attachFileToTaskTool, createColumnTool, createCommentTool, createProjectTool, createSubtaskTool, createSwimlaneTool, createTaskTool, createTasksBatchTool, deleteColumnTool, deleteCommentTool, deleteProjectTool, deleteSubtaskTool, deleteSwimlaneTool, deleteTaskTool, deleteTaskFileTool, getProjectTool, getTaskTool, listCategoriesTool, listColumnsTool, listMyTasksTool, listOverdueTasksTool, listProjectsTool, listSubtasksTool, listSwimlanesTool, listTasksTool, listProjectUsersTool, moveColumnTool, moveSwimlaneTool, moveTaskPositionTool, removeProjectUserTool, updateColumnTool, updateCommentTool, updateProjectTool, updateSubtaskTool, updateSwimlaneTool, updateTaskTool, ] as const; - src/tools/index.ts:215-233 (registration)`registerTools` function iterates all tools (including addProjectUserTool) and registers each with the MCP server via `server.registerTool(name, config, callback)`.
export function registerTools(server: McpServer, deps: ToolDeps): void { for (const tool of allTools) { // Cast: each tool handler returns a `{ content, structuredContent }` object // that satisfies `CallToolResult`. We use `unknown` in `ToolDef.handler` to // keep the per-tool return types encapsulated, so we cast here at the // registration boundary where the MCP SDK takes ownership. const cb = ((args: Record<string, unknown>) => tool.handler(args, deps)) as unknown as ToolCallback; server.registerTool( tool.name, { description: tool.description, inputSchema: tool.inputSchema, }, cb, ); } }