@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.
Files changed (2) hide show
  1. package/dist/index.js +169 -18
  2. 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
- costForecastEnabled: true,
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 needsModel = config.defaults.claudeDefaultModel == null;
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 ${TIMEOUT_MS}ms`));
2683
- }, TIMEOUT_MS);
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.6.5" : createRequire(import.meta.url)("../package.json").version;
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.6.5",
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.6.5",
42
- "@basegrid/shared": "0.6.5",
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"