@coderule/mcp 1.5.0 → 1.6.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/mcp-cli.js CHANGED
@@ -33,6 +33,8 @@ var DEFAULT_QUEUE_POLL_INTERVAL_MS = 500;
33
33
  var DEFAULT_HASH_BATCH_SIZE = 32;
34
34
  var DEFAULT_MAX_SNAPSHOT_ATTEMPTS = 5;
35
35
  var DEFAULT_HTTP_TIMEOUT_MS = 12e4;
36
+ var DEFAULT_UPLOAD_CHUNK_SIZE = 1;
37
+ var DEFAULT_MAX_QUERY_WAIT_MS = 5e4;
36
38
 
37
39
  // src/config/Configurator.ts
38
40
  var DEFAULT_RETRIEVAL_FORMATTER = "standard";
@@ -60,6 +62,14 @@ function parseInteger(value, fallback) {
60
62
  }
61
63
  return parsed;
62
64
  }
65
+ function parseSecondsToMs(value, fallbackMs) {
66
+ if (!value) return fallbackMs;
67
+ const seconds = Number.parseInt(value, 10);
68
+ if (Number.isNaN(seconds) || seconds <= 0) {
69
+ throw new Error(`Invalid seconds value: ${value}`);
70
+ }
71
+ return seconds * 1e3;
72
+ }
63
73
  function parseFormatter(value) {
64
74
  if (!value) return DEFAULT_RETRIEVAL_FORMATTER;
65
75
  const normalized = value.toLowerCase();
@@ -106,7 +116,9 @@ async function resolveConfig({
106
116
  maxSnapshotAttempts: DEFAULTS.maxSnapshotAttempts,
107
117
  retrievalFormatter: parseFormatter(
108
118
  process.env.CODERULE_RETRIEVAL_FORMATTER
109
- )
119
+ ),
120
+ uploadChunkSize: DEFAULT_UPLOAD_CHUNK_SIZE,
121
+ maxQueryWaitMs: DEFAULT_MAX_QUERY_WAIT_MS
110
122
  };
111
123
  if (process.env.CODERULE_SNAPSHOT_DEBOUNCE_MS) {
112
124
  baseConfig.snapshotDebounceMs = parseInteger(
@@ -148,6 +160,16 @@ async function resolveConfig({
148
160
  process.env.CODERULE_HTTP_TIMEOUT,
149
161
  DEFAULT_HTTP_TIMEOUT_MS
150
162
  );
163
+ if (process.env.CODERULE_UPLOAD_CHUNK_SIZE) {
164
+ baseConfig.uploadChunkSize = parseInteger(
165
+ process.env.CODERULE_UPLOAD_CHUNK_SIZE,
166
+ baseConfig.uploadChunkSize
167
+ );
168
+ }
169
+ baseConfig.maxQueryWaitMs = parseSecondsToMs(
170
+ process.env.CODERULE_MAX_WAIT_TIME,
171
+ baseConfig.maxQueryWaitMs
172
+ );
151
173
  logger.debug(
152
174
  {
153
175
  rootPath,
@@ -1081,7 +1103,7 @@ async function withRetries(op, logger2, context, maxAttempts) {
1081
1103
  }
1082
1104
  }
1083
1105
  }
1084
- async function uploadMissing(rootPath, missing, syncClient, logger2, maxAttempts, chunkSize = 64) {
1106
+ async function uploadMissing(rootPath, missing, syncClient, logger2, maxAttempts, chunkSize = 1) {
1085
1107
  if (!missing || missing.length === 0) return;
1086
1108
  const total = missing.length;
1087
1109
  const chunks = [];
@@ -1123,7 +1145,7 @@ async function uploadMissing(rootPath, missing, syncClient, logger2, maxAttempts
1123
1145
  async function ensureSnapshotCreated(rootPath, computation, syncClient, logger2, options) {
1124
1146
  const { snapshotHash, files } = computation;
1125
1147
  const maxAttempts = options?.maxAttempts ?? 5;
1126
- const uploadChunkSize = options?.uploadChunkSize ?? 64;
1148
+ const uploadChunkSize = options?.uploadChunkSize ?? 1;
1127
1149
  let status = await withRetries(
1128
1150
  () => syncClient.checkSnapshotStatus(snapshotHash),
1129
1151
  logger2,
@@ -1228,7 +1250,10 @@ async function runInitialSyncPipeline(runtime) {
1228
1250
  runtime.snapshotsRepo,
1229
1251
  runtime.clients.sync,
1230
1252
  syncLogger,
1231
- { maxAttempts: runtime.config.maxSnapshotAttempts }
1253
+ {
1254
+ maxAttempts: runtime.config.maxSnapshotAttempts,
1255
+ uploadChunkSize: runtime.config.uploadChunkSize
1256
+ }
1232
1257
  );
1233
1258
  return result;
1234
1259
  }
@@ -1632,7 +1657,10 @@ var ServiceRunner = class {
1632
1657
  this.runtime.snapshotsRepo,
1633
1658
  this.runtime.clients.sync,
1634
1659
  log,
1635
- { maxAttempts: this.runtime.config.maxSnapshotAttempts }
1660
+ {
1661
+ maxAttempts: this.runtime.config.maxSnapshotAttempts,
1662
+ uploadChunkSize: this.runtime.config.uploadChunkSize
1663
+ }
1636
1664
  );
1637
1665
  this.runtime.outbox.ack(job.id, this.fsControlLeaseOwner);
1638
1666
  this.state.updateSnapshotReady(result.createdAt);
@@ -1755,6 +1783,37 @@ function createMcpServer({
1755
1783
  runtime,
1756
1784
  runner
1757
1785
  }) {
1786
+ async function sleep3(ms) {
1787
+ return new Promise((resolve) => setTimeout(resolve, ms));
1788
+ }
1789
+ async function waitForLocalSnapshot(deadlineMs) {
1790
+ let latest = runtime.snapshotsRepo.getLatest();
1791
+ while (!latest && Date.now() < deadlineMs) {
1792
+ await sleep3(250);
1793
+ latest = runtime.snapshotsRepo.getLatest();
1794
+ }
1795
+ return latest;
1796
+ }
1797
+ async function waitForServerReady(initialHash, deadlineMs) {
1798
+ let currentHash = initialHash;
1799
+ while (Date.now() < deadlineMs) {
1800
+ try {
1801
+ const status = await runtime.clients.sync.checkSnapshotStatus(currentHash);
1802
+ if (status.status === "READY") {
1803
+ return currentHash;
1804
+ }
1805
+ if (status.status === "FAILED") {
1806
+ }
1807
+ } catch {
1808
+ }
1809
+ await sleep3(500);
1810
+ const latest = runtime.snapshotsRepo.getLatest();
1811
+ if (latest && latest.snapshot_hash !== currentHash) {
1812
+ currentHash = latest.snapshot_hash;
1813
+ }
1814
+ }
1815
+ return void 0;
1816
+ }
1758
1817
  const server = new McpServer({
1759
1818
  name: SERVER_NAME,
1760
1819
  version: SERVER_VERSION,
@@ -1790,23 +1849,31 @@ function createMcpServer({
1790
1849
  query,
1791
1850
  budgetTokens
1792
1851
  }) => {
1793
- const latest = runtime.snapshotsRepo.getLatest();
1852
+ const deadline = Date.now() + runtime.config.maxQueryWaitMs;
1853
+ const latest = await waitForLocalSnapshot(deadline);
1794
1854
  if (!latest) {
1795
- const message = "No snapshots available yet. Run indexing first.";
1796
- return {
1797
- content: [{ type: "text", text: message }],
1798
- isError: true
1799
- };
1855
+ const statusText = formatStatus(collectIndexingStatus(runtime, runner));
1856
+ const text = `We are not ready....
1857
+ ${statusText}`;
1858
+ return { content: [{ type: "text", text }] };
1859
+ }
1860
+ const readyHash = await waitForServerReady(
1861
+ latest.snapshot_hash,
1862
+ deadline
1863
+ );
1864
+ if (!readyHash) {
1865
+ const statusText = formatStatus(collectIndexingStatus(runtime, runner));
1866
+ const text = `We are not ready....
1867
+ ${statusText}`;
1868
+ return { content: [{ type: "text", text }] };
1800
1869
  }
1801
1870
  const effectiveBudget = Math.max(100, budgetTokens ?? 3e3);
1802
1871
  try {
1803
1872
  const result = await runtime.clients.retrieval.query(
1804
- latest.snapshot_hash,
1873
+ readyHash,
1805
1874
  query,
1806
1875
  effectiveBudget,
1807
- {
1808
- formatter: runtime.config.retrievalFormatter
1809
- }
1876
+ { formatter: runtime.config.retrievalFormatter }
1810
1877
  );
1811
1878
  return {
1812
1879
  content: [
@@ -1845,7 +1912,9 @@ var ENV_FLAG_MAP = {
1845
1912
  "queue-poll": "CODERULE_QUEUE_POLL_INTERVAL_MS",
1846
1913
  "hash-batch": "CODERULE_HASH_BATCH_SIZE",
1847
1914
  "hash-lease": "CODERULE_HASH_LEASE_MS",
1848
- "max-snapshot-attempts": "CODERULE_MAX_SNAPSHOT_ATTEMPTS"
1915
+ "max-snapshot-attempts": "CODERULE_MAX_SNAPSHOT_ATTEMPTS",
1916
+ "upload-chunk-size": "CODERULE_UPLOAD_CHUNK_SIZE",
1917
+ "max-wait-time": "CODERULE_MAX_WAIT_TIME"
1849
1918
  };
1850
1919
  function printUsage() {
1851
1920
  console.log(`Usage: coderule-mcp-server [token] [options]
@@ -1887,6 +1956,12 @@ function printUsage() {
1887
1956
  console.log(
1888
1957
  " --max-snapshot-attempts <n> Override CODERULE_MAX_SNAPSHOT_ATTEMPTS"
1889
1958
  );
1959
+ console.log(
1960
+ " --upload-chunk-size <n> Override CODERULE_UPLOAD_CHUNK_SIZE (default 1)"
1961
+ );
1962
+ console.log(
1963
+ " --max-wait-time <sec> Override CODERULE_MAX_WAIT_TIME (default 50s)"
1964
+ );
1890
1965
  console.log(
1891
1966
  " KEY=value Set arbitrary environment variable"
1892
1967
  );
@@ -2008,6 +2083,10 @@ async function main() {
2008
2083
  const runner = new ServiceRunner(runtime);
2009
2084
  try {
2010
2085
  await runner.prepareWatcher(true);
2086
+ const server = createMcpServer({ runtime, runner });
2087
+ const transport = new StdioServerTransport();
2088
+ await server.connect(transport);
2089
+ runtime.logger.info("MCP server connected via stdio");
2011
2090
  let initialCreatedAt;
2012
2091
  try {
2013
2092
  const initial = await runInitialSyncPipeline(runtime);
@@ -2016,7 +2095,7 @@ async function main() {
2016
2095
  snapshotHash: initial.snapshotHash,
2017
2096
  filesCount: initial.filesCount
2018
2097
  },
2019
- "Initial sync completed; starting MCP server"
2098
+ "Initial sync completed; entering continuous mode"
2020
2099
  );
2021
2100
  initialCreatedAt = initial.createdAt;
2022
2101
  } catch (error) {
@@ -2031,10 +2110,6 @@ async function main() {
2031
2110
  }
2032
2111
  await runner.startLoops();
2033
2112
  await runner.enableWatcherProcessing();
2034
- const server = createMcpServer({ runtime, runner });
2035
- const transport = new StdioServerTransport();
2036
- await server.connect(transport);
2037
- runtime.logger.info("MCP server connected via stdio");
2038
2113
  const signal = await awaitShutdownSignals();
2039
2114
  runtime.logger.info({ signal }, "Shutdown signal received");
2040
2115
  if (typeof transport.close === "function") {