@starasia/task-management-mcp 1.0.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.
package/README.md ADDED
@@ -0,0 +1,230 @@
1
+ # Task Management MCP
2
+
3
+ MCP server for Star Perkasa Technology Task Management API.
4
+
5
+ The server exposes read tools, dry-run-first write tools, and monitoring/reporting tools for spaces, projects, and tasks. It does **not** store a global user token in environment variables. Authorization is session-based: the AI agent must ask the active user for their bearer token, user id, and organization id, then call `set_auth_context` before using task tools.
6
+
7
+ ## Requirements
8
+
9
+ - Node.js 20+
10
+ - Running `task-management-api`
11
+ - A user-provided auth context at runtime:
12
+ - bearer token
13
+ - user id
14
+ - organization id
15
+
16
+ ## Package
17
+
18
+ Published package name:
19
+
20
+ ```text
21
+ @starasia/task-management-mcp
22
+ ```
23
+
24
+ Install from npm after a release lands on `main`:
25
+
26
+ ```bash
27
+ npm install -g @starasia/task-management-mcp
28
+ ```
29
+
30
+ ## Install from source
31
+
32
+ ```bash
33
+ npm install
34
+ npm run build
35
+ ```
36
+
37
+ ## Runtime configuration
38
+
39
+ Only non-user runtime settings should be configured as environment variables:
40
+
41
+ ```text
42
+ TASK_MANAGEMENT_API_BASE_URL=http://localhost:3000
43
+ TASK_MANAGEMENT_TIMEOUT_MS=30000
44
+ TASK_MANAGEMENT_MAX_BULK_SIZE=50
45
+ TASK_MANAGEMENT_AUTH_TTL_MS=28800000
46
+ ```
47
+
48
+ Do **not** configure these as server env values:
49
+
50
+ ```text
51
+ TASK_MANAGEMENT_API_TOKEN
52
+ TASK_MANAGEMENT_USER_ID
53
+ TASK_MANAGEMENT_ORGANIZATION_ID
54
+ ```
55
+
56
+ Those values are user identity and permission context. The AI agent must request them from the user at runtime and call `set_auth_context`. `userId` is derived from the bearer token JWT `sub` claim, so it should not be configured as a server env value.
57
+
58
+ ## Hermes stdio config
59
+
60
+ ```yaml
61
+ mcp_servers:
62
+ task_management_dev:
63
+ command: "npx"
64
+ args: ["-y", "@starasia/task-management-mcp"]
65
+ env:
66
+ TASK_MANAGEMENT_API_BASE_URL: "https://dev-task-management-api.example.com"
67
+
68
+ task_management_prod:
69
+ command: "npx"
70
+ args: ["-y", "@starasia/task-management-mcp"]
71
+ env:
72
+ TASK_MANAGEMENT_API_BASE_URL: "https://task-management-api.example.com"
73
+ ```
74
+
75
+ With two MCP instances, tools are clearly separated by environment. The exact rendered tool names depend on the host. Hermes usually prefixes them by server name; Claude Code and Codex use their own MCP tool naming conventions.
76
+
77
+ ## Claude Code / Codex MCP config
78
+
79
+ Use the same stdio command shape. Example:
80
+
81
+ ```json
82
+ {
83
+ "mcpServers": {
84
+ "task-management-dev": {
85
+ "command": "npx",
86
+ "args": ["-y", "@starasia/task-management-mcp"],
87
+ "env": {
88
+ "TASK_MANAGEMENT_API_BASE_URL": "https://dev-task-management-api.example.com"
89
+ }
90
+ },
91
+ "task-management-prod": {
92
+ "command": "npx",
93
+ "args": ["-y", "@starasia/task-management-mcp"],
94
+ "env": {
95
+ "TASK_MANAGEMENT_API_BASE_URL": "https://task-management-api.example.com"
96
+ }
97
+ }
98
+ }
99
+ }
100
+ ```
101
+
102
+ ## Auth flow for AI agents
103
+
104
+ Before calling any task/space/project tool, the agent should:
105
+
106
+ 1. Call `get_auth_context_status`.
107
+ 2. If `configured` is false, ask the active user for:
108
+ - bearer token
109
+ - organization id
110
+ 3. Call `set_auth_context`:
111
+
112
+ ```json
113
+ {
114
+ "bearerToken": "user-supplied-jwt-access-token",
115
+ "organizationId": "organization-id",
116
+ "environment": "dev",
117
+ "validate": true
118
+ }
119
+ ```
120
+
121
+ `set_auth_context` decodes the bearer token JWT payload and uses the `sub` claim as `userId`. If the token is not a JWT or does not contain a string `sub`, pass `userId` explicitly as an override:
122
+
123
+ ```json
124
+ {
125
+ "bearerToken": "user-supplied-token",
126
+ "userId": "explicit-user-id",
127
+ "organizationId": "organization-id",
128
+ "validate": true
129
+ }
130
+ ```
131
+
132
+ `set_auth_context` validates the token with `GET /spaces` by default, stores the context only in the MCP process memory, and returns a masked status. It never returns the token. The context expires after `TASK_MANAGEMENT_AUTH_TTL_MS` milliseconds, default 8 hours.
133
+
134
+ Important security note: for broad Claude Code / Codex compatibility, the bearer token is passed as a tool argument. That means it may transit the host application's conversation or debug surface before it reaches the MCP. Users should only provide tokens in trusted local agent sessions, prefer short-lived tokens, and call `clear_auth_context` when done.
135
+
136
+ For separate dev/prod MCP instances, call `set_auth_context` separately on each instance because each process has its own in-memory session.
137
+
138
+ When the user is done, call:
139
+
140
+ ```text
141
+ clear_auth_context
142
+ ```
143
+
144
+ If a tool is called before auth is configured, the MCP returns an error instructing the agent to ask the user for bearer token and organization id, then call `set_auth_context`. `userId` is derived from the token JWT `sub` claim unless an explicit override is provided.
145
+
146
+ ## Tools
147
+
148
+ ### Auth/session
149
+
150
+ - `set_auth_context`
151
+ - `get_auth_context_status`
152
+ - `clear_auth_context`
153
+
154
+ ### Read-only
155
+
156
+ - `list_spaces`
157
+ - `list_projects`
158
+ - `get_project`
159
+ - `list_statuses`
160
+ - `list_project_members`
161
+ - `list_tasks`
162
+ - `get_task`
163
+ - `summarize_my_tasks`
164
+ - `summarize_project_progress`
165
+
166
+ ### Write tools
167
+
168
+ Write tools default to `dryRun: true`. Set `dryRun: false` to execute.
169
+
170
+ - `create_task`
171
+ - `bulk_create_tasks`
172
+ - `update_task`
173
+ - `change_task_status`
174
+ - `assign_task` — add assignees using numeric `ProjectMember.id` values
175
+ - `unassign_task` — remove one assignee using numeric `ProjectMember.id`
176
+ - `replace_task_assignees` — make the assignee set exactly match the provided `projectMemberIds`
177
+
178
+ `create_task.createdBy`, `assign_task.assignedBy`, and `replace_task_assignees.assignedBy` default to the active auth `userId` when omitted.
179
+
180
+ ### Monitoring/reporting
181
+
182
+ - `monitor_overdue_tasks`
183
+ - `monitor_stale_tasks`
184
+ - `workload_by_user`
185
+ - `generate_work_report`
186
+
187
+ ## API assumptions found from `task-management-api`
188
+
189
+ - Spaces are exposed at `GET /spaces`.
190
+ - Projects are scoped by space: `/spaces/:spaceId/projects`.
191
+ - Tasks are scoped by project: `/projects/:projectId/tasks`.
192
+ - Cross-project task list is `GET /my-tasks` with `scope=me|all`.
193
+ - The MCP forwards `Authorization`, `X-User-Id`, and `X-Organization-Id` from the active auth context.
194
+ - Task creation currently requires `createdBy`; assignment uses numeric `ProjectMember.id` values.
195
+
196
+ ## Safety model
197
+
198
+ - User identity and bearer token are supplied at runtime, not baked into env.
199
+ - Auth context is held in process memory only and expires by `TASK_MANAGEMENT_AUTH_TTL_MS`.
200
+ - `get_auth_context_status` masks the token and only returns `hasToken: true/false`.
201
+ - The API URL cannot be overridden by `set_auth_context`; it comes from the MCP instance env to avoid bearer-token exfiltration to arbitrary hosts.
202
+ - For compatibility with Claude Code and Codex, the token is passed through the MCP tool call surface. Use trusted local sessions and short-lived tokens where possible.
203
+ - Read tools require an active auth context.
204
+ - Write tools are dry-run by default.
205
+ - Bulk create has a configurable limit (`TASK_MANAGEMENT_MAX_BULK_SIZE`, default 50).
206
+ - `bulk_create_tasks` can skip existing tasks by exact normalized title (`idempotency: "title"`).
207
+ - The MCP does not bypass application permissions; it only calls HTTP endpoints with the active user's auth context.
208
+
209
+ ## Development
210
+
211
+ ```bash
212
+ npm run typecheck
213
+ npm run lint
214
+ npm test
215
+ npm run build
216
+ npm run pack:check
217
+ ```
218
+
219
+ ## CI/CD
220
+
221
+ - `CI` runs on pull requests to `main`/`dev`, pushes to `dev`, and manual dispatch.
222
+ - `CI` uses GitHub Environment `development` for Discord notification only.
223
+ - `Release and Notify` runs on pushes to `main` and manual dispatch.
224
+ - Release workflow validates typecheck, lint, test, build, and `npm pack --dry-run` before publishing.
225
+ - Publish is skipped when the package version already exists on npm.
226
+ - Required development secrets:
227
+ - `DISCORD_WEBHOOK_URL`
228
+ - Required production secrets:
229
+ - `NPM_TOKEN`
230
+ - `DISCORD_WEBHOOK_URL` (notification)
package/dist/api.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import type { RuntimeConfig } from "./config.js";
2
+ import type { PaginationMeta } from "./types.js";
3
+ export interface RequestContext {
4
+ userId: string;
5
+ organizationId: string;
6
+ bearerToken: string;
7
+ }
8
+ export declare class TaskManagementApiError extends Error {
9
+ readonly status: number;
10
+ readonly payload: unknown;
11
+ constructor(message: string, status: number, payload: unknown);
12
+ }
13
+ export declare class TaskManagementApi {
14
+ private readonly config;
15
+ constructor(config: RuntimeConfig);
16
+ get<T>(path: string, query?: Record<string, unknown>, context?: RequestContext): Promise<{
17
+ data: T;
18
+ meta?: PaginationMeta;
19
+ message?: string;
20
+ }>;
21
+ post<T>(path: string, body?: unknown, query?: Record<string, unknown>, context?: RequestContext): Promise<{
22
+ data: T;
23
+ meta?: PaginationMeta;
24
+ message?: string;
25
+ }>;
26
+ put<T>(path: string, body?: unknown, query?: Record<string, unknown>, context?: RequestContext): Promise<{
27
+ data: T;
28
+ meta?: PaginationMeta;
29
+ message?: string;
30
+ }>;
31
+ delete<T>(path: string, body?: unknown, query?: Record<string, unknown>, context?: RequestContext): Promise<{
32
+ data: T;
33
+ meta?: PaginationMeta;
34
+ message?: string;
35
+ }>;
36
+ private request;
37
+ }
package/dist/api.js ADDED
@@ -0,0 +1,124 @@
1
+ export class TaskManagementApiError extends Error {
2
+ status;
3
+ payload;
4
+ constructor(message, status, payload) {
5
+ super(message);
6
+ this.status = status;
7
+ this.payload = payload;
8
+ this.name = "TaskManagementApiError";
9
+ }
10
+ }
11
+ function normalizeBearerToken(token) {
12
+ const trimmed = token.trim();
13
+ if (!trimmed)
14
+ throw new Error("Bearer token cannot be empty.");
15
+ if (/[\r\n]/.test(trimmed)) {
16
+ throw new Error("Bearer token cannot contain newline characters.");
17
+ }
18
+ return trimmed.toLowerCase().startsWith("bearer ")
19
+ ? trimmed
20
+ : `Bearer ${trimmed}`;
21
+ }
22
+ export class TaskManagementApi {
23
+ config;
24
+ constructor(config) {
25
+ this.config = config;
26
+ }
27
+ async get(path, query, context) {
28
+ return this.request("GET", path, undefined, query, context);
29
+ }
30
+ async post(path, body, query, context) {
31
+ return this.request("POST", path, body, query, context);
32
+ }
33
+ async put(path, body, query, context) {
34
+ return this.request("PUT", path, body, query, context);
35
+ }
36
+ async delete(path, body, query, context) {
37
+ return this.request("DELETE", path, body, query, context);
38
+ }
39
+ async request(method, path, body, query, context) {
40
+ if (!context?.bearerToken || !context.userId || !context.organizationId) {
41
+ throw new Error("Authentication context is not configured. Ask the user for bearerToken and organizationId, then call set_auth_context first. userId is derived from the token JWT sub claim unless explicitly provided.");
42
+ }
43
+ const url = new URL(path, this.config.TASK_MANAGEMENT_API_BASE_URL.replace(/\/$/, "") + "/");
44
+ for (const [key, value] of Object.entries(query ?? {})) {
45
+ if (value === undefined || value === null || value === "")
46
+ continue;
47
+ if (Array.isArray(value)) {
48
+ for (const item of value)
49
+ url.searchParams.append(key, String(item));
50
+ }
51
+ else {
52
+ url.searchParams.set(key, String(value));
53
+ }
54
+ }
55
+ const controller = new AbortController();
56
+ const timeout = setTimeout(() => controller.abort(), this.config.TASK_MANAGEMENT_TIMEOUT_MS);
57
+ try {
58
+ const headers = {
59
+ Accept: "application/json",
60
+ "X-Organization-Id": context.organizationId,
61
+ "X-User-Id": context.userId,
62
+ Authorization: normalizeBearerToken(context.bearerToken),
63
+ };
64
+ if (body !== undefined)
65
+ headers["Content-Type"] = "application/json";
66
+ const response = await fetch(url, {
67
+ method,
68
+ headers,
69
+ body: body === undefined ? undefined : JSON.stringify(body),
70
+ signal: controller.signal,
71
+ });
72
+ const text = await response.text();
73
+ const payload = parsePayload(text);
74
+ if (!response.ok) {
75
+ throw new TaskManagementApiError(extractErrorMessage(payload, response.statusText), response.status, payload);
76
+ }
77
+ if (isEnvelope(payload)) {
78
+ return {
79
+ data: payload.data,
80
+ meta: payload.meta,
81
+ message: payload.message,
82
+ };
83
+ }
84
+ return { data: payload };
85
+ }
86
+ catch (error) {
87
+ if (error instanceof TaskManagementApiError)
88
+ throw error;
89
+ if (error instanceof Error && error.name === "AbortError") {
90
+ throw new TaskManagementApiError("Task management API request timed out", 408, undefined);
91
+ }
92
+ throw error;
93
+ }
94
+ finally {
95
+ clearTimeout(timeout);
96
+ }
97
+ }
98
+ }
99
+ function isEnvelope(payload) {
100
+ return Boolean(payload &&
101
+ typeof payload === "object" &&
102
+ "status" in payload &&
103
+ "message" in payload &&
104
+ "data" in payload);
105
+ }
106
+ function parsePayload(text) {
107
+ if (!text)
108
+ return undefined;
109
+ try {
110
+ return JSON.parse(text);
111
+ }
112
+ catch {
113
+ return { message: text };
114
+ }
115
+ }
116
+ function extractErrorMessage(payload, fallback) {
117
+ if (payload &&
118
+ typeof payload === "object" &&
119
+ "message" in payload &&
120
+ typeof payload.message === "string")
121
+ return payload.message;
122
+ return fallback;
123
+ }
124
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+ declare const envSchema: z.ZodObject<{
3
+ TASK_MANAGEMENT_API_BASE_URL: z.ZodDefault<z.ZodString>;
4
+ TASK_MANAGEMENT_TIMEOUT_MS: z.ZodDefault<z.ZodNumber>;
5
+ TASK_MANAGEMENT_MAX_BULK_SIZE: z.ZodDefault<z.ZodNumber>;
6
+ TASK_MANAGEMENT_AUTH_TTL_MS: z.ZodDefault<z.ZodNumber>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ TASK_MANAGEMENT_API_BASE_URL: string;
9
+ TASK_MANAGEMENT_TIMEOUT_MS: number;
10
+ TASK_MANAGEMENT_MAX_BULK_SIZE: number;
11
+ TASK_MANAGEMENT_AUTH_TTL_MS: number;
12
+ }, {
13
+ TASK_MANAGEMENT_API_BASE_URL?: string | undefined;
14
+ TASK_MANAGEMENT_TIMEOUT_MS?: number | undefined;
15
+ TASK_MANAGEMENT_MAX_BULK_SIZE?: number | undefined;
16
+ TASK_MANAGEMENT_AUTH_TTL_MS?: number | undefined;
17
+ }>;
18
+ export type RuntimeConfig = z.infer<typeof envSchema>;
19
+ export declare function loadConfig(env?: NodeJS.ProcessEnv): RuntimeConfig;
20
+ export {};
package/dist/config.js ADDED
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ const envSchema = z.object({
3
+ TASK_MANAGEMENT_API_BASE_URL: z
4
+ .string()
5
+ .url()
6
+ .default("http://localhost:3000"),
7
+ TASK_MANAGEMENT_TIMEOUT_MS: z.coerce
8
+ .number()
9
+ .int()
10
+ .positive()
11
+ .default(30_000),
12
+ TASK_MANAGEMENT_MAX_BULK_SIZE: z.coerce.number().int().positive().default(50),
13
+ TASK_MANAGEMENT_AUTH_TTL_MS: z.coerce
14
+ .number()
15
+ .int()
16
+ .positive()
17
+ .default(8 * 60 * 60 * 1000),
18
+ });
19
+ export function loadConfig(env = process.env) {
20
+ return envSchema.parse(env);
21
+ }
22
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,SAAS,GAAG,CAAC,CAAC,MAAM,CAAC;IACzB,4BAA4B,EAAE,CAAC;SAC5B,MAAM,EAAE;SACR,GAAG,EAAE;SACL,OAAO,CAAC,uBAAuB,CAAC;IACnC,0BAA0B,EAAE,CAAC,CAAC,MAAM;SACjC,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,MAAM,CAAC;IAClB,6BAA6B,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;IAC7E,2BAA2B,EAAE,CAAC,CAAC,MAAM;SAClC,MAAM,EAAE;SACR,GAAG,EAAE;SACL,QAAQ,EAAE;SACV,OAAO,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;CAC/B,CAAC,CAAC;AAIH,MAAM,UAAU,UAAU,CAAC,GAAG,GAAG,OAAO,CAAC,GAAG;IAC1C,OAAO,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;AAC9B,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4
+ import { loadConfig } from "./config.js";
5
+ import { TaskManagementApi } from "./api.js";
6
+ import { registerTools } from "./tools.js";
7
+ const config = loadConfig();
8
+ const server = new McpServer({
9
+ name: "task-management-mcp",
10
+ version: "0.1.0",
11
+ });
12
+ registerTools(server, new TaskManagementApi(config), config.TASK_MANAGEMENT_MAX_BULK_SIZE, config.TASK_MANAGEMENT_AUTH_TTL_MS);
13
+ const transport = new StdioServerTransport();
14
+ await server.connect(transport);
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;AAC5B,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,qBAAqB;IAC3B,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,aAAa,CACX,MAAM,EACN,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAC7B,MAAM,CAAC,6BAA6B,EACpC,MAAM,CAAC,2BAA2B,CACnC,CAAC;AAEF,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { z } from "zod";
2
+ export declare const paginationSchema: {
3
+ page: z.ZodDefault<z.ZodNumber>;
4
+ perPage: z.ZodDefault<z.ZodNumber>;
5
+ };
6
+ export declare const prioritySchema: z.ZodEnum<["Low", "Normal", "High", "Urgent"]>;
7
+ export declare const categorySchema: z.ZodEnum<["ToDo", "InProgress", "Complete"]>;
8
+ export declare const dryRunSchema: {
9
+ dryRun: z.ZodDefault<z.ZodBoolean>;
10
+ };
@@ -0,0 +1,14 @@
1
+ import { z } from "zod";
2
+ export const paginationSchema = {
3
+ page: z.number().int().positive().default(1),
4
+ perPage: z.number().int().positive().max(100).default(30),
5
+ };
6
+ export const prioritySchema = z.enum(["Low", "Normal", "High", "Urgent"]);
7
+ export const categorySchema = z.enum(["ToDo", "InProgress", "Complete"]);
8
+ export const dryRunSchema = {
9
+ dryRun: z
10
+ .boolean()
11
+ .default(true)
12
+ .describe("When true, validate and preview without writing. Set false to execute."),
13
+ };
14
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5C,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;CAC1D,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAC1E,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,YAAY,EAAE,UAAU,CAAC,CAAC,CAAC;AAEzE,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,MAAM,EAAE,CAAC;SACN,OAAO,EAAE;SACT,OAAO,CAAC,IAAI,CAAC;SACb,QAAQ,CACP,wEAAwE,CACzE;CACJ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ import type { TaskManagementApi } from "./api.js";
3
+ export declare function registerTools(server: McpServer, api: TaskManagementApi, maxBulkSize: number, authTtlMs: number): void;