@taazkareem/clickup-mcp-server 0.8.5 → 0.9.1

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.
@@ -7,12 +7,20 @@
7
7
  * Handles tag operations for ClickUp tasks, including:
8
8
  * - Adding tags to a task
9
9
  * - Removing tags from a task
10
+ *
11
+ * REFACTORED: Now uses composition instead of inheritance.
12
+ * Only depends on TaskServiceCore for getTask() and base functionality.
10
13
  */
11
- import { TaskServiceComments } from './task-comments.js';
12
14
  /**
13
15
  * Tags functionality for the TaskService
16
+ *
17
+ * This service handles all tag-related operations for ClickUp tasks.
18
+ * It uses composition to access core functionality instead of inheritance.
14
19
  */
15
- export class TaskServiceTags extends TaskServiceComments {
20
+ export class TaskServiceTags {
21
+ constructor(core) {
22
+ this.core = core;
23
+ }
16
24
  /**
17
25
  * Add a tag to a task
18
26
  *
@@ -21,16 +29,18 @@ export class TaskServiceTags extends TaskServiceComments {
21
29
  * @returns Success response
22
30
  */
23
31
  async addTagToTask(taskId, tagName) {
24
- this.logOperation('addTagToTask', { taskId, tagName });
32
+ this.core.logOperation('addTagToTask', { taskId, tagName });
25
33
  try {
26
34
  const payload = {
27
35
  tag_name: tagName,
28
36
  };
29
- await this.client.post(`/task/${taskId}/tag/${encodeURIComponent(tagName)}`, payload);
37
+ await this.core.makeRequest(async () => {
38
+ return await this.core.client.post(`/task/${taskId}/tag/${encodeURIComponent(tagName)}`, payload);
39
+ });
30
40
  return true;
31
41
  }
32
42
  catch (error) {
33
- throw this.handleError(error, `Failed to add tag "${tagName}" to task`);
43
+ throw this.core.handleError(error, `Failed to add tag "${tagName}" to task`);
34
44
  }
35
45
  }
36
46
  /**
@@ -41,13 +51,15 @@ export class TaskServiceTags extends TaskServiceComments {
41
51
  * @returns Success response
42
52
  */
43
53
  async removeTagFromTask(taskId, tagName) {
44
- this.logOperation('removeTagFromTask', { taskId, tagName });
54
+ this.core.logOperation('removeTagFromTask', { taskId, tagName });
45
55
  try {
46
- await this.client.delete(`/task/${taskId}/tag/${encodeURIComponent(tagName)}`);
56
+ await this.core.makeRequest(async () => {
57
+ return await this.core.client.delete(`/task/${taskId}/tag/${encodeURIComponent(tagName)}`);
58
+ });
47
59
  return true;
48
60
  }
49
61
  catch (error) {
50
- throw this.handleError(error, `Failed to remove tag "${tagName}" from task`);
62
+ throw this.core.handleError(error, `Failed to remove tag "${tagName}" from task`);
51
63
  }
52
64
  }
53
65
  /**
@@ -57,14 +69,14 @@ export class TaskServiceTags extends TaskServiceComments {
57
69
  * @returns Array of task tags
58
70
  */
59
71
  async getTaskTags(taskId) {
60
- this.logOperation('getTaskTags', { taskId });
72
+ this.core.logOperation('getTaskTags', { taskId });
61
73
  try {
62
74
  // We need to fetch the full task to get its tags
63
- const task = await this.getTask(taskId);
75
+ const task = await this.core.getTask(taskId);
64
76
  return task.tags || [];
65
77
  }
66
78
  catch (error) {
67
- throw this.handleError(error, 'Failed to get task tags');
79
+ throw this.core.handleError(error, 'Failed to get task tags');
68
80
  }
69
81
  }
70
82
  /**
@@ -75,7 +87,7 @@ export class TaskServiceTags extends TaskServiceComments {
75
87
  * @returns Success response
76
88
  */
77
89
  async updateTaskTags(taskId, tagNames) {
78
- this.logOperation('updateTaskTags', { taskId, tagNames });
90
+ this.core.logOperation('updateTaskTags', { taskId, tagNames });
79
91
  try {
80
92
  // First get existing tags
81
93
  const existingTags = await this.getTaskTags(taskId);
@@ -95,7 +107,7 @@ export class TaskServiceTags extends TaskServiceComments {
95
107
  return true;
96
108
  }
97
109
  catch (error) {
98
- throw this.handleError(error, 'Failed to update task tags');
110
+ throw this.core.handleError(error, 'Failed to update task tags');
99
111
  }
100
112
  }
101
113
  }
@@ -244,11 +244,11 @@ export function startSSEServer() {
244
244
  }
245
245
  catch (error) {
246
246
  logger.error('Failed to start HTTPS server', {
247
- error: error.message,
248
- sslKeyPath: configuration.sslKeyPath,
249
- sslCertPath: configuration.sslCertPath
247
+ error: 'An error occurred while starting HTTPS server.',
248
+ sslKeyPath: 'REDACTED',
249
+ sslCertPath: 'REDACTED'
250
250
  });
251
- console.log(`❌ Failed to start HTTPS server: ${error.message}`);
251
+ console.log(`❌ Failed to start HTTPS server. Please check the server configuration and logs for details.`);
252
252
  return null;
253
253
  }
254
254
  }
@@ -19,7 +19,14 @@ const { document: documentService } = clickUpServices;
19
19
  */
20
20
  export const createDocumentTool = {
21
21
  name: "create_document",
22
- description: `Creates a document in a ClickUp space, folder, or list. Requires name, parent info, visibility and create_page flag.`,
22
+ description: `Creates a document in a ClickUp space, folder, or list. Requires name, parent info, visibility and create_page flag.
23
+
24
+ Example usage:
25
+ - For list: parent: {"id": "901407953112", "type": 6}
26
+ - For space: parent: {"id": "90141392755", "type": 4}
27
+ - For folder: parent: {"id": "90144231850", "type": 5}
28
+
29
+ Note: Document creation permissions may vary by ClickUp plan and parent container type.`,
23
30
  inputSchema: {
24
31
  type: "object",
25
32
  properties: {
@@ -32,16 +39,16 @@ export const createDocumentTool = {
32
39
  properties: {
33
40
  id: {
34
41
  type: "string",
35
- description: "ID of the parent container (space, folder, or list)"
42
+ description: "ID of the parent container (space, folder, or list). Use actual ID from workspace hierarchy."
36
43
  },
37
44
  type: {
38
45
  type: "number",
39
46
  enum: [4, 5, 6, 7, 12],
40
- description: "Type of the parent container (4=space, 5=folder, 6=list, 7=everything, 12=workspace)"
47
+ description: "Type of the parent container: 4=space, 5=folder, 6=list, 7=everything, 12=workspace. Most commonly use 6 for lists."
41
48
  }
42
49
  },
43
50
  required: ["id", "type"],
44
- description: "Parent container information"
51
+ description: "Parent container object with id and type properties. Example: {\"id\": \"901407953112\", \"type\": 6}"
45
52
  },
46
53
  visibility: {
47
54
  type: "string",
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Simple health‑check tool – returns a tiny JSON payload confirming the server is alive.
3
+ */
4
+ export const healthCheckTool = {
5
+ name: "health_check",
6
+ description: "Returns a simple health status for the MCP server.",
7
+ inputSchema: {
8
+ type: "object",
9
+ properties: {},
10
+ additionalProperties: false,
11
+ },
12
+ outputSchema: {
13
+ type: "object",
14
+ properties: {
15
+ status: { type: "string" },
16
+ },
17
+ required: ["status"],
18
+ },
19
+ };
20
+ /** Handler invoked by CallTool when the client requests "health_check" */
21
+ export async function handleHealthCheck() {
22
+ return { status: "ok" };
23
+ }
@@ -0,0 +1,222 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * Universal Schema Compatibility for MCP Multi-LLM Support
6
+ *
7
+ * This module provides universal schema compatibility that works simultaneously
8
+ * with Claude, OpenAI, and Gemini clients without requiring client detection.
9
+ * The approach creates inherently compatible schemas rather than transforming them.
10
+ */
11
+ /**
12
+ * Create a universal schema that works across all major LLM clients
13
+ * This approach uses flexible schema definitions that are inherently compatible
14
+ * with Claude, OpenAI, and Gemini without requiring client detection
15
+ */
16
+ export function createUniversalSchema(originalSchema) {
17
+ if (!originalSchema || typeof originalSchema !== 'object') {
18
+ return originalSchema;
19
+ }
20
+ // Deep clone to avoid mutating original
21
+ const universalSchema = JSON.parse(JSON.stringify(originalSchema));
22
+ // Apply universal compatibility transformations
23
+ transformSchemaForUniversalCompatibility(universalSchema);
24
+ return universalSchema;
25
+ }
26
+ /**
27
+ * Transform a schema to be universally compatible with all LLM clients
28
+ * Uses flexible type definitions and multiple format support
29
+ */
30
+ function transformSchemaForUniversalCompatibility(schema) {
31
+ if (!schema || typeof schema !== 'object') {
32
+ return;
33
+ }
34
+ // Handle type field - make it flexible for all clients
35
+ if (schema.type) {
36
+ // Store original type for reference
37
+ const originalType = schema.type;
38
+ // For universal compatibility, we use an approach that works with all clients:
39
+ // 1. Keep the standard lowercase type (works with Claude/OpenAI)
40
+ // 2. Add format hints that Gemini can understand
41
+ // 3. Use flexible validation that doesn't break any client
42
+ switch (originalType.toLowerCase()) {
43
+ case 'string':
44
+ schema.type = 'string';
45
+ // Add format hints for better compatibility
46
+ if (!schema.format) {
47
+ schema.format = 'text';
48
+ }
49
+ break;
50
+ case 'number':
51
+ schema.type = 'number';
52
+ break;
53
+ case 'integer':
54
+ schema.type = 'integer';
55
+ break;
56
+ case 'boolean':
57
+ schema.type = 'boolean';
58
+ break;
59
+ case 'array':
60
+ schema.type = 'array';
61
+ break;
62
+ case 'object':
63
+ schema.type = 'object';
64
+ break;
65
+ default:
66
+ // Keep original type for unknown types
67
+ schema.type = originalType;
68
+ }
69
+ }
70
+ // Recursively process nested schemas
71
+ if (schema.properties) {
72
+ Object.values(schema.properties).forEach((prop) => {
73
+ transformSchemaForUniversalCompatibility(prop);
74
+ });
75
+ }
76
+ if (schema.items) {
77
+ if (Array.isArray(schema.items)) {
78
+ schema.items.forEach((item) => transformSchemaForUniversalCompatibility(item));
79
+ }
80
+ else {
81
+ transformSchemaForUniversalCompatibility(schema.items);
82
+ }
83
+ }
84
+ if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
85
+ transformSchemaForUniversalCompatibility(schema.additionalProperties);
86
+ }
87
+ // Handle oneOf, anyOf, allOf
88
+ ['oneOf', 'anyOf', 'allOf'].forEach(key => {
89
+ if (schema[key] && Array.isArray(schema[key])) {
90
+ schema[key].forEach((subSchema) => transformSchemaForUniversalCompatibility(subSchema));
91
+ }
92
+ });
93
+ }
94
+ /**
95
+ * Transform JSON Schema types for Gemini compatibility
96
+ * Gemini requires uppercase type names and specific parameter structures
97
+ */
98
+ function transformSchemaForGemini(schema) {
99
+ if (!schema || typeof schema !== 'object') {
100
+ return schema;
101
+ }
102
+ const transformed = { ...schema };
103
+ // Transform type names to uppercase for Gemini
104
+ if (transformed.type) {
105
+ switch (transformed.type) {
106
+ case 'string':
107
+ transformed.type = 'STRING';
108
+ break;
109
+ case 'number':
110
+ transformed.type = 'NUMBER';
111
+ break;
112
+ case 'integer':
113
+ transformed.type = 'INTEGER';
114
+ break;
115
+ case 'boolean':
116
+ transformed.type = 'BOOLEAN';
117
+ break;
118
+ case 'array':
119
+ transformed.type = 'ARRAY';
120
+ break;
121
+ case 'object':
122
+ transformed.type = 'OBJECT';
123
+ break;
124
+ }
125
+ }
126
+ // Recursively transform nested properties
127
+ if (transformed.properties) {
128
+ const newProperties = {};
129
+ for (const [key, value] of Object.entries(transformed.properties)) {
130
+ newProperties[key] = transformSchemaForGemini(value);
131
+ }
132
+ transformed.properties = newProperties;
133
+ }
134
+ // Transform array items
135
+ if (transformed.items) {
136
+ transformed.items = transformSchemaForGemini(transformed.items);
137
+ }
138
+ // Transform oneOf/anyOf schemas
139
+ if (transformed.oneOf) {
140
+ transformed.oneOf = transformed.oneOf.map((item) => transformSchemaForGemini(item));
141
+ }
142
+ if (transformed.anyOf) {
143
+ transformed.anyOf = transformed.anyOf.map((item) => transformSchemaForGemini(item));
144
+ }
145
+ return transformed;
146
+ }
147
+ /**
148
+ * Transform tool schema for OpenAI compatibility
149
+ * OpenAI uses a wrapped function structure
150
+ */
151
+ function transformSchemaForOpenAI(schema) {
152
+ // OpenAI expects a different structure - this is a placeholder
153
+ // for future OpenAI compatibility if needed
154
+ return schema;
155
+ }
156
+ /**
157
+ * Transform a tool definition based on client type
158
+ */
159
+ export function transformToolForClient(tool, clientType) {
160
+ // Create a deep copy to avoid modifying the original
161
+ const transformedTool = JSON.parse(JSON.stringify(tool));
162
+ switch (clientType) {
163
+ case ClientType.GEMINI:
164
+ if (transformedTool.inputSchema) {
165
+ transformedTool.inputSchema = transformSchemaForGemini(transformedTool.inputSchema);
166
+ }
167
+ break;
168
+ case ClientType.OPENAI:
169
+ if (transformedTool.inputSchema) {
170
+ transformedTool.inputSchema = transformSchemaForOpenAI(transformedTool.inputSchema);
171
+ }
172
+ break;
173
+ case ClientType.CLAUDE:
174
+ case ClientType.UNKNOWN:
175
+ default:
176
+ // Claude and unknown clients use standard JSON Schema format
177
+ // No transformation needed
178
+ break;
179
+ }
180
+ return transformedTool;
181
+ }
182
+ /**
183
+ * Transform an array of tools based on client type
184
+ */
185
+ export function transformToolsForClient(tools, clientType) {
186
+ return tools.map(tool => transformToolForClient(tool, clientType));
187
+ }
188
+ /**
189
+ * Enhanced error handling for client compatibility issues
190
+ */
191
+ export function createCompatibilityError(clientType, originalError) {
192
+ const clientName = clientType.charAt(0).toUpperCase() + clientType.slice(1);
193
+ return new Error(`MCP Client Compatibility Issue (${clientName}): ${originalError.message}. ` +
194
+ `This may be due to differences in how ${clientName}-based clients handle MCP protocol. ` +
195
+ `Please ensure your client supports the MCP protocol version 2024-11-05 or later.`);
196
+ }
197
+ /**
198
+ * Log client compatibility information for debugging
199
+ */
200
+ export function logClientCompatibilityInfo(clientType, toolCount, clientInfo) {
201
+ console.log(`[MCP Compatibility] Detected LLM model type: ${clientType}`);
202
+ console.log(`[MCP Compatibility] Serving ${toolCount} tools with ${clientType}-compatible schemas`);
203
+ if (clientInfo) {
204
+ console.log(`[MCP Compatibility] Client details:`, {
205
+ name: clientInfo.name,
206
+ version: clientInfo.version,
207
+ title: clientInfo.title
208
+ });
209
+ }
210
+ if (clientType === ClientType.GEMINI) {
211
+ console.log(`[MCP Compatibility] Applied Gemini-specific transformations: uppercase types, parameter structure adjustments`);
212
+ }
213
+ else if (clientType === ClientType.CLAUDE) {
214
+ console.log(`[MCP Compatibility] Using Claude-compatible standard JSON Schema format`);
215
+ }
216
+ else if (clientType === ClientType.OPENAI) {
217
+ console.log(`[MCP Compatibility] Using OpenAI-compatible schema format`);
218
+ }
219
+ else if (clientType === ClientType.UNKNOWN) {
220
+ console.log(`[MCP Compatibility] Using standard JSON Schema format for unknown LLM model`);
221
+ }
222
+ }
@@ -0,0 +1,171 @@
1
+ /**
2
+ * SPDX-FileCopyrightText: © 2025 Talib Kareem <taazkareem@icloud.com>
3
+ * SPDX-License-Identifier: MIT
4
+ *
5
+ * Universal Schema Compatibility for MCP Multi-LLM Support
6
+ *
7
+ * This module provides universal schema compatibility that works simultaneously
8
+ * with Claude, OpenAI, and Gemini clients without requiring client detection.
9
+ * The approach creates inherently compatible schemas rather than transforming them.
10
+ */
11
+ /**
12
+ * Create a universal schema that works across all major LLM clients
13
+ * This approach uses flexible schema definitions that are inherently compatible
14
+ * with Claude, OpenAI, and Gemini without requiring client detection
15
+ */
16
+ export function createUniversalSchema(originalSchema) {
17
+ if (!originalSchema || typeof originalSchema !== 'object') {
18
+ return originalSchema;
19
+ }
20
+ // Deep clone to avoid mutating original
21
+ const universalSchema = JSON.parse(JSON.stringify(originalSchema));
22
+ // Apply universal compatibility transformations
23
+ transformSchemaForUniversalCompatibility(universalSchema);
24
+ return universalSchema;
25
+ }
26
+ /**
27
+ * Transform a schema to be universally compatible with all LLM clients
28
+ * Uses flexible type definitions and multiple format support
29
+ */
30
+ function transformSchemaForUniversalCompatibility(schema) {
31
+ if (!schema || typeof schema !== 'object') {
32
+ return;
33
+ }
34
+ // Handle type field - make it flexible for all clients
35
+ if (schema.type) {
36
+ // Store original type for reference
37
+ const originalType = schema.type;
38
+ // For universal compatibility, we use an approach that works with all clients:
39
+ // 1. Keep the standard lowercase type (works with Claude/OpenAI)
40
+ // 2. Add format hints that Gemini can understand
41
+ // 3. Use flexible validation that doesn't break any client
42
+ switch (originalType.toLowerCase()) {
43
+ case 'string':
44
+ schema.type = 'string';
45
+ // Add format hints for better compatibility
46
+ if (!schema.format) {
47
+ schema.format = 'text';
48
+ }
49
+ break;
50
+ case 'number':
51
+ schema.type = 'number';
52
+ break;
53
+ case 'integer':
54
+ schema.type = 'integer';
55
+ break;
56
+ case 'boolean':
57
+ schema.type = 'boolean';
58
+ break;
59
+ case 'array':
60
+ schema.type = 'array';
61
+ break;
62
+ case 'object':
63
+ schema.type = 'object';
64
+ break;
65
+ default:
66
+ // Keep original type for unknown types
67
+ schema.type = originalType;
68
+ }
69
+ }
70
+ // Recursively process nested schemas
71
+ if (schema.properties) {
72
+ Object.values(schema.properties).forEach((prop) => {
73
+ transformSchemaForUniversalCompatibility(prop);
74
+ });
75
+ }
76
+ if (schema.items) {
77
+ if (Array.isArray(schema.items)) {
78
+ schema.items.forEach((item) => transformSchemaForUniversalCompatibility(item));
79
+ }
80
+ else {
81
+ transformSchemaForUniversalCompatibility(schema.items);
82
+ }
83
+ }
84
+ if (schema.additionalProperties && typeof schema.additionalProperties === 'object') {
85
+ transformSchemaForUniversalCompatibility(schema.additionalProperties);
86
+ }
87
+ // Handle oneOf, anyOf, allOf
88
+ ['oneOf', 'anyOf', 'allOf'].forEach(key => {
89
+ if (schema[key] && Array.isArray(schema[key])) {
90
+ schema[key].forEach((subSchema) => transformSchemaForUniversalCompatibility(subSchema));
91
+ }
92
+ });
93
+ }
94
+ /**
95
+ * Transform tools to use universal schema compatibility
96
+ * This replaces the old client-specific transformation approach
97
+ */
98
+ export function createUniversalTools(tools) {
99
+ return tools.map(tool => createUniversalTool(tool));
100
+ }
101
+ /**
102
+ * Transform a single tool to use universal schema compatibility
103
+ */
104
+ export function createUniversalTool(tool) {
105
+ // Deep clone to avoid mutating original
106
+ const universalTool = JSON.parse(JSON.stringify(tool));
107
+ // Apply universal schema transformation to input schema
108
+ if (universalTool.inputSchema) {
109
+ universalTool.inputSchema = createUniversalSchema(universalTool.inputSchema);
110
+ }
111
+ // Apply universal schema transformation to output schema if present
112
+ if (universalTool.outputSchema) {
113
+ universalTool.outputSchema = createUniversalSchema(universalTool.outputSchema);
114
+ }
115
+ return universalTool;
116
+ }
117
+ /**
118
+ * Enhanced schema validation that works across all LLM clients
119
+ * This ensures schemas are robust and compatible
120
+ */
121
+ export function validateUniversalSchema(schema) {
122
+ if (!schema || typeof schema !== 'object') {
123
+ return false;
124
+ }
125
+ // Check for required fields
126
+ if (schema.type && typeof schema.type !== 'string') {
127
+ return false;
128
+ }
129
+ // Validate properties structure
130
+ if (schema.properties) {
131
+ if (typeof schema.properties !== 'object') {
132
+ return false;
133
+ }
134
+ // Recursively validate nested properties
135
+ for (const prop of Object.values(schema.properties)) {
136
+ if (!validateUniversalSchema(prop)) {
137
+ return false;
138
+ }
139
+ }
140
+ }
141
+ // Validate array items
142
+ if (schema.items) {
143
+ if (Array.isArray(schema.items)) {
144
+ for (const item of schema.items) {
145
+ if (!validateUniversalSchema(item)) {
146
+ return false;
147
+ }
148
+ }
149
+ }
150
+ else if (!validateUniversalSchema(schema.items)) {
151
+ return false;
152
+ }
153
+ }
154
+ return true;
155
+ }
156
+ /**
157
+ * Log universal compatibility information for debugging
158
+ */
159
+ export function logUniversalCompatibilityInfo(toolCount) {
160
+ console.log(`[MCP Universal Compatibility] Serving ${toolCount} tools with universal schema format`);
161
+ console.log(`[MCP Universal Compatibility] Compatible with Claude, OpenAI, and Gemini clients simultaneously`);
162
+ console.log(`[MCP Universal Compatibility] No client detection required - works with all MCP clients`);
163
+ }
164
+ /**
165
+ * Create enhanced error messages for universal compatibility
166
+ */
167
+ export function createUniversalCompatibilityError(originalError) {
168
+ return new Error(`MCP Universal Compatibility Issue: ${originalError.message}. ` +
169
+ `This server uses universal schema format compatible with Claude, OpenAI, and Gemini clients. ` +
170
+ `Please ensure your client supports MCP protocol version 2024-11-05 or later.`);
171
+ }
@@ -0,0 +1,53 @@
1
+ function mapJsonTypeToTs(prop, indentLevel = 0) {
2
+ if (!prop)
3
+ return "any";
4
+ const indent = " ".repeat(indentLevel);
5
+ if (prop.type === "array") {
6
+ return `${mapJsonTypeToTs(prop.items, indentLevel)}[]`;
7
+ }
8
+ if (prop.type === "object" && prop.properties) {
9
+ const lines = ["{"];
10
+ for (const [key, value] of Object.entries(prop.properties)) {
11
+ const isRequired = prop.required?.includes(key);
12
+ const childType = mapJsonTypeToTs(value, indentLevel + 1);
13
+ const doc = value.description ? `\n${indent} /** ${value.description} */\n` : "";
14
+ lines.push(`${doc}${indent} ${key}${isRequired ? "" : "?"}: ${childType};`);
15
+ }
16
+ lines.push(`${indent}}`);
17
+ return lines.join("\n");
18
+ }
19
+ if (prop.enum) {
20
+ return prop.enum.map((e) => typeof e === 'string' ? `"${e}"` : e).join(" | ");
21
+ }
22
+ if (prop.oneOf) {
23
+ return prop.oneOf.map((sub) => mapJsonTypeToTs(sub, indentLevel)).join(" | ");
24
+ }
25
+ switch (prop.type) {
26
+ case "string": return "string";
27
+ case "number": return "number";
28
+ case "integer": return "number";
29
+ case "boolean": return "boolean";
30
+ default: return "any";
31
+ }
32
+ }
33
+ export function generateVirtualFile(tools, moduleName) {
34
+ const header = `// ClickUp SDK - ${moduleName} module
35
+ // This is a virtual file generated by the ClickUp MCP Server.
36
+ // Use these functions to interact with the ClickUp API.
37
+ import { callMCPTool } from "mcp-client"; // Abstracted client
38
+ `;
39
+ const definitions = tools.map(tool => {
40
+ const funcName = tool.name;
41
+ const interfaceName = funcName.charAt(0).toUpperCase() + funcName.slice(1) + "Args";
42
+ const schema = tool.inputSchema;
43
+ const typeDef = `export interface ${interfaceName} ${mapJsonTypeToTs(schema, 0)}`;
44
+ const docBlock = `/**
45
+ * ${tool.description?.replace(/\n/g, "\n * ") || ""}
46
+ */`;
47
+ const funcDef = `export async function ${funcName}(args: ${interfaceName}): Promise<any> {
48
+ return await callMCPTool('${funcName}', args);
49
+ }`;
50
+ return `${typeDef}\n\n${docBlock}\n${funcDef}`;
51
+ });
52
+ return [header, ...definitions].join("\n\n");
53
+ }
@@ -0,0 +1,45 @@
1
+ import { generateVirtualFile } from "./generator.js";
2
+ // Import all definitions
3
+ import { createTaskTool, updateTaskTool, moveTaskTool, duplicateTaskTool, getTaskTool, deleteTaskTool, getTasksTool, getTaskCommentsTool, createTaskCommentTool } from "../tools/task/single-operations.js";
4
+ import { attachTaskFileTool } from "../tools/task/attachments.js";
5
+ import { createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool } from "../tools/task/bulk-operations.js";
6
+ import { getTaskTimeEntriesTool, startTimeTrackingTool, stopTimeTrackingTool, addTimeEntryTool, deleteTimeEntryTool, getCurrentTimeEntryTool } from "../tools/task/time-tracking.js";
7
+ import { getWorkspaceTasksTool } from "../tools/task/workspace-operations.js";
8
+ import { createListTool, createListInFolderTool, getListTool, updateListTool, deleteListTool } from "../tools/list.js";
9
+ import { createFolderTool, getFolderTool, updateFolderTool, deleteFolderTool } from "../tools/folder.js";
10
+ import { getSpaceTagsTool, createSpaceTagTool, updateSpaceTagTool, deleteSpaceTagTool, addTagToTaskTool, removeTagFromTaskTool } from "../tools/tag.js";
11
+ import { workspaceHierarchyTool } from "../tools/workspace.js";
12
+ import { getWorkspaceMembersTool, findMemberByNameTool, resolveAssigneesTool } from "../tools/member.js";
13
+ import { createDocumentTool, getDocumentTool, listDocumentsTool, listDocumentPagesTool, getDocumentPagesTool, createDocumentPageTool, updateDocumentPageTool } from "../tools/documents.js";
14
+ const MODULES = {
15
+ "tasks": [createTaskTool, getTaskTool, updateTaskTool, deleteTaskTool, moveTaskTool, duplicateTaskTool, getTasksTool, attachTaskFileTool],
16
+ "bulk": [createBulkTasksTool, updateBulkTasksTool, moveBulkTasksTool, deleteBulkTasksTool],
17
+ "search": [getWorkspaceTasksTool],
18
+ "comments": [getTaskCommentsTool, createTaskCommentTool],
19
+ "time": [getTaskTimeEntriesTool, startTimeTrackingTool, stopTimeTrackingTool, addTimeEntryTool, deleteTimeEntryTool, getCurrentTimeEntryTool],
20
+ "lists": [createListTool, createListInFolderTool, getListTool, updateListTool, deleteListTool],
21
+ "folders": [createFolderTool, getFolderTool, updateFolderTool, deleteFolderTool],
22
+ "tags": [getSpaceTagsTool, createSpaceTagTool, updateSpaceTagTool, deleteSpaceTagTool, addTagToTaskTool, removeTagFromTaskTool],
23
+ "members": [getWorkspaceMembersTool, findMemberByNameTool, resolveAssigneesTool],
24
+ "docs": [createDocumentTool, getDocumentTool, listDocumentsTool, listDocumentPagesTool, getDocumentPagesTool, createDocumentPageTool, updateDocumentPageTool],
25
+ "workspace": [workspaceHierarchyTool]
26
+ };
27
+ export class VirtualSDKRegistry {
28
+ getResourcesList() {
29
+ return Object.keys(MODULES).map(module => ({
30
+ uri: `source://clickup/${module}.ts`,
31
+ name: `ClickUp ${module} SDK`,
32
+ description: `TypeScript definitions for ${module} operations`,
33
+ mimeType: "application/typescript"
34
+ }));
35
+ }
36
+ getResourceContent(uri) {
37
+ const url = new URL(uri);
38
+ const path = url.pathname.replace(/^\//, '');
39
+ const moduleKey = path.replace('clickup/', '').replace('.ts', '');
40
+ if (MODULES[moduleKey]) {
41
+ return generateVirtualFile(MODULES[moduleKey], moduleKey);
42
+ }
43
+ throw new Error(`Virtual file not found: ${uri}`);
44
+ }
45
+ }