@coderule/mcp 1.4.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/cli.cjs +38 -6
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +38 -6
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +33 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +33 -5
- package/dist/index.js.map +1 -1
- package/dist/mcp-cli.cjs +154 -31
- package/dist/mcp-cli.cjs.map +1 -1
- package/dist/mcp-cli.js +154 -31
- package/dist/mcp-cli.js.map +1 -1
- package/package.json +1 -1
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 =
|
|
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 ??
|
|
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
|
-
{
|
|
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
|
-
{
|
|
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);
|
|
@@ -1690,6 +1718,63 @@ function collectIndexingStatus(runtime, runner) {
|
|
|
1690
1718
|
service: runner.getServiceStateSnapshot()
|
|
1691
1719
|
};
|
|
1692
1720
|
}
|
|
1721
|
+
function formatStatus(status) {
|
|
1722
|
+
const lines = [];
|
|
1723
|
+
lines.push("=== Coderule Indexing Status ===");
|
|
1724
|
+
lines.push("");
|
|
1725
|
+
lines.push(`Timestamp: ${new Date(status.timestamp).toISOString()}`);
|
|
1726
|
+
lines.push("");
|
|
1727
|
+
lines.push("Repository:");
|
|
1728
|
+
lines.push(` ID: ${status.root.id}`);
|
|
1729
|
+
lines.push(` Path: ${status.root.path}`);
|
|
1730
|
+
lines.push("");
|
|
1731
|
+
lines.push("Files:");
|
|
1732
|
+
lines.push(` Total: ${status.files.total}`);
|
|
1733
|
+
lines.push(" States:");
|
|
1734
|
+
lines.push(` Clean: ${status.files.byState.clean}`);
|
|
1735
|
+
lines.push(` Dirty: ${status.files.byState.dirty}`);
|
|
1736
|
+
lines.push(` Hashing: ${status.files.byState.hashing}`);
|
|
1737
|
+
lines.push(` Missing: ${status.files.byState.missing}`);
|
|
1738
|
+
lines.push("");
|
|
1739
|
+
lines.push("Queue:");
|
|
1740
|
+
lines.push(` Pending: ${status.queue.pending}`);
|
|
1741
|
+
lines.push(` Processing: ${status.queue.processing}`);
|
|
1742
|
+
lines.push(` Done: ${status.queue.done}`);
|
|
1743
|
+
lines.push(` Failed: ${status.queue.failed}`);
|
|
1744
|
+
lines.push("");
|
|
1745
|
+
if (status.latestSnapshot) {
|
|
1746
|
+
lines.push("Latest Snapshot:");
|
|
1747
|
+
lines.push(` Hash: ${status.latestSnapshot.snapshot_hash}`);
|
|
1748
|
+
lines.push(` Files: ${status.latestSnapshot.files_count}`);
|
|
1749
|
+
lines.push(` Size: ${formatBytes(status.latestSnapshot.total_size)}`);
|
|
1750
|
+
lines.push(
|
|
1751
|
+
` Created: ${new Date(status.latestSnapshot.created_at).toISOString()}`
|
|
1752
|
+
);
|
|
1753
|
+
} else {
|
|
1754
|
+
lines.push("Latest Snapshot: None");
|
|
1755
|
+
}
|
|
1756
|
+
lines.push("");
|
|
1757
|
+
lines.push("Service State:");
|
|
1758
|
+
lines.push(
|
|
1759
|
+
` Last Change: ${new Date(status.service.lastChangeAt).toISOString()}`
|
|
1760
|
+
);
|
|
1761
|
+
lines.push(
|
|
1762
|
+
` Last Snapshot Ready: ${new Date(status.service.lastSnapshotReadyAt).toISOString()}`
|
|
1763
|
+
);
|
|
1764
|
+
lines.push(
|
|
1765
|
+
` Last Heartbeat: ${status.service.lastHeartbeatEnqueuedAt > 0 ? new Date(status.service.lastHeartbeatEnqueuedAt).toISOString() : "Never"}`
|
|
1766
|
+
);
|
|
1767
|
+
lines.push(` Watcher Ready: ${status.service.watcherReady ? "Yes" : "No"}`);
|
|
1768
|
+
lines.push(` Buffering: ${status.service.buffering ? "Yes" : "No"}`);
|
|
1769
|
+
return lines.join("\n");
|
|
1770
|
+
}
|
|
1771
|
+
function formatBytes(bytes) {
|
|
1772
|
+
if (bytes === 0) return "0 B";
|
|
1773
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
1774
|
+
const k = 1024;
|
|
1775
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
1776
|
+
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${units[i]}`;
|
|
1777
|
+
}
|
|
1693
1778
|
|
|
1694
1779
|
// src/mcp/server.ts
|
|
1695
1780
|
var SERVER_NAME = "coderule-scanner-mcp";
|
|
@@ -1698,6 +1783,37 @@ function createMcpServer({
|
|
|
1698
1783
|
runtime,
|
|
1699
1784
|
runner
|
|
1700
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
|
+
}
|
|
1701
1817
|
const server = new McpServer({
|
|
1702
1818
|
name: SERVER_NAME,
|
|
1703
1819
|
version: SERVER_VERSION,
|
|
@@ -1712,7 +1828,7 @@ function createMcpServer({
|
|
|
1712
1828
|
},
|
|
1713
1829
|
async () => {
|
|
1714
1830
|
const status = collectIndexingStatus(runtime, runner);
|
|
1715
|
-
const text =
|
|
1831
|
+
const text = formatStatus(status);
|
|
1716
1832
|
return {
|
|
1717
1833
|
content: [{ type: "text", text }]
|
|
1718
1834
|
};
|
|
@@ -1733,38 +1849,37 @@ function createMcpServer({
|
|
|
1733
1849
|
query,
|
|
1734
1850
|
budgetTokens
|
|
1735
1851
|
}) => {
|
|
1736
|
-
const
|
|
1852
|
+
const deadline = Date.now() + runtime.config.maxQueryWaitMs;
|
|
1853
|
+
const latest = await waitForLocalSnapshot(deadline);
|
|
1737
1854
|
if (!latest) {
|
|
1738
|
-
const
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
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 }] };
|
|
1743
1869
|
}
|
|
1744
1870
|
const effectiveBudget = Math.max(100, budgetTokens ?? 3e3);
|
|
1745
1871
|
try {
|
|
1746
1872
|
const result = await runtime.clients.retrieval.query(
|
|
1747
|
-
|
|
1873
|
+
readyHash,
|
|
1748
1874
|
query,
|
|
1749
1875
|
effectiveBudget,
|
|
1750
|
-
{
|
|
1751
|
-
formatter: runtime.config.retrievalFormatter
|
|
1752
|
-
}
|
|
1876
|
+
{ formatter: runtime.config.retrievalFormatter }
|
|
1753
1877
|
);
|
|
1754
|
-
const summary = {
|
|
1755
|
-
snapshotHash: latest.snapshot_hash,
|
|
1756
|
-
budgetTokens: effectiveBudget,
|
|
1757
|
-
formatter: runtime.config.retrievalFormatter
|
|
1758
|
-
};
|
|
1759
1878
|
return {
|
|
1760
1879
|
content: [
|
|
1761
1880
|
{
|
|
1762
1881
|
type: "text",
|
|
1763
1882
|
text: result.formatted_output ?? "(no formatted output)"
|
|
1764
|
-
},
|
|
1765
|
-
{
|
|
1766
|
-
type: "text",
|
|
1767
|
-
text: JSON.stringify({ summary, result }, null, 2)
|
|
1768
1883
|
}
|
|
1769
1884
|
]
|
|
1770
1885
|
};
|
|
@@ -1797,7 +1912,9 @@ var ENV_FLAG_MAP = {
|
|
|
1797
1912
|
"queue-poll": "CODERULE_QUEUE_POLL_INTERVAL_MS",
|
|
1798
1913
|
"hash-batch": "CODERULE_HASH_BATCH_SIZE",
|
|
1799
1914
|
"hash-lease": "CODERULE_HASH_LEASE_MS",
|
|
1800
|
-
"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"
|
|
1801
1918
|
};
|
|
1802
1919
|
function printUsage() {
|
|
1803
1920
|
console.log(`Usage: coderule-mcp-server [token] [options]
|
|
@@ -1839,6 +1956,12 @@ function printUsage() {
|
|
|
1839
1956
|
console.log(
|
|
1840
1957
|
" --max-snapshot-attempts <n> Override CODERULE_MAX_SNAPSHOT_ATTEMPTS"
|
|
1841
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
|
+
);
|
|
1842
1965
|
console.log(
|
|
1843
1966
|
" KEY=value Set arbitrary environment variable"
|
|
1844
1967
|
);
|
|
@@ -1960,6 +2083,10 @@ async function main() {
|
|
|
1960
2083
|
const runner = new ServiceRunner(runtime);
|
|
1961
2084
|
try {
|
|
1962
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");
|
|
1963
2090
|
let initialCreatedAt;
|
|
1964
2091
|
try {
|
|
1965
2092
|
const initial = await runInitialSyncPipeline(runtime);
|
|
@@ -1968,7 +2095,7 @@ async function main() {
|
|
|
1968
2095
|
snapshotHash: initial.snapshotHash,
|
|
1969
2096
|
filesCount: initial.filesCount
|
|
1970
2097
|
},
|
|
1971
|
-
"Initial sync completed;
|
|
2098
|
+
"Initial sync completed; entering continuous mode"
|
|
1972
2099
|
);
|
|
1973
2100
|
initialCreatedAt = initial.createdAt;
|
|
1974
2101
|
} catch (error) {
|
|
@@ -1983,10 +2110,6 @@ async function main() {
|
|
|
1983
2110
|
}
|
|
1984
2111
|
await runner.startLoops();
|
|
1985
2112
|
await runner.enableWatcherProcessing();
|
|
1986
|
-
const server = createMcpServer({ runtime, runner });
|
|
1987
|
-
const transport = new StdioServerTransport();
|
|
1988
|
-
await server.connect(transport);
|
|
1989
|
-
runtime.logger.info("MCP server connected via stdio");
|
|
1990
2113
|
const signal = await awaitShutdownSignals();
|
|
1991
2114
|
runtime.logger.info({ signal }, "Shutdown signal received");
|
|
1992
2115
|
if (typeof transport.close === "function") {
|