@azerate/claudette-mcp 1.7.6 → 1.7.8

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 CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  MCP server for Claudette IDE - providing Claude with comprehensive workspace management tools.
4
4
 
5
+ > **v1.7.6** - Added merge safety checks to prevent code loss during PR merges.
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
package/dist/api.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Get the Claudette API base URL.
3
+ * Reads from .server-port file on each call to handle:
4
+ * - Server starting after MCP
5
+ * - Server restarting on different port
6
+ */
7
+ export declare function getApiUrl(): string;
package/dist/api.js ADDED
@@ -0,0 +1,36 @@
1
+ // Shared API URL utility for Claudette MCP server
2
+ // Dynamically reads port from .server-port file on each call
3
+ import { readFileSync, existsSync } from "fs";
4
+ import { join, dirname } from "path";
5
+ import { fileURLToPath } from "url";
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+ const DEFAULT_PORT = 52001;
9
+ /**
10
+ * Get the Claudette API base URL.
11
+ * Reads from .server-port file on each call to handle:
12
+ * - Server starting after MCP
13
+ * - Server restarting on different port
14
+ */
15
+ export function getApiUrl() {
16
+ // Try to find .server-port file in multiple locations
17
+ const portFilePaths = [
18
+ join(__dirname, '..', '..', '.server-port'), // From dist/
19
+ join(__dirname, '..', '.server-port'), // From src/
20
+ join(process.cwd(), '.server-port'), // From cwd
21
+ ];
22
+ for (const portFile of portFilePaths) {
23
+ if (existsSync(portFile)) {
24
+ try {
25
+ const port = parseInt(readFileSync(portFile, 'utf-8').trim(), 10);
26
+ if (!isNaN(port) && port > 0 && port < 65536) {
27
+ return `http://localhost:${port}`;
28
+ }
29
+ }
30
+ catch {
31
+ // Ignore read errors, try next path
32
+ }
33
+ }
34
+ }
35
+ return `http://localhost:${DEFAULT_PORT}`;
36
+ }
package/dist/index.js CHANGED
@@ -10,12 +10,15 @@ import { getCheckpoints, createCheckpoint, restoreCheckpoint, deleteCheckpoint }
10
10
  import { getGitChanges, checkMergeSafety } from './git.js';
11
11
  import { getTypeScriptErrors } from './errors.js';
12
12
  import { getPendingAction } from './actions.js';
13
- // Claudette server API base URL
14
- const CLAUDETTE_API = "http://localhost:52001";
13
+ import { getApiUrl } from './api.js';
14
+ // Helper function - called on each request for dynamic port detection
15
+ function getApi() {
16
+ return getApiUrl();
17
+ }
15
18
  // Helper to log refactor activity
