contextforge-mcp 0.1.73 → 0.1.75
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/dist/api-client.d.ts +15 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +27 -0
- package/dist/api-client.js.map +1 -1
- package/dist/index.js +330 -94
- package/dist/index.js.map +1 -1
- package/dist/task-params.d.ts +19 -0
- package/dist/task-params.d.ts.map +1 -0
- package/dist/task-params.js +37 -0
- package/dist/task-params.js.map +1 -0
- package/dist/types.d.ts +93 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +34 -0
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,8 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
5
5
|
import { ApiClient, ApiClientError, readProjectLinkConfig, } from "./api-client.js";
|
|
6
|
-
import { ConfigSchema, IngestInputSchema, QueryInputSchema, CreateProjectInputSchema, CreateSpaceInputSchema, RelateInputSchema, ListRelationshipsInputSchema, DeleteInputSchema, GitConnectInputSchema, GitActivateInputSchema, GitDisconnectInputSchema, GitSyncInputSchema, GitHistoryInputSchema, SnapshotCreateInputSchema, SnapshotRestoreInputSchema, SnapshotDeleteInputSchema, ExportInputSchema, ImportInputSchema, IngestBatchInputSchema, DeleteBatchInputSchema, LinkProjectInputSchema, parseArrayInput, } from "./types.js";
|
|
6
|
+
import { ConfigSchema, IngestInputSchema, QueryInputSchema, CreateProjectInputSchema, CreateSpaceInputSchema, RelateInputSchema, ListRelationshipsInputSchema, DeleteInputSchema, GitConnectInputSchema, GitActivateInputSchema, GitDisconnectInputSchema, GitSyncInputSchema, GitHistoryInputSchema, SnapshotCreateInputSchema, SnapshotRestoreInputSchema, SnapshotDeleteInputSchema, ExportInputSchema, ImportInputSchema, IngestBatchInputSchema, DeleteBatchInputSchema, LinkProjectInputSchema, SkillsListInputSchema, SkillsGetInputSchema, SkillsCreateInputSchema, SkillsUpdateInputSchema, SkillsDeleteInputSchema, SkillsRunInputSchema, parseArrayInput, } from "./types.js";
|
|
7
|
+
import { resolveTaskIdentifier } from "./task-params.js";
|
|
7
8
|
import { appendFileSync } from "fs";
|
|
8
9
|
import { createRequire } from "module";
|
|
9
10
|
import { checkForUpdates, getUpdateNotice } from "./update-checker.js";
|
|
@@ -88,6 +89,12 @@ function logTool(toolName, details) {
|
|
|
88
89
|
tasks_add_comment: "💬",
|
|
89
90
|
collaborators_list: "👥",
|
|
90
91
|
project_share: "🔗",
|
|
92
|
+
skills_list: "🧰",
|
|
93
|
+
skills_get: "🔧",
|
|
94
|
+
skills_create: "➕",
|
|
95
|
+
skills_update: "✏️",
|
|
96
|
+
skills_delete: "🗑️",
|
|
97
|
+
skills_run: "▶️",
|
|
91
98
|
};
|
|
92
99
|
const icon = icons[toolName] || "🔧";
|
|
93
100
|
log(icon, `${colors.bright}${toolName}${colors.reset}${details ? ` ${colors.dim}${details}${colors.reset}` : ""}`, colors.cyan);
|
|
@@ -1009,32 +1016,32 @@ const TOOLS = [
|
|
|
1009
1016
|
},
|
|
1010
1017
|
{
|
|
1011
1018
|
name: "tasks_start",
|
|
1012
|
-
description: 'Mark a task as "in_progress". Use this when you start working on a task. The response includes a dashboard URL — always show it to the user.',
|
|
1019
|
+
description: 'Mark a task as "in_progress". Use this when you start working on a task. Accepts any identifier: UUID, short_id, or task title. The response includes a dashboard URL — always show it to the user.',
|
|
1013
1020
|
annotations: { title: "Start Task", destructiveHint: true },
|
|
1014
1021
|
inputSchema: {
|
|
1015
1022
|
type: "object",
|
|
1016
1023
|
properties: {
|
|
1017
|
-
|
|
1024
|
+
identifier: {
|
|
1018
1025
|
type: "string",
|
|
1019
|
-
description: "Task UUID
|
|
1026
|
+
description: "Task identifier — can be a UUID, short_id (e.g. 'hpiu09'), or task title/name",
|
|
1020
1027
|
},
|
|
1021
1028
|
},
|
|
1022
|
-
required: ["
|
|
1029
|
+
required: ["identifier"],
|
|
1023
1030
|
},
|
|
1024
1031
|
},
|
|
1025
1032
|
{
|
|
1026
1033
|
name: "tasks_resolve",
|
|
1027
|
-
description: 'Mark a task as "resolved". Use this when you finish working on a task. The response includes a dashboard URL — always show it to the user.',
|
|
1034
|
+
description: 'Mark a task as "resolved". Use this when you finish working on a task. Accepts any identifier: UUID, short_id, or task title. The response includes a dashboard URL — always show it to the user.',
|
|
1028
1035
|
annotations: { title: "Resolve Task", destructiveHint: true },
|
|
1029
1036
|
inputSchema: {
|
|
1030
1037
|
type: "object",
|
|
1031
1038
|
properties: {
|
|
1032
|
-
|
|
1039
|
+
identifier: {
|
|
1033
1040
|
type: "string",
|
|
1034
|
-
description: "Task UUID
|
|
1041
|
+
description: "Task identifier — can be a UUID, short_id (e.g. 'hpiu09'), or task title/name",
|
|
1035
1042
|
},
|
|
1036
1043
|
},
|
|
1037
|
-
required: ["
|
|
1044
|
+
required: ["identifier"],
|
|
1038
1045
|
},
|
|
1039
1046
|
},
|
|
1040
1047
|
{
|
|
@@ -1099,18 +1106,14 @@ const TOOLS = [
|
|
|
1099
1106
|
},
|
|
1100
1107
|
{
|
|
1101
1108
|
name: "tasks_update",
|
|
1102
|
-
description: "Update a task's title, description, status, priority, tags, due date, or assignee.
|
|
1109
|
+
description: "Update a task's title, description, status, priority, tags, due date, or assignee. Accepts any identifier: UUID, short_id, or task title. The response includes a dashboard URL — always show it to the user.",
|
|
1103
1110
|
annotations: { title: "Update Task", destructiveHint: true },
|
|
1104
1111
|
inputSchema: {
|
|
1105
1112
|
type: "object",
|
|
1106
1113
|
properties: {
|
|
1107
|
-
|
|
1114
|
+
identifier: {
|
|
1108
1115
|
type: "string",
|
|
1109
|
-
description: "Task UUID (
|
|
1110
|
-
},
|
|
1111
|
-
short_id: {
|
|
1112
|
-
type: "string",
|
|
1113
|
-
description: 'Short ID of the task (e.g., "4w3123") - alternative to issue_id',
|
|
1116
|
+
description: "Task identifier — can be a UUID, short_id (e.g. 'hpiu09'), or task title/name",
|
|
1114
1117
|
},
|
|
1115
1118
|
title: {
|
|
1116
1119
|
type: "string",
|
|
@@ -1149,41 +1152,33 @@ const TOOLS = [
|
|
|
1149
1152
|
},
|
|
1150
1153
|
{
|
|
1151
1154
|
name: "tasks_assign",
|
|
1152
|
-
description: "Assign a task to a collaborator by their email address. The response includes a dashboard URL — always show it to the user.",
|
|
1155
|
+
description: "Assign a task to a collaborator by their email address. Accepts any task identifier: UUID, short_id, or task title. The response includes a dashboard URL — always show it to the user.",
|
|
1153
1156
|
annotations: { title: "Assign Task", destructiveHint: true },
|
|
1154
1157
|
inputSchema: {
|
|
1155
1158
|
type: "object",
|
|
1156
1159
|
properties: {
|
|
1157
|
-
|
|
1160
|
+
identifier: {
|
|
1158
1161
|
type: "string",
|
|
1159
|
-
description: "Task
|
|
1160
|
-
},
|
|
1161
|
-
short_id: {
|
|
1162
|
-
type: "string",
|
|
1163
|
-
description: 'Short ID of the task (e.g., "4w3123") - easier to reference than full UUID',
|
|
1164
|
-
},
|
|
1165
|
-
issue_title: {
|
|
1166
|
-
type: "string",
|
|
1167
|
-
description: "Task title to search for (optional if issue_id or short_id is provided)",
|
|
1162
|
+
description: "Task identifier — can be a UUID, short_id (e.g. 'hpiu09'), or task title/name",
|
|
1168
1163
|
},
|
|
1169
1164
|
assignee_email: {
|
|
1170
1165
|
type: "string",
|
|
1171
1166
|
description: "Email of the collaborator to assign the task to",
|
|
1172
1167
|
},
|
|
1173
1168
|
},
|
|
1174
|
-
required: ["assignee_email"],
|
|
1169
|
+
required: ["identifier", "assignee_email"],
|
|
1175
1170
|
},
|
|
1176
1171
|
},
|
|
1177
1172
|
{
|
|
1178
1173
|
name: "tasks_resolve_by_name",
|
|
1179
|
-
description: "Resolve a task by searching for it by title. Use this when you
|
|
1174
|
+
description: "Resolve a task by searching for it by title, short_id, or UUID. Use this when you have any identifier for the task. The response includes a dashboard URL — always show it to the user.",
|
|
1180
1175
|
annotations: { title: "Resolve Task by Name", destructiveHint: true },
|
|
1181
1176
|
inputSchema: {
|
|
1182
1177
|
type: "object",
|
|
1183
1178
|
properties: {
|
|
1184
1179
|
title: {
|
|
1185
1180
|
type: "string",
|
|
1186
|
-
description: "Task
|
|
1181
|
+
description: "Task identifier — can be a title (partial match), short_id, or UUID",
|
|
1187
1182
|
},
|
|
1188
1183
|
},
|
|
1189
1184
|
required: ["title"],
|
|
@@ -1191,52 +1186,52 @@ const TOOLS = [
|
|
|
1191
1186
|
},
|
|
1192
1187
|
{
|
|
1193
1188
|
name: "tasks_delete",
|
|
1194
|
-
description: "Permanently delete a task
|
|
1189
|
+
description: "Permanently delete a task. Accepts any identifier: UUID, short_id, or task title. Also deletes related comments, activity, and notifications.",
|
|
1195
1190
|
annotations: { title: "Delete Task", destructiveHint: true },
|
|
1196
1191
|
inputSchema: {
|
|
1197
1192
|
type: "object",
|
|
1198
1193
|
properties: {
|
|
1199
|
-
|
|
1194
|
+
identifier: {
|
|
1200
1195
|
type: "string",
|
|
1201
|
-
description: "Task UUID
|
|
1196
|
+
description: "Task identifier — can be a UUID, short_id (e.g. 'hpiu09'), or task title/name",
|
|
1202
1197
|
},
|
|
1203
1198
|
},
|
|
1204
|
-
required: ["
|
|
1199
|
+
required: ["identifier"],
|
|
1205
1200
|
},
|
|
1206
1201
|
},
|
|
1207
1202
|
// ============ Task Comments ============
|
|
1208
1203
|
{
|
|
1209
1204
|
name: "tasks_list_comments",
|
|
1210
|
-
description: "List comments on a task.
|
|
1205
|
+
description: "List comments on a task. Accepts any identifier: UUID, short_id, or task title.",
|
|
1211
1206
|
annotations: { title: "List Task Comments", readOnlyHint: true },
|
|
1212
1207
|
inputSchema: {
|
|
1213
1208
|
type: "object",
|
|
1214
1209
|
properties: {
|
|
1215
|
-
|
|
1210
|
+
identifier: {
|
|
1216
1211
|
type: "string",
|
|
1217
|
-
description: "Task UUID
|
|
1212
|
+
description: "Task identifier — can be a UUID, short_id (e.g. 'hpiu09'), or task title/name",
|
|
1218
1213
|
},
|
|
1219
1214
|
},
|
|
1220
|
-
required: ["
|
|
1215
|
+
required: ["identifier"],
|
|
1221
1216
|
},
|
|
1222
1217
|
},
|
|
1223
1218
|
{
|
|
1224
1219
|
name: "tasks_add_comment",
|
|
1225
|
-
description: "Add a comment to a task. The response includes the task dashboard URL — always show it to the user.",
|
|
1220
|
+
description: "Add a comment to a task. Accepts any task identifier: UUID, short_id, or task title. The response includes the task dashboard URL — always show it to the user.",
|
|
1226
1221
|
annotations: { title: "Add Task Comment", destructiveHint: true },
|
|
1227
1222
|
inputSchema: {
|
|
1228
1223
|
type: "object",
|
|
1229
1224
|
properties: {
|
|
1230
|
-
|
|
1225
|
+
identifier: {
|
|
1231
1226
|
type: "string",
|
|
1232
|
-
description: "Task UUID
|
|
1227
|
+
description: "Task identifier — can be a UUID, short_id (e.g. 'hpiu09'), or task title/name",
|
|
1233
1228
|
},
|
|
1234
1229
|
content: {
|
|
1235
1230
|
type: "string",
|
|
1236
1231
|
description: "Comment text to add",
|
|
1237
1232
|
},
|
|
1238
1233
|
},
|
|
1239
|
-
required: ["
|
|
1234
|
+
required: ["identifier", "content"],
|
|
1240
1235
|
},
|
|
1241
1236
|
},
|
|
1242
1237
|
// ============ Collaborators ============
|
|
@@ -1287,6 +1282,109 @@ const TOOLS = [
|
|
|
1287
1282
|
required: ["email"],
|
|
1288
1283
|
},
|
|
1289
1284
|
},
|
|
1285
|
+
{
|
|
1286
|
+
name: "skills_list",
|
|
1287
|
+
description: "List all Skills in a project. Returns skills with their name, description, model, and prompt body.",
|
|
1288
|
+
annotations: { title: "List Skills", readOnlyHint: true },
|
|
1289
|
+
inputSchema: {
|
|
1290
|
+
type: "object",
|
|
1291
|
+
properties: {
|
|
1292
|
+
project_id: { type: "string", description: "Project UUID" },
|
|
1293
|
+
},
|
|
1294
|
+
required: ["project_id"],
|
|
1295
|
+
},
|
|
1296
|
+
},
|
|
1297
|
+
{
|
|
1298
|
+
name: "skills_get",
|
|
1299
|
+
description: "Get a single Skill by ID with full body.",
|
|
1300
|
+
annotations: { title: "Get Skill", readOnlyHint: true },
|
|
1301
|
+
inputSchema: {
|
|
1302
|
+
type: "object",
|
|
1303
|
+
properties: {
|
|
1304
|
+
id: { type: "string", description: "Skill UUID" },
|
|
1305
|
+
},
|
|
1306
|
+
required: ["id"],
|
|
1307
|
+
},
|
|
1308
|
+
},
|
|
1309
|
+
{
|
|
1310
|
+
name: "skills_create",
|
|
1311
|
+
description: "Create a new Skill in a project. The 'body' is a markdown prompt template that may use {{variable}} placeholders for input_params at run time.",
|
|
1312
|
+
annotations: { title: "Create Skill", destructiveHint: false },
|
|
1313
|
+
inputSchema: {
|
|
1314
|
+
type: "object",
|
|
1315
|
+
properties: {
|
|
1316
|
+
project_id: { type: "string" },
|
|
1317
|
+
name: { type: "string" },
|
|
1318
|
+
description: { type: "string" },
|
|
1319
|
+
body: {
|
|
1320
|
+
type: "string",
|
|
1321
|
+
description: "Markdown prompt template with optional {{var}} placeholders",
|
|
1322
|
+
},
|
|
1323
|
+
input_schema: {
|
|
1324
|
+
type: "object",
|
|
1325
|
+
description: "Optional JSON Schema for input_params",
|
|
1326
|
+
},
|
|
1327
|
+
llm_provider: {
|
|
1328
|
+
type: "string",
|
|
1329
|
+
enum: ["anthropic", "openai"],
|
|
1330
|
+
description: "Default 'anthropic'",
|
|
1331
|
+
},
|
|
1332
|
+
model: { type: "string", description: "e.g., claude-sonnet-4-6" },
|
|
1333
|
+
save_to_space_id: {
|
|
1334
|
+
type: "string",
|
|
1335
|
+
description: "Optional: save outputs as knowledge_items in this space",
|
|
1336
|
+
},
|
|
1337
|
+
},
|
|
1338
|
+
required: ["project_id", "name", "body", "model"],
|
|
1339
|
+
},
|
|
1340
|
+
},
|
|
1341
|
+
{
|
|
1342
|
+
name: "skills_update",
|
|
1343
|
+
description: "Update an existing Skill.",
|
|
1344
|
+
annotations: { title: "Update Skill", destructiveHint: true },
|
|
1345
|
+
inputSchema: {
|
|
1346
|
+
type: "object",
|
|
1347
|
+
properties: {
|
|
1348
|
+
id: { type: "string" },
|
|
1349
|
+
name: { type: "string" },
|
|
1350
|
+
description: { type: "string" },
|
|
1351
|
+
body: { type: "string" },
|
|
1352
|
+
input_schema: { type: "object" },
|
|
1353
|
+
llm_provider: { type: "string", enum: ["anthropic", "openai"] },
|
|
1354
|
+
model: { type: "string" },
|
|
1355
|
+
save_to_space_id: { type: "string" },
|
|
1356
|
+
},
|
|
1357
|
+
required: ["id"],
|
|
1358
|
+
},
|
|
1359
|
+
},
|
|
1360
|
+
{
|
|
1361
|
+
name: "skills_delete",
|
|
1362
|
+
description: "Delete a Skill.",
|
|
1363
|
+
annotations: { title: "Delete Skill", destructiveHint: true },
|
|
1364
|
+
inputSchema: {
|
|
1365
|
+
type: "object",
|
|
1366
|
+
properties: {
|
|
1367
|
+
id: { type: "string" },
|
|
1368
|
+
},
|
|
1369
|
+
required: ["id"],
|
|
1370
|
+
},
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
name: "skills_run",
|
|
1374
|
+
description: "Execute a Skill on the configured LLM, optionally storing the output as a knowledge_item, and returns the result. Available to all project members.",
|
|
1375
|
+
annotations: { title: "Run Skill", destructiveHint: false },
|
|
1376
|
+
inputSchema: {
|
|
1377
|
+
type: "object",
|
|
1378
|
+
properties: {
|
|
1379
|
+
skill_id: { type: "string" },
|
|
1380
|
+
input_params: {
|
|
1381
|
+
type: "object",
|
|
1382
|
+
description: "Variables substituted into the skill body",
|
|
1383
|
+
},
|
|
1384
|
+
},
|
|
1385
|
+
required: ["skill_id"],
|
|
1386
|
+
},
|
|
1387
|
+
},
|
|
1290
1388
|
];
|
|
1291
1389
|
// ============ Main Server ============
|
|
1292
1390
|
async function main() {
|
|
@@ -2340,12 +2438,24 @@ async function main() {
|
|
|
2340
2438
|
};
|
|
2341
2439
|
}
|
|
2342
2440
|
case "tasks_start": {
|
|
2343
|
-
const
|
|
2344
|
-
if (!
|
|
2345
|
-
throw new Error("
|
|
2441
|
+
const ident = resolveTaskIdentifier(args);
|
|
2442
|
+
if (!ident) {
|
|
2443
|
+
throw new Error("identifier is required — pass a UUID, short_id, or task title");
|
|
2444
|
+
}
|
|
2445
|
+
logTool(name, ident.value);
|
|
2446
|
+
let result;
|
|
2447
|
+
if (ident.type === "title") {
|
|
2448
|
+
// Find by title first, then start
|
|
2449
|
+
const { issues } = await apiClient.listTasks({ status: "all", limit: 100 });
|
|
2450
|
+
const titleLower = ident.value.toLowerCase();
|
|
2451
|
+
const match = issues?.find((i) => i.title.toLowerCase().includes(titleLower) || i.title.toLowerCase() === titleLower);
|
|
2452
|
+
if (!match)
|
|
2453
|
+
throw new Error(`No task found matching "${ident.value}"`);
|
|
2454
|
+
result = await apiClient.updateTaskStatus(match.id, "in_progress");
|
|
2455
|
+
}
|
|
2456
|
+
else {
|
|
2457
|
+
result = await apiClient.updateTaskStatus(ident.value, "in_progress");
|
|
2346
2458
|
}
|
|
2347
|
-
logTool(name, issueId);
|
|
2348
|
-
const result = await apiClient.updateTaskStatus(issueId, "in_progress");
|
|
2349
2459
|
const elapsed = Date.now() - startTime;
|
|
2350
2460
|
logSuccess(`Started working on "${result.issue?.title}" in ${elapsed}ms`);
|
|
2351
2461
|
return {
|
|
@@ -2365,12 +2475,20 @@ async function main() {
|
|
|
2365
2475
|
};
|
|
2366
2476
|
}
|
|
2367
2477
|
case "tasks_resolve": {
|
|
2368
|
-
const
|
|
2369
|
-
if (!
|
|
2370
|
-
throw new Error("
|
|
2478
|
+
const ident = resolveTaskIdentifier(args);
|
|
2479
|
+
if (!ident) {
|
|
2480
|
+
throw new Error("identifier is required — pass a UUID, short_id, or task title");
|
|
2481
|
+
}
|
|
2482
|
+
logTool(name, ident.value);
|
|
2483
|
+
let result;
|
|
2484
|
+
if (ident.type === "title") {
|
|
2485
|
+
// Resolve by title search
|
|
2486
|
+
result = await apiClient.resolveTaskByName(ident.value);
|
|
2487
|
+
}
|
|
2488
|
+
else {
|
|
2489
|
+
// UUID or short_id — updateTaskStatus already handles both
|
|
2490
|
+
result = await apiClient.updateTaskStatus(ident.value, "resolved");
|
|
2371
2491
|
}
|
|
2372
|
-
logTool(name, issueId);
|
|
2373
|
-
const result = await apiClient.updateTaskStatus(issueId, "resolved");
|
|
2374
2492
|
const elapsed = Date.now() - startTime;
|
|
2375
2493
|
logSuccess(`Resolved "${result.issue?.title}" in ${elapsed}ms`);
|
|
2376
2494
|
return {
|
|
@@ -2471,21 +2589,19 @@ async function main() {
|
|
|
2471
2589
|
};
|
|
2472
2590
|
}
|
|
2473
2591
|
case "tasks_assign": {
|
|
2474
|
-
const
|
|
2475
|
-
const shortId = args?.short_id;
|
|
2476
|
-
const issueTitle = args?.issue_title || args?.title;
|
|
2592
|
+
const ident = resolveTaskIdentifier(args);
|
|
2477
2593
|
const assigneeEmail = args?.assignee_email;
|
|
2478
2594
|
if (!assigneeEmail) {
|
|
2479
2595
|
throw new Error("assignee_email is required");
|
|
2480
2596
|
}
|
|
2481
|
-
if (!
|
|
2482
|
-
throw new Error("
|
|
2597
|
+
if (!ident) {
|
|
2598
|
+
throw new Error("identifier is required — pass a UUID, short_id, or task title");
|
|
2483
2599
|
}
|
|
2484
2600
|
logTool(name, `→ ${assigneeEmail}`);
|
|
2485
2601
|
const result = await apiClient.assignTask({
|
|
2486
|
-
issue_id:
|
|
2487
|
-
short_id:
|
|
2488
|
-
issue_title:
|
|
2602
|
+
issue_id: ident.type === "uuid" ? ident.value : undefined,
|
|
2603
|
+
short_id: ident.type === "short_id" ? ident.value : undefined,
|
|
2604
|
+
issue_title: ident.type === "title" ? ident.value : undefined,
|
|
2489
2605
|
assignee_email: assigneeEmail,
|
|
2490
2606
|
});
|
|
2491
2607
|
const elapsed = Date.now() - startTime;
|
|
@@ -2500,12 +2616,19 @@ async function main() {
|
|
|
2500
2616
|
};
|
|
2501
2617
|
}
|
|
2502
2618
|
case "tasks_resolve_by_name": {
|
|
2503
|
-
const
|
|
2504
|
-
if (!
|
|
2505
|
-
throw new Error("
|
|
2619
|
+
const ident = resolveTaskIdentifier(args);
|
|
2620
|
+
if (!ident) {
|
|
2621
|
+
throw new Error("identifier is required — pass a title, short_id, or UUID");
|
|
2622
|
+
}
|
|
2623
|
+
logTool(name, `"${ident.value}"`);
|
|
2624
|
+
let result;
|
|
2625
|
+
if (ident.type === "title") {
|
|
2626
|
+
result = await apiClient.resolveTaskByName(ident.value);
|
|
2627
|
+
}
|
|
2628
|
+
else {
|
|
2629
|
+
// UUID or short_id — resolve directly
|
|
2630
|
+
result = await apiClient.updateTaskStatus(ident.value, "resolved");
|
|
2506
2631
|
}
|
|
2507
|
-
logTool(name, `"${title}"`);
|
|
2508
|
-
const result = await apiClient.resolveTaskByName(title);
|
|
2509
2632
|
const elapsed = Date.now() - startTime;
|
|
2510
2633
|
logSuccess(`Resolved "${result.issue?.title}" in ${elapsed}ms`);
|
|
2511
2634
|
return {
|
|
@@ -2521,12 +2644,21 @@ async function main() {
|
|
|
2521
2644
|
};
|
|
2522
2645
|
}
|
|
2523
2646
|
case "tasks_delete": {
|
|
2524
|
-
const
|
|
2525
|
-
if (!
|
|
2526
|
-
throw new Error("
|
|
2647
|
+
const ident = resolveTaskIdentifier(args);
|
|
2648
|
+
if (!ident) {
|
|
2649
|
+
throw new Error("identifier is required — pass a UUID, short_id, or task title");
|
|
2650
|
+
}
|
|
2651
|
+
logTool(name, ident.value);
|
|
2652
|
+
let deleteId = ident.value;
|
|
2653
|
+
if (ident.type === "title") {
|
|
2654
|
+
const { issues } = await apiClient.listTasks({ status: "all", limit: 100 });
|
|
2655
|
+
const titleLower = ident.value.toLowerCase();
|
|
2656
|
+
const match = issues?.find((i) => i.title.toLowerCase().includes(titleLower) || i.title.toLowerCase() === titleLower);
|
|
2657
|
+
if (!match)
|
|
2658
|
+
throw new Error(`No task found matching "${ident.value}"`);
|
|
2659
|
+
deleteId = match.id;
|
|
2527
2660
|
}
|
|
2528
|
-
|
|
2529
|
-
const result = await apiClient.deleteTask(issueId);
|
|
2661
|
+
const result = await apiClient.deleteTask(deleteId);
|
|
2530
2662
|
const elapsed = Date.now() - startTime;
|
|
2531
2663
|
logSuccess(`Deleted task "${result.title}" in ${elapsed}ms`);
|
|
2532
2664
|
return {
|
|
@@ -2542,10 +2674,20 @@ async function main() {
|
|
|
2542
2674
|
};
|
|
2543
2675
|
}
|
|
2544
2676
|
case "tasks_update": {
|
|
2545
|
-
const
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2677
|
+
const ident = resolveTaskIdentifier(args);
|
|
2678
|
+
if (!ident) {
|
|
2679
|
+
throw new Error("identifier is required — pass a UUID, short_id, or task title");
|
|
2680
|
+
}
|
|
2681
|
+
// Resolve title to UUID/short_id for the API
|
|
2682
|
+
let issueId = ident.type === "uuid" ? ident.value : undefined;
|
|
2683
|
+
let shortId = ident.type === "short_id" ? ident.value : undefined;
|
|
2684
|
+
if (ident.type === "title") {
|
|
2685
|
+
const { issues } = await apiClient.listTasks({ status: "all", limit: 100 });
|
|
2686
|
+
const titleLower = ident.value.toLowerCase();
|
|
2687
|
+
const match = issues?.find((i) => i.title.toLowerCase().includes(titleLower) || i.title.toLowerCase() === titleLower);
|
|
2688
|
+
if (!match)
|
|
2689
|
+
throw new Error(`No task found matching "${ident.value}"`);
|
|
2690
|
+
issueId = match.id;
|
|
2549
2691
|
}
|
|
2550
2692
|
const updateFields = {};
|
|
2551
2693
|
if (args?.title !== undefined)
|
|
@@ -2594,22 +2736,28 @@ async function main() {
|
|
|
2594
2736
|
};
|
|
2595
2737
|
}
|
|
2596
2738
|
case "tasks_list_comments": {
|
|
2597
|
-
const
|
|
2598
|
-
if (!
|
|
2599
|
-
throw new Error("
|
|
2739
|
+
const ident = resolveTaskIdentifier(args);
|
|
2740
|
+
if (!ident) {
|
|
2741
|
+
throw new Error("identifier is required — pass a UUID, short_id, or task title");
|
|
2600
2742
|
}
|
|
2601
|
-
logTool(name,
|
|
2602
|
-
// Resolve
|
|
2603
|
-
let resolvedId =
|
|
2604
|
-
|
|
2605
|
-
if (isShortId) {
|
|
2743
|
+
logTool(name, ident.value);
|
|
2744
|
+
// Resolve to UUID
|
|
2745
|
+
let resolvedId = ident.value;
|
|
2746
|
+
if (ident.type !== "uuid") {
|
|
2606
2747
|
const { issues } = await apiClient.listTasks({
|
|
2607
2748
|
status: "all",
|
|
2608
2749
|
limit: 100,
|
|
2609
2750
|
});
|
|
2610
|
-
|
|
2751
|
+
let match;
|
|
2752
|
+
if (ident.type === "short_id") {
|
|
2753
|
+
match = issues?.find((i) => i.short_id?.toLowerCase() === ident.value.toLowerCase());
|
|
2754
|
+
}
|
|
2755
|
+
else {
|
|
2756
|
+
const titleLower = ident.value.toLowerCase();
|
|
2757
|
+
match = issues?.find((i) => i.title.toLowerCase().includes(titleLower) || i.title.toLowerCase() === titleLower);
|
|
2758
|
+
}
|
|
2611
2759
|
if (!match) {
|
|
2612
|
-
throw new Error(`No task found
|
|
2760
|
+
throw new Error(`No task found matching "${ident.value}"`);
|
|
2613
2761
|
}
|
|
2614
2762
|
resolvedId = match.id;
|
|
2615
2763
|
}
|
|
@@ -2645,26 +2793,32 @@ async function main() {
|
|
|
2645
2793
|
};
|
|
2646
2794
|
}
|
|
2647
2795
|
case "tasks_add_comment": {
|
|
2648
|
-
const
|
|
2796
|
+
const ident = resolveTaskIdentifier(args);
|
|
2649
2797
|
const content = args?.content;
|
|
2650
|
-
if (!
|
|
2651
|
-
throw new Error("
|
|
2798
|
+
if (!ident) {
|
|
2799
|
+
throw new Error("identifier is required — pass a UUID, short_id, or task title");
|
|
2652
2800
|
}
|
|
2653
2801
|
if (!content) {
|
|
2654
2802
|
throw new Error("content is required");
|
|
2655
2803
|
}
|
|
2656
|
-
logTool(name,
|
|
2657
|
-
// Resolve
|
|
2658
|
-
let resolvedId =
|
|
2659
|
-
|
|
2660
|
-
if (isShortId) {
|
|
2804
|
+
logTool(name, ident.value);
|
|
2805
|
+
// Resolve to UUID
|
|
2806
|
+
let resolvedId = ident.value;
|
|
2807
|
+
if (ident.type !== "uuid") {
|
|
2661
2808
|
const { issues } = await apiClient.listTasks({
|
|
2662
2809
|
status: "all",
|
|
2663
2810
|
limit: 100,
|
|
2664
2811
|
});
|
|
2665
|
-
|
|
2812
|
+
let match;
|
|
2813
|
+
if (ident.type === "short_id") {
|
|
2814
|
+
match = issues?.find((i) => i.short_id?.toLowerCase() === ident.value.toLowerCase());
|
|
2815
|
+
}
|
|
2816
|
+
else {
|
|
2817
|
+
const titleLower = ident.value.toLowerCase();
|
|
2818
|
+
match = issues?.find((i) => i.title.toLowerCase().includes(titleLower) || i.title.toLowerCase() === titleLower);
|
|
2819
|
+
}
|
|
2666
2820
|
if (!match) {
|
|
2667
|
-
throw new Error(`No task found
|
|
2821
|
+
throw new Error(`No task found matching "${ident.value}"`);
|
|
2668
2822
|
}
|
|
2669
2823
|
resolvedId = match.id;
|
|
2670
2824
|
}
|
|
@@ -2968,6 +3122,88 @@ https://github.com/contextforge/contextforge-mcp
|
|
|
2968
3122
|
],
|
|
2969
3123
|
};
|
|
2970
3124
|
}
|
|
3125
|
+
case "skills_list": {
|
|
3126
|
+
const input = SkillsListInputSchema.parse(args);
|
|
3127
|
+
logTool(name, input.project_id);
|
|
3128
|
+
const skills = await apiClient.listSkills(input);
|
|
3129
|
+
return {
|
|
3130
|
+
content: [
|
|
3131
|
+
{
|
|
3132
|
+
type: "text",
|
|
3133
|
+
text: JSON.stringify(skills, null, 2),
|
|
3134
|
+
},
|
|
3135
|
+
],
|
|
3136
|
+
};
|
|
3137
|
+
}
|
|
3138
|
+
case "skills_get": {
|
|
3139
|
+
const input = SkillsGetInputSchema.parse(args);
|
|
3140
|
+
logTool(name, input.id);
|
|
3141
|
+
const skill = await apiClient.getSkill(input);
|
|
3142
|
+
return {
|
|
3143
|
+
content: [
|
|
3144
|
+
{
|
|
3145
|
+
type: "text",
|
|
3146
|
+
text: JSON.stringify(skill, null, 2),
|
|
3147
|
+
},
|
|
3148
|
+
],
|
|
3149
|
+
};
|
|
3150
|
+
}
|
|
3151
|
+
case "skills_create": {
|
|
3152
|
+
const input = SkillsCreateInputSchema.parse(args);
|
|
3153
|
+
logTool(name, `"${input.name}"`);
|
|
3154
|
+
const skill = await apiClient.createSkill(input);
|
|
3155
|
+
return {
|
|
3156
|
+
content: [
|
|
3157
|
+
{
|
|
3158
|
+
type: "text",
|
|
3159
|
+
text: `✅ Created skill ${skill.name} (id: ${skill.id})`,
|
|
3160
|
+
},
|
|
3161
|
+
],
|
|
3162
|
+
};
|
|
3163
|
+
}
|
|
3164
|
+
case "skills_update": {
|
|
3165
|
+
const input = SkillsUpdateInputSchema.parse(args);
|
|
3166
|
+
logTool(name, input.id);
|
|
3167
|
+
const skill = await apiClient.updateSkill(input);
|
|
3168
|
+
return {
|
|
3169
|
+
content: [
|
|
3170
|
+
{
|
|
3171
|
+
type: "text",
|
|
3172
|
+
text: `✅ Updated skill ${skill.name}`,
|
|
3173
|
+
},
|
|
3174
|
+
],
|
|
3175
|
+
};
|
|
3176
|
+
}
|
|
3177
|
+
case "skills_delete": {
|
|
3178
|
+
const input = SkillsDeleteInputSchema.parse(args);
|
|
3179
|
+
logTool(name, input.id);
|
|
3180
|
+
await apiClient.deleteSkill(input);
|
|
3181
|
+
return {
|
|
3182
|
+
content: [
|
|
3183
|
+
{
|
|
3184
|
+
type: "text",
|
|
3185
|
+
text: `🗑️ Skill ${input.id} deleted`,
|
|
3186
|
+
},
|
|
3187
|
+
],
|
|
3188
|
+
};
|
|
3189
|
+
}
|
|
3190
|
+
case "skills_run": {
|
|
3191
|
+
const input = SkillsRunInputSchema.parse(args);
|
|
3192
|
+
logTool(name, input.skill_id);
|
|
3193
|
+
const result = await apiClient.runSkill(input);
|
|
3194
|
+
const cost = typeof result?.cost_usd === "number"
|
|
3195
|
+
? result.cost_usd.toFixed(4)
|
|
3196
|
+
: "0";
|
|
3197
|
+
const summary = `**Output:**\n\n${result?.output ?? ""}\n\n---\n*Tokens: ${result?.tokens_input ?? 0}/${result?.tokens_output ?? 0} • Cost: $${cost}*`;
|
|
3198
|
+
return {
|
|
3199
|
+
content: [
|
|
3200
|
+
{
|
|
3201
|
+
type: "text",
|
|
3202
|
+
text: summary,
|
|
3203
|
+
},
|
|
3204
|
+
],
|
|
3205
|
+
};
|
|
3206
|
+
}
|
|
2971
3207
|
default:
|
|
2972
3208
|
logError(`Unknown tool: ${name}`);
|
|
2973
3209
|
return {
|