basecamp-mcp 1.0.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.
- package/README.md +155 -0
- package/dist/constants.d.ts +14 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +14 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/common.d.ts +13 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +19 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/tools/comments.d.ts +7 -0
- package/dist/tools/comments.d.ts.map +1 -0
- package/dist/tools/comments.js +179 -0
- package/dist/tools/comments.js.map +1 -0
- package/dist/tools/kanban.d.ts +6 -0
- package/dist/tools/kanban.d.ts.map +1 -0
- package/dist/tools/kanban.js +390 -0
- package/dist/tools/kanban.js.map +1 -0
- package/dist/tools/messages.d.ts +8 -0
- package/dist/tools/messages.d.ts.map +1 -0
- package/dist/tools/messages.js +300 -0
- package/dist/tools/messages.js.map +1 -0
- package/dist/tools/people.d.ts +6 -0
- package/dist/tools/people.d.ts.map +1 -0
- package/dist/tools/people.js +142 -0
- package/dist/tools/people.js.map +1 -0
- package/dist/tools/projects.d.ts +11 -0
- package/dist/tools/projects.d.ts.map +1 -0
- package/dist/tools/projects.js +116 -0
- package/dist/tools/projects.js.map +1 -0
- package/dist/tools/todos.d.ts +6 -0
- package/dist/tools/todos.d.ts.map +1 -0
- package/dist/tools/todos.js +223 -0
- package/dist/tools/todos.js.map +1 -0
- package/dist/types.d.ts +27 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/auth.d.ts +24 -0
- package/dist/utils/auth.d.ts.map +1 -0
- package/dist/utils/auth.js +62 -0
- package/dist/utils/auth.js.map +1 -0
- package/dist/utils/contentOperations.d.ts +55 -0
- package/dist/utils/contentOperations.d.ts.map +1 -0
- package/dist/utils/contentOperations.js +109 -0
- package/dist/utils/contentOperations.js.map +1 -0
- package/dist/utils/errorHandlers.d.ts +11 -0
- package/dist/utils/errorHandlers.d.ts.map +1 -0
- package/dist/utils/errorHandlers.js +87 -0
- package/dist/utils/errorHandlers.js.map +1 -0
- package/dist/utils/serializers.d.ts +27 -0
- package/dist/utils/serializers.d.ts.map +1 -0
- package/dist/utils/serializers.js +19 -0
- package/dist/utils/serializers.js.map +1 -0
- package/package.json +65 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TODO tools for Basecamp MCP server
|
|
3
|
+
*/
|
|
4
|
+
import { asyncPagedToArray } from "basecamp-client";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { BasecampIdSchema } from "../schemas/common.js";
|
|
7
|
+
import { initializeBasecampClient } from "../utils/auth.js";
|
|
8
|
+
import { htmlRules } from "../utils/contentOperations.js";
|
|
9
|
+
import { handleBasecampError } from "../utils/errorHandlers.js";
|
|
10
|
+
export function registerTodoTools(server) {
|
|
11
|
+
server.registerTool("basecamp_get_todoset", {
|
|
12
|
+
title: "Get Basecamp Todo Set",
|
|
13
|
+
description: "Get todo set container for a project. Returns todo lists and groups.",
|
|
14
|
+
inputSchema: {
|
|
15
|
+
bucket_id: BasecampIdSchema,
|
|
16
|
+
todoset_id: BasecampIdSchema,
|
|
17
|
+
},
|
|
18
|
+
annotations: {
|
|
19
|
+
readOnlyHint: true,
|
|
20
|
+
destructiveHint: false,
|
|
21
|
+
idempotentHint: true,
|
|
22
|
+
openWorldHint: true,
|
|
23
|
+
},
|
|
24
|
+
}, async (params) => {
|
|
25
|
+
try {
|
|
26
|
+
const client = await initializeBasecampClient();
|
|
27
|
+
const responseTodoSet = await client.todoSets.get({
|
|
28
|
+
params: { bucketId: params.bucket_id, todosetId: params.todoset_id },
|
|
29
|
+
});
|
|
30
|
+
if (responseTodoSet.status !== 200 || !responseTodoSet.body) {
|
|
31
|
+
throw new Error("Failed to fetch todo set");
|
|
32
|
+
}
|
|
33
|
+
const todoLists = await asyncPagedToArray({
|
|
34
|
+
fetchPage: client.todoLists.list,
|
|
35
|
+
request: {
|
|
36
|
+
params: {
|
|
37
|
+
bucketId: params.bucket_id,
|
|
38
|
+
todosetId: params.todoset_id,
|
|
39
|
+
},
|
|
40
|
+
query: {},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
const todoSet = responseTodoSet.body;
|
|
44
|
+
return {
|
|
45
|
+
content: [
|
|
46
|
+
{
|
|
47
|
+
type: "text",
|
|
48
|
+
text: JSON.stringify({
|
|
49
|
+
id: todoSet.id,
|
|
50
|
+
name: todoSet.name,
|
|
51
|
+
url: todoSet.app_url,
|
|
52
|
+
completed: todoSet.completed,
|
|
53
|
+
todoLists: todoLists.map((list) => ({
|
|
54
|
+
id: list.id,
|
|
55
|
+
url: list.app_url,
|
|
56
|
+
title: list.title,
|
|
57
|
+
completed: list.completed,
|
|
58
|
+
position: list.position,
|
|
59
|
+
})),
|
|
60
|
+
}, null, 2),
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
return {
|
|
67
|
+
content: [{ type: "text", text: handleBasecampError(error) }],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
server.registerTool("basecamp_list_todos", {
|
|
72
|
+
title: "List Basecamp Todos",
|
|
73
|
+
description: "List todos in a todo list. Filter by status: 'active' or 'archived'.",
|
|
74
|
+
inputSchema: {
|
|
75
|
+
bucket_id: BasecampIdSchema,
|
|
76
|
+
todolist_id: BasecampIdSchema,
|
|
77
|
+
status: z.enum(["active", "archived"]).default("active").optional(),
|
|
78
|
+
completed: z.enum(["true"]).optional(),
|
|
79
|
+
},
|
|
80
|
+
annotations: {
|
|
81
|
+
readOnlyHint: true,
|
|
82
|
+
destructiveHint: false,
|
|
83
|
+
idempotentHint: true,
|
|
84
|
+
openWorldHint: true,
|
|
85
|
+
},
|
|
86
|
+
}, async (params) => {
|
|
87
|
+
try {
|
|
88
|
+
const client = await initializeBasecampClient();
|
|
89
|
+
const todos = await asyncPagedToArray({
|
|
90
|
+
fetchPage: client.todos.list,
|
|
91
|
+
request: {
|
|
92
|
+
params: {
|
|
93
|
+
bucketId: params.bucket_id,
|
|
94
|
+
todolistId: params.todolist_id,
|
|
95
|
+
},
|
|
96
|
+
query: { status: params.status, completed: params.completed },
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
content: [
|
|
101
|
+
{
|
|
102
|
+
type: "text",
|
|
103
|
+
text: JSON.stringify({
|
|
104
|
+
count: todos.length,
|
|
105
|
+
todos: todos.map((t) => ({
|
|
106
|
+
id: t.id,
|
|
107
|
+
content: t.content,
|
|
108
|
+
completed: t.completed,
|
|
109
|
+
})),
|
|
110
|
+
}, null, 2),
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
return {
|
|
117
|
+
content: [{ type: "text", text: handleBasecampError(error) }],
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
server.registerTool("basecamp_create_todo", {
|
|
122
|
+
title: "Create Basecamp Todo",
|
|
123
|
+
description: `Create a new todo item in a todo list. ${htmlRules}`,
|
|
124
|
+
inputSchema: {
|
|
125
|
+
bucket_id: BasecampIdSchema,
|
|
126
|
+
todolist_id: BasecampIdSchema,
|
|
127
|
+
content: z.string().min(1),
|
|
128
|
+
description: z.string().optional(),
|
|
129
|
+
},
|
|
130
|
+
annotations: {
|
|
131
|
+
readOnlyHint: false,
|
|
132
|
+
destructiveHint: false,
|
|
133
|
+
idempotentHint: false,
|
|
134
|
+
openWorldHint: true,
|
|
135
|
+
},
|
|
136
|
+
}, async (params) => {
|
|
137
|
+
try {
|
|
138
|
+
const client = await initializeBasecampClient();
|
|
139
|
+
const response = await client.todos.create({
|
|
140
|
+
params: {
|
|
141
|
+
bucketId: params.bucket_id,
|
|
142
|
+
todolistId: params.todolist_id,
|
|
143
|
+
},
|
|
144
|
+
body: { content: params.content, description: params.description },
|
|
145
|
+
});
|
|
146
|
+
if (response.status !== 201 || !response.body) {
|
|
147
|
+
throw new Error("Failed to create todo");
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
content: [
|
|
151
|
+
{
|
|
152
|
+
type: "text",
|
|
153
|
+
text: `Todo created!\n\nID: ${response.body.id}\nContent: ${response.body.content}`,
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
return {
|
|
160
|
+
content: [{ type: "text", text: handleBasecampError(error) }],
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
server.registerTool("basecamp_complete_todo", {
|
|
165
|
+
title: "Complete Basecamp Todo",
|
|
166
|
+
description: "Mark a todo as completed.",
|
|
167
|
+
inputSchema: {
|
|
168
|
+
bucket_id: BasecampIdSchema,
|
|
169
|
+
todo_id: BasecampIdSchema,
|
|
170
|
+
},
|
|
171
|
+
annotations: {
|
|
172
|
+
readOnlyHint: false,
|
|
173
|
+
destructiveHint: false,
|
|
174
|
+
idempotentHint: true,
|
|
175
|
+
openWorldHint: true,
|
|
176
|
+
},
|
|
177
|
+
}, async (params) => {
|
|
178
|
+
try {
|
|
179
|
+
const client = await initializeBasecampClient();
|
|
180
|
+
await client.todos.complete({
|
|
181
|
+
params: { bucketId: params.bucket_id, todoId: params.todo_id },
|
|
182
|
+
});
|
|
183
|
+
return {
|
|
184
|
+
content: [{ type: "text", text: "Todo marked as completed!" }],
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return {
|
|
189
|
+
content: [{ type: "text", text: handleBasecampError(error) }],
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
server.registerTool("basecamp_uncomplete_todo", {
|
|
194
|
+
title: "Uncomplete Basecamp Todo",
|
|
195
|
+
description: "Mark a todo as incomplete (undo completion).",
|
|
196
|
+
inputSchema: {
|
|
197
|
+
bucket_id: BasecampIdSchema,
|
|
198
|
+
todo_id: BasecampIdSchema,
|
|
199
|
+
},
|
|
200
|
+
annotations: {
|
|
201
|
+
readOnlyHint: false,
|
|
202
|
+
destructiveHint: false,
|
|
203
|
+
idempotentHint: true,
|
|
204
|
+
openWorldHint: true,
|
|
205
|
+
},
|
|
206
|
+
}, async (params) => {
|
|
207
|
+
try {
|
|
208
|
+
const client = await initializeBasecampClient();
|
|
209
|
+
await client.todos.uncomplete({
|
|
210
|
+
params: { bucketId: params.bucket_id, todoId: params.todo_id },
|
|
211
|
+
});
|
|
212
|
+
return {
|
|
213
|
+
content: [{ type: "text", text: "Todo marked as incomplete!" }],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
return {
|
|
218
|
+
content: [{ type: "text", text: handleBasecampError(error) }],
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=todos.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todos.js","sourceRoot":"","sources":["../../src/tools/todos.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript types and enums for the Basecamp MCP server
|
|
3
|
+
*/
|
|
4
|
+
/** Parsed Basecamp URL structure */
|
|
5
|
+
export interface ParsedBasecampUrl {
|
|
6
|
+
accountId: string;
|
|
7
|
+
bucketId: string;
|
|
8
|
+
resourceType: string;
|
|
9
|
+
resourceId: string;
|
|
10
|
+
fullUrl: string;
|
|
11
|
+
}
|
|
12
|
+
/** Pagination metadata for list responses */
|
|
13
|
+
export interface PaginationMetadata {
|
|
14
|
+
total: number;
|
|
15
|
+
count: number;
|
|
16
|
+
offset: number;
|
|
17
|
+
has_more: boolean;
|
|
18
|
+
next_offset?: number;
|
|
19
|
+
}
|
|
20
|
+
/** Truncation metadata when response exceeds character limit */
|
|
21
|
+
export interface TruncationInfo {
|
|
22
|
+
truncated: boolean;
|
|
23
|
+
truncation_message?: string;
|
|
24
|
+
original_count?: number;
|
|
25
|
+
returned_count?: number;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oCAAoC;AACpC,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,6CAA6C;AAC7C,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,OAAO,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication utilities for Basecamp API
|
|
3
|
+
*/
|
|
4
|
+
import { type Client } from "basecamp-client";
|
|
5
|
+
/**
|
|
6
|
+
* Initialize and return an authenticated Basecamp client.
|
|
7
|
+
*
|
|
8
|
+
* Uses environment variables:
|
|
9
|
+
* - BASECAMP_CLIENT_ID
|
|
10
|
+
* - BASECAMP_CLIENT_SECRET
|
|
11
|
+
* - BASECAMP_REFRESH_TOKEN
|
|
12
|
+
* - BASECAMP_USER_AGENT (optional)
|
|
13
|
+
* - BASECAMP_ACCOUNT_ID
|
|
14
|
+
*
|
|
15
|
+
* @param accountId - Basecamp account ID to use for the client
|
|
16
|
+
* @returns Authenticated Basecamp client instance
|
|
17
|
+
* @throws Error if required environment variables are missing or authentication fails
|
|
18
|
+
*/
|
|
19
|
+
export declare function initializeBasecampClient(): Promise<Client>;
|
|
20
|
+
/**
|
|
21
|
+
* Clear the cached bearer token (useful for forcing token refresh)
|
|
22
|
+
*/
|
|
23
|
+
export declare function clearTokenCache(): void;
|
|
24
|
+
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/utils/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAe,KAAK,MAAM,EAAkB,MAAM,iBAAiB,CAAC;AAK3E;;;;;;;;;;;;;GAaG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,CAAC,CAwChE;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,IAAI,CAEtC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Authentication utilities for Basecamp API
|
|
3
|
+
*/
|
|
4
|
+
import { buildClient, getBearerToken } from "basecamp-client";
|
|
5
|
+
/** Cached bearer token to avoid repeated OAuth requests */
|
|
6
|
+
let cachedBearerToken = null;
|
|
7
|
+
/**
|
|
8
|
+
* Initialize and return an authenticated Basecamp client.
|
|
9
|
+
*
|
|
10
|
+
* Uses environment variables:
|
|
11
|
+
* - BASECAMP_CLIENT_ID
|
|
12
|
+
* - BASECAMP_CLIENT_SECRET
|
|
13
|
+
* - BASECAMP_REFRESH_TOKEN
|
|
14
|
+
* - BASECAMP_USER_AGENT (optional)
|
|
15
|
+
* - BASECAMP_ACCOUNT_ID
|
|
16
|
+
*
|
|
17
|
+
* @param accountId - Basecamp account ID to use for the client
|
|
18
|
+
* @returns Authenticated Basecamp client instance
|
|
19
|
+
* @throws Error if required environment variables are missing or authentication fails
|
|
20
|
+
*/
|
|
21
|
+
export async function initializeBasecampClient() {
|
|
22
|
+
// Validate required environment variables
|
|
23
|
+
const requiredEnvVars = [
|
|
24
|
+
"BASECAMP_CLIENT_ID",
|
|
25
|
+
"BASECAMP_CLIENT_SECRET",
|
|
26
|
+
"BASECAMP_REFRESH_TOKEN",
|
|
27
|
+
"BASECAMP_ACCOUNT_ID",
|
|
28
|
+
];
|
|
29
|
+
const missing = requiredEnvVars.filter((varName) => !process.env[varName]);
|
|
30
|
+
if (missing.length > 0) {
|
|
31
|
+
throw new Error(`Missing required environment variables: ${missing.join(", ")}. ` +
|
|
32
|
+
`Please set these in your environment or .env file.`);
|
|
33
|
+
}
|
|
34
|
+
// Get bearer token (cache if possible to avoid repeated OAuth requests)
|
|
35
|
+
if (!cachedBearerToken) {
|
|
36
|
+
try {
|
|
37
|
+
cachedBearerToken = await getBearerToken({
|
|
38
|
+
clientId: process.env.BASECAMP_CLIENT_ID,
|
|
39
|
+
clientSecret: process.env.BASECAMP_CLIENT_SECRET,
|
|
40
|
+
refreshToken: process.env.BASECAMP_REFRESH_TOKEN,
|
|
41
|
+
userAgent: process.env.BASECAMP_USER_AGENT,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
throw new Error(`Failed to obtain Basecamp access token: ${error instanceof Error ? error.message : String(error)}. ` +
|
|
46
|
+
`Check your BASECAMP_CLIENT_ID, BASECAMP_CLIENT_SECRET, and BASECAMP_REFRESH_TOKEN are correct.`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Build and return client
|
|
50
|
+
return buildClient({
|
|
51
|
+
bearerToken: cachedBearerToken,
|
|
52
|
+
accountId: process.env.BASECAMP_ACCOUNT_ID,
|
|
53
|
+
userAgent: process.env.BASECAMP_USER_AGENT,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Clear the cached bearer token (useful for forcing token refresh)
|
|
58
|
+
*/
|
|
59
|
+
export function clearTokenCache() {
|
|
60
|
+
cachedBearerToken = null;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/utils/auth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAe,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAE3E,2DAA2D;AAC3D,IAAI,iBAAiB,GAAkB,IAAI,CAAC;AAE5C;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB;IAC5C,0CAA0C;IAC1C,MAAM,eAAe,GAAG;QACtB,oBAAoB;QACpB,wBAAwB;QACxB,wBAAwB;QACxB,qBAAqB;KACtB,CAAC;IAEF,MAAM,OAAO,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3E,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,2CAA2C,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI;YAC/D,oDAAoD,CACvD,CAAC;IACJ,CAAC;IAED,wEAAwE;IACxE,IAAI,CAAC,iBAAiB,EAAE,CAAC;QACvB,IAAI,CAAC;YACH,iBAAiB,GAAG,MAAM,cAAc,CAAC;gBACvC,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAmB;gBACzC,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAuB;gBACjD,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAuB;gBACjD,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB;aAC3C,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CACb,2CAA2C,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI;gBACnG,gGAAgG,CACnG,CAAC;QACJ,CAAC;IACH,CAAC;IAED,0BAA0B;IAC1B,OAAO,WAAW,CAAC;QACjB,WAAW,EAAE,iBAAiB;QAC9B,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAoB;QAC3C,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,mBAAmB;KAC3C,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,iBAAiB,GAAG,IAAI,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities and schemas for content manipulation operations
|
|
3
|
+
* Used by messages, comments, and other content-based tools
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
export declare const htmlRules = "\n\nHTML rules for content:\n\n* Allowed tags: div, h1, br, strong, em, strike, a (with an href attribute), pre, ol, ul, li, blockquote, bc-attachment (with sgid attribute).\n* Try to be semantic despite the limitations of tags. Use double <br> as paragraphs\n* To mention people: <bc-attachment sgid=\"{ person.attachable_sgid }\"></bc-attachment>\n";
|
|
7
|
+
/**
|
|
8
|
+
* Shared Zod schema for content operation fields
|
|
9
|
+
* These fields can be composed into tool-specific schemas
|
|
10
|
+
*/
|
|
11
|
+
export declare const ContentOperationFields: {
|
|
12
|
+
content: z.ZodOptional<z.ZodString>;
|
|
13
|
+
content_append: z.ZodOptional<z.ZodString>;
|
|
14
|
+
content_prepend: z.ZodOptional<z.ZodString>;
|
|
15
|
+
search_replace: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
16
|
+
find: z.ZodString;
|
|
17
|
+
replace: z.ZodString;
|
|
18
|
+
}, "strip", z.ZodTypeAny, {
|
|
19
|
+
find: string;
|
|
20
|
+
replace: string;
|
|
21
|
+
}, {
|
|
22
|
+
find: string;
|
|
23
|
+
replace: string;
|
|
24
|
+
}>, "many">>;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Parameters for applying content operations
|
|
28
|
+
*/
|
|
29
|
+
export interface ContentOperationParams {
|
|
30
|
+
content?: string;
|
|
31
|
+
content_append?: string;
|
|
32
|
+
content_prepend?: string;
|
|
33
|
+
search_replace?: Array<{
|
|
34
|
+
find: string;
|
|
35
|
+
replace: string;
|
|
36
|
+
}>;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Apply content operations to existing content
|
|
40
|
+
*
|
|
41
|
+
* @param currentContent - The current content to operate on
|
|
42
|
+
* @param operations - The operations to apply
|
|
43
|
+
* @returns The final content after applying all operations, or undefined if no operations
|
|
44
|
+
* @throws Error if validation fails (mutual exclusivity, no operations provided)
|
|
45
|
+
*/
|
|
46
|
+
export declare function applyContentOperations(currentContent: string, operations: ContentOperationParams): string | undefined;
|
|
47
|
+
/**
|
|
48
|
+
* Validate that at least one content operation is provided
|
|
49
|
+
*
|
|
50
|
+
* @param operations - The operations to validate
|
|
51
|
+
* @param additionalFields - Additional field names that count as valid operations
|
|
52
|
+
* @throws Error if no operations are provided
|
|
53
|
+
*/
|
|
54
|
+
export declare function validateContentOperations(operations: ContentOperationParams, additionalFields?: string[]): void;
|
|
55
|
+
//# sourceMappingURL=contentOperations.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentOperations.d.ts","sourceRoot":"","sources":["../../src/utils/contentOperations.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,SAAS,mWAOrB,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;CAgClC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3D;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,UAAU,EAAE,sBAAsB,GACjC,MAAM,GAAG,SAAS,CAkDpB;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,UAAU,EAAE,sBAAsB,EAClC,gBAAgB,GAAE,MAAM,EAAO,GAC9B,IAAI,CAmBN"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared utilities and schemas for content manipulation operations
|
|
3
|
+
* Used by messages, comments, and other content-based tools
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
export const htmlRules = `
|
|
7
|
+
|
|
8
|
+
HTML rules for content:
|
|
9
|
+
|
|
10
|
+
* Allowed tags: div, h1, br, strong, em, strike, a (with an href attribute), pre, ol, ul, li, blockquote, bc-attachment (with sgid attribute).
|
|
11
|
+
* Try to be semantic despite the limitations of tags. Use double <br> as paragraphs
|
|
12
|
+
* To mention people: <bc-attachment sgid="{ person.attachable_sgid }"></bc-attachment>
|
|
13
|
+
`;
|
|
14
|
+
/**
|
|
15
|
+
* Shared Zod schema for content operation fields
|
|
16
|
+
* These fields can be composed into tool-specific schemas
|
|
17
|
+
*/
|
|
18
|
+
export const ContentOperationFields = {
|
|
19
|
+
content: z
|
|
20
|
+
.string()
|
|
21
|
+
.optional()
|
|
22
|
+
.describe(`If provided, replaces entire HTML content. Cannot be used with content_append, content_prepend, or search_replace.`),
|
|
23
|
+
content_append: z
|
|
24
|
+
.string()
|
|
25
|
+
.optional()
|
|
26
|
+
.describe("Text to append to the end of current content. Cannot be used with content."),
|
|
27
|
+
content_prepend: z
|
|
28
|
+
.string()
|
|
29
|
+
.optional()
|
|
30
|
+
.describe("Text to prepend to the beginning of current content. Cannot be used with content."),
|
|
31
|
+
search_replace: z
|
|
32
|
+
.array(z.object({
|
|
33
|
+
find: z.string().describe("Text to search for"),
|
|
34
|
+
replace: z
|
|
35
|
+
.string()
|
|
36
|
+
.describe("Text to replace ALL the occurrences with"),
|
|
37
|
+
}))
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Array of search-replace operations to apply to current content. Cannot be used with content."),
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Apply content operations to existing content
|
|
43
|
+
*
|
|
44
|
+
* @param currentContent - The current content to operate on
|
|
45
|
+
* @param operations - The operations to apply
|
|
46
|
+
* @returns The final content after applying all operations, or undefined if no operations
|
|
47
|
+
* @throws Error if validation fails (mutual exclusivity, no operations provided)
|
|
48
|
+
*/
|
|
49
|
+
export function applyContentOperations(currentContent, operations) {
|
|
50
|
+
const hasPartialOps = operations.content_append ||
|
|
51
|
+
operations.content_prepend ||
|
|
52
|
+
operations.search_replace;
|
|
53
|
+
// Validate mutual exclusivity
|
|
54
|
+
if (operations.content && hasPartialOps) {
|
|
55
|
+
throw new Error("Cannot use 'content' with partial operations (content_append, content_prepend, search_replace). Use either full replacement or partial operations, not both.");
|
|
56
|
+
}
|
|
57
|
+
// If full content replacement, return it directly
|
|
58
|
+
if (operations.content !== undefined) {
|
|
59
|
+
return operations.content;
|
|
60
|
+
}
|
|
61
|
+
// If no operations at all, return undefined (no changes)
|
|
62
|
+
if (!hasPartialOps) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
// Apply partial operations
|
|
66
|
+
let finalContent = currentContent;
|
|
67
|
+
// Apply search-replace operations first
|
|
68
|
+
if (operations.search_replace) {
|
|
69
|
+
for (const operation of operations.search_replace) {
|
|
70
|
+
// Check if the search string exists in the content
|
|
71
|
+
if (!finalContent.includes(operation.find)) {
|
|
72
|
+
throw new Error(`Search string not found: "${operation.find}". The content does not contain this text.`);
|
|
73
|
+
}
|
|
74
|
+
finalContent = finalContent.replaceAll(operation.find, operation.replace);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// Apply prepend
|
|
78
|
+
if (operations.content_prepend) {
|
|
79
|
+
finalContent = operations.content_prepend + finalContent;
|
|
80
|
+
}
|
|
81
|
+
// Apply append
|
|
82
|
+
if (operations.content_append) {
|
|
83
|
+
finalContent = finalContent + operations.content_append;
|
|
84
|
+
}
|
|
85
|
+
return finalContent;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validate that at least one content operation is provided
|
|
89
|
+
*
|
|
90
|
+
* @param operations - The operations to validate
|
|
91
|
+
* @param additionalFields - Additional field names that count as valid operations
|
|
92
|
+
* @throws Error if no operations are provided
|
|
93
|
+
*/
|
|
94
|
+
export function validateContentOperations(operations, additionalFields = []) {
|
|
95
|
+
const hasContentOp = operations.content ||
|
|
96
|
+
operations.content_append ||
|
|
97
|
+
operations.content_prepend ||
|
|
98
|
+
operations.search_replace;
|
|
99
|
+
const hasAdditionalFields = additionalFields.some((field) => operations[field] !== undefined);
|
|
100
|
+
if (!hasContentOp && !hasAdditionalFields) {
|
|
101
|
+
const fieldsStr = [
|
|
102
|
+
"content",
|
|
103
|
+
"partial operations",
|
|
104
|
+
...additionalFields,
|
|
105
|
+
].join(", ");
|
|
106
|
+
throw new Error(`At least one field (${fieldsStr}) must be provided`);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=contentOperations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contentOperations.js","sourceRoot":"","sources":["../../src/utils/contentOperations.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities for Basecamp API
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Convert Basecamp API errors to helpful, actionable error messages for LLMs.
|
|
6
|
+
*
|
|
7
|
+
* @param error - Error object from API call
|
|
8
|
+
* @returns Human-readable error message with guidance
|
|
9
|
+
*/
|
|
10
|
+
export declare function handleBasecampError(error: unknown): string;
|
|
11
|
+
//# sourceMappingURL=errorHandlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorHandlers.d.ts","sourceRoot":"","sources":["../../src/utils/errorHandlers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CA8E1D"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities for Basecamp API
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Convert Basecamp API errors to helpful, actionable error messages for LLMs.
|
|
6
|
+
*
|
|
7
|
+
* @param error - Error object from API call
|
|
8
|
+
* @returns Human-readable error message with guidance
|
|
9
|
+
*/
|
|
10
|
+
export function handleBasecampError(error) {
|
|
11
|
+
// Handle HTTP response errors
|
|
12
|
+
if (error && typeof error === "object" && "status" in error) {
|
|
13
|
+
const status = error.status;
|
|
14
|
+
switch (status) {
|
|
15
|
+
case 400:
|
|
16
|
+
return ("Error: Bad request. Check that all required parameters are provided and formatted correctly. " +
|
|
17
|
+
extractErrorDetails(error));
|
|
18
|
+
case 401:
|
|
19
|
+
return ("Error: Authentication failed. Your access token may have expired. " +
|
|
20
|
+
"Try restarting the server to refresh the token, or check your BASECAMP_REFRESH_TOKEN is valid.");
|
|
21
|
+
case 403:
|
|
22
|
+
return ("Error: Access denied. You don't have permission to access this resource. " +
|
|
23
|
+
"Check that you're using the correct account ID and that your Basecamp user has access to this project/resource.");
|
|
24
|
+
case 404:
|
|
25
|
+
return ("Error: Resource not found. The requested resource (message, todo, project, etc.) doesn't exist or has been deleted. " +
|
|
26
|
+
"Verify the ID is correct and the resource hasn't been moved to trash.");
|
|
27
|
+
case 422:
|
|
28
|
+
return ("Error: Validation failed. The request data didn't pass Basecamp's validation rules. " +
|
|
29
|
+
extractErrorDetails(error) +
|
|
30
|
+
" Check the data format and required fields.");
|
|
31
|
+
case 429:
|
|
32
|
+
return ("Error: Rate limit exceeded. Too many requests have been made to the Basecamp API. " +
|
|
33
|
+
"Please wait a moment before trying again.");
|
|
34
|
+
case 500:
|
|
35
|
+
case 502:
|
|
36
|
+
case 503:
|
|
37
|
+
case 504:
|
|
38
|
+
return ("Error: Basecamp server error. The Basecamp API is experiencing issues. " +
|
|
39
|
+
"Please try again in a moment.");
|
|
40
|
+
default:
|
|
41
|
+
return `Error: API request failed with status ${status}. ${extractErrorDetails(error)}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Handle network/connection errors
|
|
45
|
+
if (error && typeof error === "object" && "code" in error) {
|
|
46
|
+
const code = error.code;
|
|
47
|
+
if (code === "ECONNABORTED" || code === "ETIMEDOUT") {
|
|
48
|
+
return ("Error: Request timed out. The Basecamp API took too long to respond. " +
|
|
49
|
+
"Please try again.");
|
|
50
|
+
}
|
|
51
|
+
if (code === "ECONNREFUSED" || code === "ENOTFOUND") {
|
|
52
|
+
return ("Error: Unable to connect to Basecamp API. " +
|
|
53
|
+
"Check your internet connection and try again.");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Generic error fallback
|
|
57
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extract additional error details from API response if available.
|
|
61
|
+
*
|
|
62
|
+
* @param error - Error object
|
|
63
|
+
* @returns Error details string or empty string
|
|
64
|
+
*/
|
|
65
|
+
function extractErrorDetails(error) {
|
|
66
|
+
if (!error || typeof error !== "object") {
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
// Try to extract error message from response body
|
|
70
|
+
if ("body" in error && error.body && typeof error.body === "object") {
|
|
71
|
+
const body = error.body;
|
|
72
|
+
if ("error" in body && typeof body.error === "string") {
|
|
73
|
+
return `Details: ${body.error}`;
|
|
74
|
+
}
|
|
75
|
+
if ("message" in body && typeof body.message === "string") {
|
|
76
|
+
return `Details: ${body.message}`;
|
|
77
|
+
}
|
|
78
|
+
if ("errors" in body && Array.isArray(body.errors)) {
|
|
79
|
+
const errorMessages = body.errors
|
|
80
|
+
.map((e) => (typeof e === "string" ? e : JSON.stringify(e)))
|
|
81
|
+
.join(", ");
|
|
82
|
+
return `Details: ${errorMessages}`;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return "";
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=errorHandlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errorHandlers.js","sourceRoot":"","sources":["../../src/utils/errorHandlers.ts"],"names":[],"mappings":"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"}
|