16
19
  async function logRefactorActivity(workspacePath, type, action, message, details) {
17
20
  try {
18
- await fetch(`${CLAUDETTE_API}/api/refactor/activity`, {
21
+ await fetch(`${getApi()}/api/refactor/activity`, {
19
22
  method: "POST",
20
23
  headers: { "Content-Type": "application/json" },
21
24
  body: JSON.stringify({ path: workspacePath, type, action, message, details }),
@@ -923,7 +926,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
923
926
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
924
927
  }
925
928
  try {
926
- const response = await fetch(`${CLAUDETTE_API}/api/refactor/analyze`, {
929
+ const response = await fetch(`${getApi()}/api/refactor/analyze`, {
927
930
  method: "POST",
928
931
  headers: { "Content-Type": "application/json" },
929
932
  body: JSON.stringify({ path: workspacePath }),
@@ -965,7 +968,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
965
968
  }
966
969
  try {
967
970
  await logRefactorActivity(workspacePath, 'info', 'read_plan', 'Reading refactor plan...');
968
- const response = await fetch(`${CLAUDETTE_API}/api/refactor/plan?path=${encodeURIComponent(workspacePath)}`);
971
+ const response = await fetch(`${getApi()}/api/refactor/plan?path=${encodeURIComponent(workspacePath)}`);
969
972
  if (response.status === 404) {
970
973
  return { content: [{ type: "text", text: "No refactor plan found. Use create_refactor_plan first." }] };
971
974
  }
@@ -1028,7 +1031,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1028
1031
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1029
1032
  }
1030
1033
  try {
1031
- const response = await fetch(`${CLAUDETTE_API}/api/refactor/plan?path=${encodeURIComponent(workspacePath)}`, {
1034
+ const response = await fetch(`${getApi()}/api/refactor/plan?path=${encodeURIComponent(workspacePath)}`, {
1032
1035
  method: "DELETE",
1033
1036
  });
1034
1037
  const data = await response.json();
@@ -1050,7 +1053,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1050
1053
  }
1051
1054
  try {
1052
1055
  await logRefactorActivity(workspacePath, 'info', 'verify', 'Running tests...');
1053
- const response = await fetch(`${CLAUDETTE_API}/api/refactor/verify`, {
1056
+ const response = await fetch(`${getApi()}/api/refactor/verify`, {
1054
1057
  method: "POST",
1055
1058
  headers: { "Content-Type": "application/json" },
1056
1059
  body: JSON.stringify({ path: workspacePath }),
@@ -1101,7 +1104,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1101
1104
  return { content: [{ type: "text", text: "Error: workspace_path and step_id are required" }] };
1102
1105
  }
1103
1106
  try {
1104
- const response = await fetch(`${CLAUDETTE_API}/api/refactor/step/complete`, {
1107
+ const response = await fetch(`${getApi()}/api/refactor/step/complete`, {
1105
1108
  method: "POST",
1106
1109
  headers: { "Content-Type": "application/json" },
1107
1110
  body: JSON.stringify({ path: workspacePath, stepId }),
@@ -1125,7 +1128,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1125
1128
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1126
1129
  }
1127
1130
  try {
1128
- const response = await fetch(`${CLAUDETTE_API}/api/refactor/step/next?path=${encodeURIComponent(workspacePath)}`);
1131
+ const response = await fetch(`${getApi()}/api/refactor/step/next?path=${encodeURIComponent(workspacePath)}`);
1129
1132
  const data = await response.json();
1130
1133
  if (!data.hasNext) {
1131
1134
  await logRefactorActivity(workspacePath, 'success', 'all_complete', 'All refactoring steps completed!');
@@ -1183,7 +1186,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1183
1186
  }
1184
1187
  try {
1185
1188
  // Start the tests
1186
- const response = await fetch(`${CLAUDETTE_API}/api/jest/run`, {
1189
+ const response = await fetch(`${getApi()}/api/jest/run`, {
1187
1190
  method: "POST",
1188
1191
  headers: { "Content-Type": "application/json" },
1189
1192
  body: JSON.stringify({ path: workspacePath, testFile }),
@@ -1198,7 +1201,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1198
1201
  while (attempts < maxAttempts) {
1199
1202
  await new Promise(resolve => setTimeout(resolve, 1000));
1200
1203
  attempts++;
1201
- const statusRes = await fetch(`${CLAUDETTE_API}/api/jest/status?path=${encodeURIComponent(workspacePath)}`);
1204
+ const statusRes = await fetch(`${getApi()}/api/jest/status?path=${encodeURIComponent(workspacePath)}`);
1202
1205
  const status = await statusRes.json();
1203
1206
  if (!status.isRunning && status.lastResult) {
1204
1207
  const result = status.lastResult;
@@ -1232,7 +1235,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1232
1235
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1233
1236
  }
1234
1237
  try {
1235
- const response = await fetch(`${CLAUDETTE_API}/api/jest/status?path=${encodeURIComponent(workspacePath)}`);
1238
+ const response = await fetch(`${getApi()}/api/jest/status?path=${encodeURIComponent(workspacePath)}`);
1236
1239
  const status = await response.json();
1237
1240
  if (status.isRunning) {
1238
1241
  return { content: [{ type: "text", text: "Tests are currently running..." }] };
@@ -1268,7 +1271,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1268
1271
  }
1269
1272
  try {
1270
1273
  // Start the benchmarks
1271
- const response = await fetch(`${CLAUDETTE_API}/api/benchmarks/run`, {
1274
+ const response = await fetch(`${getApi()}/api/benchmarks/run`, {
1272
1275
  method: "POST",
1273
1276
  headers: { "Content-Type": "application/json" },
1274
1277
  body: JSON.stringify({ path: workspacePath, async: false }),
@@ -1308,7 +1311,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1308
1311
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1309
1312
  }
1310
1313
  try {
1311
- const response = await fetch(`${CLAUDETTE_API}/api/benchmarks/status?path=${encodeURIComponent(workspacePath)}`);
1314
+ const response = await fetch(`${getApi()}/api/benchmarks/status?path=${encodeURIComponent(workspacePath)}`);
1312
1315
  const status = await response.json();
1313
1316
  if (status.isRunning) {
1314
1317
  return { content: [{ type: "text", text: "Benchmarks are currently running..." }] };
@@ -1353,7 +1356,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1353
1356
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1354
1357
  }
1355
1358
  try {
1356
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/status?path=${encodeURIComponent(workspacePath)}`);
1359
+ const response = await fetch(`${getApi()}/api/workflow/status?path=${encodeURIComponent(workspacePath)}`);
1357
1360
  const status = await response.json();
1358
1361
  let output = `Workflow Status\n${"=".repeat(50)}\n\n`;
1359
1362
  output += `Current Step: ${status.currentStep}\n`;
@@ -1442,7 +1445,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1442
1445
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1443
1446
  }
1444
1447
  try {
1445
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/trigger`, {
1448
+ const response = await fetch(`${getApi()}/api/workflow/trigger`, {
1446
1449
  method: "POST",
1447
1450
  headers: { "Content-Type": "application/json" },
1448
1451
  body: JSON.stringify({ path: workspacePath }),
@@ -1470,7 +1473,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1470
1473
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1471
1474
  }
1472
1475
  try {
1473
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/commit-message?path=${encodeURIComponent(workspacePath)}`);
1476
+ const response = await fetch(`${getApi()}/api/workflow/commit-message?path=${encodeURIComponent(workspacePath)}`);
1474
1477
  const data = await response.json();
1475
1478
  return { content: [{ type: "text", text: `Suggested commit message:\n\n${data.message}` }] };
1476
1479
  }
@@ -1488,7 +1491,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1488
1491
  return { content: [{ type: "text", text: "Error: message is required" }] };
1489
1492
  }
1490
1493
  try {
1491
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/commit`, {
1494
+ const response = await fetch(`${getApi()}/api/workflow/commit`, {
1492
1495
  method: "POST",
1493
1496
  headers: { "Content-Type": "application/json" },
1494
1497
  body: JSON.stringify({ path: workspacePath, message }),
@@ -1511,7 +1514,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1511
1514
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1512
1515
  }
1513
1516
  try {
1514
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/push`, {
1517
+ const response = await fetch(`${getApi()}/api/workflow/push`, {
1515
1518
  method: "POST",
1516
1519
  headers: { "Content-Type": "application/json" },
1517
1520
  body: JSON.stringify({ path: workspacePath }),
@@ -1538,7 +1541,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1538
1541
  return { content: [{ type: "text", text: "Error: tests_written is required" }] };
1539
1542
  }
1540
1543
  try {
1541
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/write-tests`, {
1544
+ const response = await fetch(`${getApi()}/api/workflow/write-tests`, {
1542
1545
  method: "POST",
1543
1546
  headers: { "Content-Type": "application/json" },
1544
1547
  body: JSON.stringify({ path: workspacePath, testsWritten }),
@@ -1591,6 +1594,16 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1591
1594
  let merged = false;
1592
1595
  if (prNumber) {
1593
1596
  const mergeFlag = mergeMethod === 'squash' ? '--squash' : mergeMethod === 'rebase' ? '--rebase' : '--merge';
1597
+ // Stash any uncommitted changes before merge (gh pr merge --delete-branch needs clean checkout)
1598
+ let stashed = false;
1599
+ try {
1600
+ const status = execSync('git status --porcelain', { cwd: workspacePath, encoding: 'utf-8' }).trim();
1601
+ if (status) {
1602
+ execSync('git stash push -m "claudette-auto-stash"', { cwd: workspacePath, stdio: 'pipe' });
1603
+ stashed = true;
1604
+ }
1605
+ }
1606
+ catch { /* ignore stash errors */ }
1594
1607
  try {
1595
1608
  execSync(`gh pr merge ${prNumber} ${mergeFlag} --delete-branch`, {
1596
1609
  cwd: workspacePath,
@@ -1600,18 +1613,32 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1600
1613
  merged = true;
1601
1614
  }
1602
1615
  catch (mergeErr) {
1616
+ // Restore stash if merge failed
1617
+ if (stashed) {
1618
+ try {
1619
+ execSync('git stash pop', { cwd: workspacePath, stdio: 'pipe' });
1620
+ }
1621
+ catch { /* ignore */ }
1622
+ }
1603
1623
  return { content: [{ type: "text", text: `❌ Failed to merge PR #${prNumber}: ${mergeErr.message}` }] };
1604
1624
  }
1625
+ // After successful merge, drop the stash (we're on main now, stash content is outdated)
1626
+ if (stashed) {
1627
+ try {
1628
+ execSync('git stash drop', { cwd: workspacePath, stdio: 'pipe' });
1629
+ }
1630
+ catch { /* ignore */ }
1631
+ }
1605
1632
  }
1606
1633
  // Mark workflow as complete
1607
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/code-review`, {
1634
+ const response = await fetch(`${getApi()}/api/workflow/code-review`, {
1608
1635
  method: "POST",
1609
1636
  headers: { "Content-Type": "application/json" },
1610
1637
  body: JSON.stringify({ path: workspacePath, reviewer }),
1611
1638
  });
1612
1639
  await response.json();
1613
1640
  // Reset workflow after successful merge
1614
- await fetch(`${CLAUDETTE_API}/api/workflow/reset`, {
1641
+ await fetch(`${getApi()}/api/workflow/reset`, {
1615
1642
  method: "POST",
1616
1643
  headers: { "Content-Type": "application/json" },
1617
1644
  body: JSON.stringify({ path: workspacePath }),
@@ -1646,7 +1673,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1646
1673
  return { content: [{ type: "text", text: "Error: title is required" }] };
1647
1674
  }
1648
1675
  try {
1649
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/pr`, {
1676
+ const response = await fetch(`${getApi()}/api/workflow/pr`, {
1650
1677
  method: "POST",
1651
1678
  headers: { "Content-Type": "application/json" },
1652
1679
  body: JSON.stringify({ path: workspacePath, title, body }),
@@ -1670,7 +1697,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1670
1697
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1671
1698
  }
1672
1699
  try {
1673
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/branch?path=${encodeURIComponent(workspacePath)}&fetch=${doFetch}`);
1700
+ const response = await fetch(`${getApi()}/api/workflow/branch?path=${encodeURIComponent(workspacePath)}&fetch=${doFetch}`);
1674
1701
  const info = await response.json();
1675
1702
  let output = `Branch Info\n${'='.repeat(50)}\n\n`;
1676
1703
  output += `Current Branch: ${info.current}\n`;
@@ -1721,7 +1748,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1721
1748
  return { content: [{ type: "text", text: "Error: branch_name is required" }] };
1722
1749
  }
1723
1750
  try {
1724
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/branch`, {
1751
+ const response = await fetch(`${getApi()}/api/workflow/branch`, {
1725
1752
  method: "POST",
1726
1753
  headers: { "Content-Type": "application/json" },
1727
1754
  body: JSON.stringify({ path: workspacePath, name: branchName }),
@@ -1745,7 +1772,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1745
1772
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1746
1773
  }
1747
1774
  try {
1748
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/sync`, {
1775
+ const response = await fetch(`${getApi()}/api/workflow/sync`, {
1749
1776
  method: "POST",
1750
1777
  headers: { "Content-Type": "application/json" },
1751
1778
  body: JSON.stringify({ path: workspacePath, method }),
@@ -1768,7 +1795,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1768
1795
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1769
1796
  }
1770
1797
  try {
1771
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/pr-status?path=${encodeURIComponent(workspacePath)}`);
1798
+ const response = await fetch(`${getApi()}/api/workflow/pr-status?path=${encodeURIComponent(workspacePath)}`);
1772
1799
  const status = await response.json();
1773
1800
  let output = `Branch PR Status\n${'='.repeat(50)}\n\n`;
1774
1801
  if (!status.hasPR) {
@@ -1809,7 +1836,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1809
1836
  return { content: [{ type: "text", text: "Error: workspace_path is required" }] };
1810
1837
  }
1811
1838
  try {
1812
- const response = await fetch(`${CLAUDETTE_API}/api/workflow/recommendation?path=${encodeURIComponent(workspacePath)}`);
1839
+ const response = await fetch(`${getApi()}/api/workflow/recommendation?path=${encodeURIComponent(workspacePath)}`);
1813
1840
  const rec = await response.json();
1814
1841
  let output = `Workflow Recommendation\n${'='.repeat(50)}\n\n`;
1815
1842
  // Action header with icon
package/dist/scripts.js CHANGED
@@ -1,11 +1,10 @@
1
1
  // Script management functions for Claudette MCP server
2
2
  import { stripAnsi } from './utils.js';
3
- // Claudette server API base URL
4
- const CLAUDETTE_API = "http://localhost:52001";
3
+ import { getApiUrl } from './api.js';
5
4
  // Get list of scripts from workspace
6
5
  export async function getScripts(workspacePath) {
7
6
  try {
8
- const response = await fetch(`${CLAUDETTE_API}/api/scripts?path=${encodeURIComponent(workspacePath)}`);
7
+ const response = await fetch(`${getApiUrl()}/api/scripts?path=${encodeURIComponent(workspacePath)}`);
9
8
  if (!response.ok)
10
9
  return { scripts: [], hasPackageJson: false };
11
10
  return await response.json();
@@ -17,7 +16,7 @@ export async function getScripts(workspacePath) {
17
16
  // Get script output
18
17
  export async function getScriptOutput(workspacePath, scriptName) {
19
18
  try {
20
- const response = await fetch(`${CLAUDETTE_API}/api/scripts/status?path=${encodeURIComponent(workspacePath)}&scriptName=${encodeURIComponent(scriptName)}`);
19
+ const response = await fetch(`${getApiUrl()}/api/scripts/status?path=${encodeURIComponent(workspacePath)}&scriptName=${encodeURIComponent(scriptName)}`);
21
20
  if (!response.ok)
22
21
  return null;
23
22
  const data = await response.json();
@@ -34,7 +33,7 @@ export async function getScriptOutput(workspacePath, scriptName) {
34
33
  // Start a script
35
34
  export async function startScript(workspacePath, scriptName) {
36
35
  try {
37
- const response = await fetch(`${CLAUDETTE_API}/api/scripts/run`, {
36
+ const response = await fetch(`${getApiUrl()}/api/scripts/run`, {
38
37
  method: 'POST',
39
38
  headers: { 'Content-Type': 'application/json' },
40
39
  body: JSON.stringify({ path: workspacePath, scriptName, autoLaunchBrowser: true }),
@@ -48,7 +47,7 @@ export async function startScript(workspacePath, scriptName) {
48
47
  // Stop a script
49
48
  export async function stopScript(workspacePath, scriptName) {
50
49
  try {
51
- const response = await fetch(`${CLAUDETTE_API}/api/scripts/stop`, {
50
+ const response = await fetch(`${getApiUrl()}/api/scripts/stop`, {
52
51
  method: 'POST',
53
52
  headers: { 'Content-Type': 'application/json' },
54
53
  body: JSON.stringify({ path: workspacePath, scriptName }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azerate/claudette-mcp",
3
- "version": "1.7.6",
3
+ "version": "1.7.8",
4
4
  "description": "MCP server for Claudette IDE - TypeScript errors, git changes, checkpoints, memory, and script management",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",