@basegrid_tech/mcp 0.6.5 → 0.8.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/dist/index.js +169 -18
- package/package.json +7 -3
package/dist/index.js
CHANGED
|
@@ -10,6 +10,10 @@ import fs4 from "fs";
|
|
|
10
10
|
// ../shared/src/types.ts
|
|
11
11
|
var CLAUDE_DEFAULT_MODEL_ID = "claude-sonnet-4-6";
|
|
12
12
|
var CLAUDE_DEFAULT_EFFORT = "medium";
|
|
13
|
+
var CLAUDE_OBSOLETE_MODEL_IDS = [
|
|
14
|
+
"claude-opus-4-7[1m]",
|
|
15
|
+
"claude-opus-4-6[1m]"
|
|
16
|
+
];
|
|
13
17
|
var CODEX_DEFAULT_MODEL_ID = "gpt-5.5";
|
|
14
18
|
var CODEX_OBSOLETE_MODEL_IDS = [
|
|
15
19
|
"gpt-5-codex",
|
|
@@ -466,7 +470,11 @@ function createSchema() {
|
|
|
466
470
|
agent_session_id TEXT,
|
|
467
471
|
docked INTEGER NOT NULL DEFAULT 0,
|
|
468
472
|
basegrid_role TEXT,
|
|
469
|
-
shell_cwd TEXT
|
|
473
|
+
shell_cwd TEXT,
|
|
474
|
+
tab_type TEXT NOT NULL DEFAULT 'terminal',
|
|
475
|
+
browser_url TEXT,
|
|
476
|
+
browser_title TEXT,
|
|
477
|
+
browser_favicon TEXT
|
|
470
478
|
);
|
|
471
479
|
|
|
472
480
|
CREATE TABLE IF NOT EXISTS schedule_log (
|
|
@@ -974,7 +982,7 @@ function migrateSchema(d) {
|
|
|
974
982
|
last_synced_at TEXT NOT NULL,
|
|
975
983
|
conflict_state TEXT NOT NULL DEFAULT 'none',
|
|
976
984
|
PRIMARY KEY (task_id),
|
|
977
|
-
UNIQUE (connection_id, external_id)
|
|
985
|
+
UNIQUE (connection_id, external_id, external_url)
|
|
978
986
|
)
|
|
979
987
|
`);
|
|
980
988
|
const taskCols = d.prepare("PRAGMA table_info(tasks)").all();
|
|
@@ -1127,6 +1135,51 @@ function migrateSchema(d) {
|
|
|
1127
1135
|
})();
|
|
1128
1136
|
logger_default.info("[database] migrated schema to version 24 (account cache + install_id)");
|
|
1129
1137
|
}
|
|
1138
|
+
if (version < 25) {
|
|
1139
|
+
d.pragma("foreign_keys = OFF");
|
|
1140
|
+
try {
|
|
1141
|
+
d.transaction(() => {
|
|
1142
|
+
d.exec(`
|
|
1143
|
+
CREATE TABLE task_source_links_new (
|
|
1144
|
+
task_id TEXT NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
|
|
1145
|
+
connection_id TEXT NOT NULL REFERENCES source_connections(id) ON DELETE CASCADE,
|
|
1146
|
+
connector_id TEXT NOT NULL,
|
|
1147
|
+
external_id TEXT NOT NULL,
|
|
1148
|
+
external_url TEXT NOT NULL,
|
|
1149
|
+
source_status_raw TEXT NOT NULL,
|
|
1150
|
+
source_updated_at TEXT NOT NULL,
|
|
1151
|
+
last_synced_at TEXT NOT NULL,
|
|
1152
|
+
conflict_state TEXT NOT NULL DEFAULT 'none',
|
|
1153
|
+
last_error TEXT,
|
|
1154
|
+
last_error_at TEXT,
|
|
1155
|
+
PRIMARY KEY (task_id),
|
|
1156
|
+
UNIQUE (connection_id, external_id, external_url)
|
|
1157
|
+
)
|
|
1158
|
+
`);
|
|
1159
|
+
d.exec(`
|
|
1160
|
+
INSERT INTO task_source_links_new
|
|
1161
|
+
(task_id, connection_id, connector_id, external_id, external_url,
|
|
1162
|
+
source_status_raw, source_updated_at, last_synced_at, conflict_state,
|
|
1163
|
+
last_error, last_error_at)
|
|
1164
|
+
SELECT
|
|
1165
|
+
task_id, connection_id, connector_id, external_id, external_url,
|
|
1166
|
+
source_status_raw, source_updated_at, last_synced_at, conflict_state,
|
|
1167
|
+
last_error, last_error_at
|
|
1168
|
+
FROM task_source_links
|
|
1169
|
+
`);
|
|
1170
|
+
d.exec("DROP TABLE task_source_links");
|
|
1171
|
+
d.exec("ALTER TABLE task_source_links_new RENAME TO task_source_links");
|
|
1172
|
+
d.prepare(
|
|
1173
|
+
"INSERT OR REPLACE INTO schema_meta (key, value) VALUES ('schema_version', '25')"
|
|
1174
|
+
).run();
|
|
1175
|
+
})();
|
|
1176
|
+
} finally {
|
|
1177
|
+
d.pragma("foreign_keys = ON");
|
|
1178
|
+
}
|
|
1179
|
+
logger_default.info(
|
|
1180
|
+
"[database] migrated schema to version 25 (task_source_links unique includes external_url)"
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1130
1183
|
}
|
|
1131
1184
|
function verifySchema(d) {
|
|
1132
1185
|
const expectedTables = [
|
|
@@ -1174,7 +1227,7 @@ function verifySchema(d) {
|
|
|
1174
1227
|
last_synced_at TEXT NOT NULL,
|
|
1175
1228
|
conflict_state TEXT NOT NULL DEFAULT 'none',
|
|
1176
1229
|
PRIMARY KEY (task_id),
|
|
1177
|
-
UNIQUE (connection_id, external_id)
|
|
1230
|
+
UNIQUE (connection_id, external_id, external_url)
|
|
1178
1231
|
)`
|
|
1179
1232
|
}
|
|
1180
1233
|
];
|
|
@@ -1225,7 +1278,14 @@ function verifySchema(d) {
|
|
|
1225
1278
|
ddl: "ALTER TABLE sessions ADD COLUMN docked INTEGER NOT NULL DEFAULT 0"
|
|
1226
1279
|
},
|
|
1227
1280
|
{ column: "basegrid_role", ddl: "ALTER TABLE sessions ADD COLUMN basegrid_role TEXT" },
|
|
1228
|
-
{ column: "shell_cwd", ddl: "ALTER TABLE sessions ADD COLUMN shell_cwd TEXT" }
|
|
1281
|
+
{ column: "shell_cwd", ddl: "ALTER TABLE sessions ADD COLUMN shell_cwd TEXT" },
|
|
1282
|
+
{
|
|
1283
|
+
column: "tab_type",
|
|
1284
|
+
ddl: "ALTER TABLE sessions ADD COLUMN tab_type TEXT NOT NULL DEFAULT 'terminal'"
|
|
1285
|
+
},
|
|
1286
|
+
{ column: "browser_url", ddl: "ALTER TABLE sessions ADD COLUMN browser_url TEXT" },
|
|
1287
|
+
{ column: "browser_title", ddl: "ALTER TABLE sessions ADD COLUMN browser_title TEXT" },
|
|
1288
|
+
{ column: "browser_favicon", ddl: "ALTER TABLE sessions ADD COLUMN browser_favicon TEXT" }
|
|
1229
1289
|
],
|
|
1230
1290
|
agent_commands: [
|
|
1231
1291
|
{
|
|
@@ -1328,13 +1388,13 @@ var DEFAULTS_KEYS_MAP = {
|
|
|
1328
1388
|
widgetEnabled: true,
|
|
1329
1389
|
minimizeToTray: true,
|
|
1330
1390
|
dashboardEnabled: true,
|
|
1391
|
+
canvasEnabled: true,
|
|
1331
1392
|
slashCommandFallbackDescription: true,
|
|
1332
1393
|
taskViewMode: true,
|
|
1333
1394
|
taskListCollapsedStatuses: true,
|
|
1334
1395
|
layoutMode: true,
|
|
1335
1396
|
mainViewMode: true,
|
|
1336
1397
|
activeWorkspace: true,
|
|
1337
|
-
updateChannel: true,
|
|
1338
1398
|
webAccessEnabled: true,
|
|
1339
1399
|
mobileAccessEnabled: true,
|
|
1340
1400
|
networkAccessEnabled: true,
|
|
@@ -1352,17 +1412,18 @@ var DEFAULTS_KEYS_MAP = {
|
|
|
1352
1412
|
defaultSpinnerVariant: true,
|
|
1353
1413
|
dragFilesFromChanges: true,
|
|
1354
1414
|
taskGithubIssueEnabled: true,
|
|
1355
|
-
morningBriefingEnabled: true,
|
|
1356
|
-
lastBriefingShownDate: true,
|
|
1357
|
-
toolHeatmapEnabled: true,
|
|
1358
1415
|
dashboardCollapsedColumns: true,
|
|
1359
1416
|
perfOverlayEnabled: true,
|
|
1360
1417
|
sidebarSnapPointsEnabled: true,
|
|
1361
1418
|
sidebarWidth: true,
|
|
1362
1419
|
navigationHistoryEnabled: true,
|
|
1363
1420
|
experimentalClaudeStreamRuntime: true,
|
|
1364
|
-
|
|
1365
|
-
sendAnonymousUsageData: true
|
|
1421
|
+
experimentalHeadlessHiddenTerminals: true,
|
|
1422
|
+
sendAnonymousUsageData: true,
|
|
1423
|
+
reviewAgent: true,
|
|
1424
|
+
reviewClaudeModel: true,
|
|
1425
|
+
reviewCodexModel: true,
|
|
1426
|
+
reviewCodexEffort: true
|
|
1366
1427
|
};
|
|
1367
1428
|
var DEFAULTS_KEYS = Object.keys(DEFAULTS_KEYS_MAP);
|
|
1368
1429
|
var INTERNAL_DEFAULTS_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -2043,7 +2104,8 @@ var ConfigManager = class {
|
|
|
2043
2104
|
* ни `/effort`. Теперь записываем стандартные значения в DB сразу, чтобы
|
|
2044
2105
|
* источник правды был один. */
|
|
2045
2106
|
seedClaudeDefaults(config) {
|
|
2046
|
-
const
|
|
2107
|
+
const stored = config.defaults.claudeDefaultModel;
|
|
2108
|
+
const needsModel = stored == null || typeof stored === "string" && CLAUDE_OBSOLETE_MODEL_IDS.includes(stored);
|
|
2047
2109
|
const needsEffort = config.defaults.claudeDefaultEffort == null;
|
|
2048
2110
|
if (!needsModel && !needsEffort) return config;
|
|
2049
2111
|
const seeded = {
|
|
@@ -2669,18 +2731,19 @@ function readPort() {
|
|
|
2669
2731
|
return discoverAndHeal();
|
|
2670
2732
|
}
|
|
2671
2733
|
}
|
|
2672
|
-
async function rpcCall(method, params) {
|
|
2734
|
+
async function rpcCall(method, params, opts = {}) {
|
|
2673
2735
|
const result = readPort();
|
|
2674
2736
|
if (!result.port) {
|
|
2675
2737
|
throw new Error(result.reason === "invalid" ? PORT_FILE_INVALID_MSG : PORT_FILE_MISSING_MSG);
|
|
2676
2738
|
}
|
|
2739
|
+
const effectiveTimeout = opts.timeoutMs ?? TIMEOUT_MS;
|
|
2677
2740
|
return new Promise((resolve, reject) => {
|
|
2678
2741
|
const ws = new WebSocket(`ws://127.0.0.1:${result.port}/ws`);
|
|
2679
2742
|
const id = ++rpcId;
|
|
2680
|
-
const timer = setTimeout(() => {
|
|
2743
|
+
const timer = effectiveTimeout > 0 ? setTimeout(() => {
|
|
2681
2744
|
ws.close();
|
|
2682
|
-
reject(new Error(`RPC call "${method}" timed out after ${
|
|
2683
|
-
},
|
|
2745
|
+
reject(new Error(`RPC call "${method}" timed out after ${effectiveTimeout}ms`));
|
|
2746
|
+
}, effectiveTimeout) : null;
|
|
2684
2747
|
ws.on("open", () => {
|
|
2685
2748
|
ws.send(JSON.stringify({ jsonrpc: "2.0", id, method, params }));
|
|
2686
2749
|
});
|
|
@@ -2688,7 +2751,7 @@ async function rpcCall(method, params) {
|
|
|
2688
2751
|
try {
|
|
2689
2752
|
const msg = JSON.parse(raw.toString());
|
|
2690
2753
|
if (msg.id !== id) return;
|
|
2691
|
-
clearTimeout(timer);
|
|
2754
|
+
if (timer) clearTimeout(timer);
|
|
2692
2755
|
ws.close();
|
|
2693
2756
|
if (msg.error) {
|
|
2694
2757
|
reject(new Error(msg.error.message));
|
|
@@ -2699,7 +2762,7 @@ async function rpcCall(method, params) {
|
|
|
2699
2762
|
}
|
|
2700
2763
|
});
|
|
2701
2764
|
ws.on("error", (err) => {
|
|
2702
|
-
clearTimeout(timer);
|
|
2765
|
+
if (timer) clearTimeout(timer);
|
|
2703
2766
|
reject(new Error(`Cannot connect to BaseGrid server: ${err.message}. Is the app running?`));
|
|
2704
2767
|
});
|
|
2705
2768
|
});
|
|
@@ -3425,6 +3488,93 @@ function registerWorkspaceTools(server) {
|
|
|
3425
3488
|
);
|
|
3426
3489
|
}
|
|
3427
3490
|
|
|
3491
|
+
// src/tools/ask-user-question.ts
|
|
3492
|
+
import { z as z7 } from "zod";
|
|
3493
|
+
var ASK_TIMEOUT_MS = 60 * 60 * 1e3;
|
|
3494
|
+
var questionSchema = z7.object({
|
|
3495
|
+
question: z7.string().min(1).max(1e3).describe("The question to ask the user."),
|
|
3496
|
+
header: z7.string().max(40).optional().describe("Very short label/chip shown above the question (max 40 chars)."),
|
|
3497
|
+
multiSelect: z7.boolean().optional().describe("Allow multiple options to be selected. Defaults to single-select."),
|
|
3498
|
+
options: z7.array(z7.string().min(1).max(200)).min(2).max(4).describe(
|
|
3499
|
+
'Available choices (2\u20134). Do NOT include "Other" yourself \u2014 the UI adds it automatically when supported.'
|
|
3500
|
+
)
|
|
3501
|
+
});
|
|
3502
|
+
function registerAskUserQuestionTool(server) {
|
|
3503
|
+
server.tool(
|
|
3504
|
+
"ask_user_question",
|
|
3505
|
+
[
|
|
3506
|
+
"Ask the user a question during execution. Use to: gather preferences,",
|
|
3507
|
+
"clarify ambiguous instructions, get a decision on an implementation",
|
|
3508
|
+
"choice, or offer the user options about the next direction.",
|
|
3509
|
+
"",
|
|
3510
|
+
"Each question shows 2\u20134 mutually exclusive options. The UI returns the",
|
|
3511
|
+
"selected label(s). If the user dismisses the card, the tool returns",
|
|
3512
|
+
'`status: "skipped"` \u2014 proceed with your best judgement.',
|
|
3513
|
+
"",
|
|
3514
|
+
'Do NOT add an "Other" option to `options` \u2014 the host UI handles free-',
|
|
3515
|
+
"text input separately when needed."
|
|
3516
|
+
].join(" "),
|
|
3517
|
+
{
|
|
3518
|
+
questions: z7.array(questionSchema).min(1).max(4).describe("Questions to ask (1\u20134). Each independent and mutually exclusive.")
|
|
3519
|
+
},
|
|
3520
|
+
async (args) => {
|
|
3521
|
+
const terminalId = process.env.BASEGRID_TERMINAL_ID;
|
|
3522
|
+
if (!terminalId) {
|
|
3523
|
+
return {
|
|
3524
|
+
content: [
|
|
3525
|
+
{
|
|
3526
|
+
type: "text",
|
|
3527
|
+
text: "Error: BASEGRID_TERMINAL_ID is not set in the environment. This tool can only be used from a BaseGrid-managed session."
|
|
3528
|
+
}
|
|
3529
|
+
],
|
|
3530
|
+
isError: true
|
|
3531
|
+
};
|
|
3532
|
+
}
|
|
3533
|
+
const normalized = args.questions.map((q) => ({
|
|
3534
|
+
question: q.question,
|
|
3535
|
+
...q.header ? { header: q.header } : {},
|
|
3536
|
+
...typeof q.multiSelect === "boolean" ? { multiSelect: q.multiSelect } : {},
|
|
3537
|
+
options: q.options.map((label) => ({ label }))
|
|
3538
|
+
}));
|
|
3539
|
+
try {
|
|
3540
|
+
const res = await rpcCall(
|
|
3541
|
+
"mcp:askUserQuestion",
|
|
3542
|
+
{ terminalId, questions: normalized },
|
|
3543
|
+
{ timeoutMs: ASK_TIMEOUT_MS }
|
|
3544
|
+
);
|
|
3545
|
+
if (res.skipped || !res.answers) {
|
|
3546
|
+
return {
|
|
3547
|
+
content: [
|
|
3548
|
+
{
|
|
3549
|
+
type: "text",
|
|
3550
|
+
text: "The user dismissed the question card without answering. Proceed with your best judgement based on prior context."
|
|
3551
|
+
}
|
|
3552
|
+
]
|
|
3553
|
+
};
|
|
3554
|
+
}
|
|
3555
|
+
const out = {};
|
|
3556
|
+
args.questions.forEach((q, i) => {
|
|
3557
|
+
const value = res.answers?.[String(i)] ?? res.answers?.[q.question];
|
|
3558
|
+
if (value !== void 0) out[q.question] = value;
|
|
3559
|
+
});
|
|
3560
|
+
return {
|
|
3561
|
+
content: [{ type: "text", text: JSON.stringify(out, null, 2) }]
|
|
3562
|
+
};
|
|
3563
|
+
} catch (err) {
|
|
3564
|
+
return {
|
|
3565
|
+
content: [
|
|
3566
|
+
{
|
|
3567
|
+
type: "text",
|
|
3568
|
+
text: `Error asking user: ${err instanceof Error ? err.message : String(err)}`
|
|
3569
|
+
}
|
|
3570
|
+
],
|
|
3571
|
+
isError: true
|
|
3572
|
+
};
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
);
|
|
3576
|
+
}
|
|
3577
|
+
|
|
3428
3578
|
// src/server.ts
|
|
3429
3579
|
function createMcpServer(version) {
|
|
3430
3580
|
const server = new McpServer({ name: "basegrid", version }, { capabilities: { tools: {} } });
|
|
@@ -3434,6 +3584,7 @@ function createMcpServer(version) {
|
|
|
3434
3584
|
registerSessionTools(server);
|
|
3435
3585
|
registerWorkflowTools(server);
|
|
3436
3586
|
registerWorkspaceTools(server);
|
|
3587
|
+
registerAskUserQuestionTool(server);
|
|
3437
3588
|
return server;
|
|
3438
3589
|
}
|
|
3439
3590
|
|
|
@@ -3446,7 +3597,7 @@ console.warn = (...args) => _origError("[mcp:warn]", ...args);
|
|
|
3446
3597
|
console.error = (...args) => _origError("[mcp:error]", ...args);
|
|
3447
3598
|
async function main() {
|
|
3448
3599
|
configManager.init();
|
|
3449
|
-
const version = true ? "0.
|
|
3600
|
+
const version = true ? "0.8.0" : createRequire(import.meta.url)("../package.json").version;
|
|
3450
3601
|
const server = createMcpServer(version);
|
|
3451
3602
|
const transport = new StdioServerTransport();
|
|
3452
3603
|
await server.connect(transport);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basegrid_tech/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "BaseGrid MCP server — task management, git, and workflow tools for AI coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"files": [
|
|
27
27
|
"dist"
|
|
28
28
|
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public",
|
|
31
|
+
"registry": "https://registry.npmjs.org/"
|
|
32
|
+
},
|
|
29
33
|
"scripts": {
|
|
30
34
|
"build": "tsup",
|
|
31
35
|
"dev": "tsx src/index.ts"
|
|
@@ -38,8 +42,8 @@
|
|
|
38
42
|
"zod": "^4.3.6"
|
|
39
43
|
},
|
|
40
44
|
"devDependencies": {
|
|
41
|
-
"@basegrid/server": "0.
|
|
42
|
-
"@basegrid/shared": "0.
|
|
45
|
+
"@basegrid/server": "0.8.0",
|
|
46
|
+
"@basegrid/shared": "0.8.0",
|
|
43
47
|
"tsup": "^8.5.1",
|
|
44
48
|
"tsx": "^4.21.0",
|
|
45
49
|
"typescript": "^6.0.3"
|