@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.cjs
CHANGED
|
@@ -48,6 +48,8 @@ var DEFAULT_QUEUE_POLL_INTERVAL_MS = 500;
|
|
|
48
48
|
var DEFAULT_HASH_BATCH_SIZE = 32;
|
|
49
49
|
var DEFAULT_MAX_SNAPSHOT_ATTEMPTS = 5;
|
|
50
50
|
var DEFAULT_HTTP_TIMEOUT_MS = 12e4;
|
|
51
|
+
var DEFAULT_UPLOAD_CHUNK_SIZE = 1;
|
|
52
|
+
var DEFAULT_MAX_QUERY_WAIT_MS = 5e4;
|
|
51
53
|
|
|
52
54
|
// src/config/Configurator.ts
|
|
53
55
|
var DEFAULT_RETRIEVAL_FORMATTER = "standard";
|
|
@@ -75,6 +77,14 @@ function parseInteger(value, fallback) {
|
|
|
75
77
|
}
|
|
76
78
|
return parsed;
|
|
77
79
|
}
|
|
80
|
+
function parseSecondsToMs(value, fallbackMs) {
|
|
81
|
+
if (!value) return fallbackMs;
|
|
82
|
+
const seconds = Number.parseInt(value, 10);
|
|
83
|
+
if (Number.isNaN(seconds) || seconds <= 0) {
|
|
84
|
+
throw new Error(`Invalid seconds value: ${value}`);
|
|
85
|
+
}
|
|
86
|
+
return seconds * 1e3;
|
|
87
|
+
}
|
|
78
88
|
function parseFormatter(value) {
|
|
79
89
|
if (!value) return DEFAULT_RETRIEVAL_FORMATTER;
|
|
80
90
|
const normalized = value.toLowerCase();
|
|
@@ -121,7 +131,9 @@ async function resolveConfig({
|
|
|
121
131
|
maxSnapshotAttempts: DEFAULTS.maxSnapshotAttempts,
|
|
122
132
|
retrievalFormatter: parseFormatter(
|
|
123
133
|
process.env.CODERULE_RETRIEVAL_FORMATTER
|
|
124
|
-
)
|
|
134
|
+
),
|
|
135
|
+
uploadChunkSize: DEFAULT_UPLOAD_CHUNK_SIZE,
|
|
136
|
+
maxQueryWaitMs: DEFAULT_MAX_QUERY_WAIT_MS
|
|
125
137
|
};
|
|
126
138
|
if (process.env.CODERULE_SNAPSHOT_DEBOUNCE_MS) {
|
|
127
139
|
baseConfig.snapshotDebounceMs = parseInteger(
|
|
@@ -163,6 +175,16 @@ async function resolveConfig({
|
|
|
163
175
|
process.env.CODERULE_HTTP_TIMEOUT,
|
|
164
176
|
DEFAULT_HTTP_TIMEOUT_MS
|
|
165
177
|
);
|
|
178
|
+
if (process.env.CODERULE_UPLOAD_CHUNK_SIZE) {
|
|
179
|
+
baseConfig.uploadChunkSize = parseInteger(
|
|
180
|
+
process.env.CODERULE_UPLOAD_CHUNK_SIZE,
|
|
181
|
+
baseConfig.uploadChunkSize
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
baseConfig.maxQueryWaitMs = parseSecondsToMs(
|
|
185
|
+
process.env.CODERULE_MAX_WAIT_TIME,
|
|
186
|
+
baseConfig.maxQueryWaitMs
|
|
187
|
+
);
|
|
166
188
|
logger.debug(
|
|
167
189
|
{
|
|
168
190
|
rootPath,
|
|
@@ -1096,7 +1118,7 @@ async function withRetries(op, logger2, context, maxAttempts) {
|
|
|
1096
1118
|
}
|
|
1097
1119
|
}
|
|
1098
1120
|
}
|
|
1099
|
-
async function uploadMissing(rootPath, missing, syncClient, logger2, maxAttempts, chunkSize =
|
|
1121
|
+
async function uploadMissing(rootPath, missing, syncClient, logger2, maxAttempts, chunkSize = 1) {
|
|
1100
1122
|
if (!missing || missing.length === 0) return;
|
|
1101
1123
|
const total = missing.length;
|
|
1102
1124
|
const chunks = [];
|
|
@@ -1138,7 +1160,7 @@ async function uploadMissing(rootPath, missing, syncClient, logger2, maxAttempts
|
|
|
1138
1160
|
async function ensureSnapshotCreated(rootPath, computation, syncClient, logger2, options) {
|
|
1139
1161
|
const { snapshotHash, files } = computation;
|
|
1140
1162
|
const maxAttempts = options?.maxAttempts ?? 5;
|
|
1141
|
-
const uploadChunkSize = options?.uploadChunkSize ??
|
|
1163
|
+
const uploadChunkSize = options?.uploadChunkSize ?? 1;
|
|
1142
1164
|
let status = await withRetries(
|
|
1143
1165
|
() => syncClient.checkSnapshotStatus(snapshotHash),
|
|
1144
1166
|
logger2,
|
|
@@ -1243,7 +1265,10 @@ async function runInitialSyncPipeline(runtime) {
|
|
|
1243
1265
|
runtime.snapshotsRepo,
|
|
1244
1266
|
runtime.clients.sync,
|
|
1245
1267
|
syncLogger,
|
|
1246
|
-
{
|
|
1268
|
+
{
|
|
1269
|
+
maxAttempts: runtime.config.maxSnapshotAttempts,
|
|
1270
|
+
uploadChunkSize: runtime.config.uploadChunkSize
|
|
1271
|
+
}
|
|
1247
1272
|
);
|
|
1248
1273
|
return result;
|
|
1249
1274
|
}
|
|
@@ -1647,7 +1672,10 @@ var ServiceRunner = class {
|
|
|
1647
1672
|
this.runtime.snapshotsRepo,
|
|
1648
1673
|
this.runtime.clients.sync,
|
|
1649
1674
|
log,
|
|
1650
|
-
{
|
|
1675
|
+
{
|
|
1676
|
+
maxAttempts: this.runtime.config.maxSnapshotAttempts,
|
|
1677
|
+
uploadChunkSize: this.runtime.config.uploadChunkSize
|
|
1678
|
+
}
|
|
1651
1679
|
);
|
|
1652
1680
|
this.runtime.outbox.ack(job.id, this.fsControlLeaseOwner);
|
|
1653
1681
|
this.state.updateSnapshotReady(result.createdAt);
|
|
@@ -1705,6 +1733,63 @@ function collectIndexingStatus(runtime, runner) {
|
|
|
1705
1733
|
service: runner.getServiceStateSnapshot()
|
|
1706
1734
|
};
|
|
1707
1735
|
}
|
|
1736
|
+
function formatStatus(status) {
|
|
1737
|
+
const lines = [];
|
|
1738
|
+
lines.push("=== Coderule Indexing Status ===");
|
|
1739
|
+
lines.push("");
|
|
1740
|
+
lines.push(`Timestamp: ${new Date(status.timestamp).toISOString()}`);
|
|
1741
|
+
lines.push("");
|
|
1742
|
+
lines.push("Repository:");
|
|
1743
|
+
lines.push(` ID: ${status.root.id}`);
|
|
1744
|
+
lines.push(` Path: ${status.root.path}`);
|
|
1745
|
+
lines.push("");
|
|
1746
|
+
lines.push("Files:");
|
|
1747
|
+
lines.push(` Total: ${status.files.total}`);
|
|
1748
|
+
lines.push(" States:");
|
|
1749
|
+
lines.push(` Clean: ${status.files.byState.clean}`);
|
|
1750
|
+
lines.push(` Dirty: ${status.files.byState.dirty}`);
|
|
1751
|
+
lines.push(` Hashing: ${status.files.byState.hashing}`);
|
|
1752
|
+
lines.push(` Missing: ${status.files.byState.missing}`);
|
|
1753
|
+
lines.push("");
|
|
1754
|
+
lines.push("Queue:");
|
|
1755
|
+
lines.push(` Pending: ${status.queue.pending}`);
|
|
1756
|
+
lines.push(` Processing: ${status.queue.processing}`);
|
|
1757
|
+
lines.push(` Done: ${status.queue.done}`);
|
|
1758
|
+
lines.push(` Failed: ${status.queue.failed}`);
|
|
1759
|
+
lines.push("");
|
|
1760
|
+
if (status.latestSnapshot) {
|
|
1761
|
+
lines.push("Latest Snapshot:");
|
|
1762
|
+
lines.push(` Hash: ${status.latestSnapshot.snapshot_hash}`);
|
|
1763
|
+
lines.push(` Files: ${status.latestSnapshot.files_count}`);
|
|
1764
|
+
lines.push(` Size: ${formatBytes(status.latestSnapshot.total_size)}`);
|
|
1765
|
+
lines.push(
|
|
1766
|
+
` Created: ${new Date(status.latestSnapshot.created_at).toISOString()}`
|
|
1767
|
+
);
|
|
1768
|
+
} else {
|
|
1769
|
+
lines.push("Latest Snapshot: None");
|
|
1770
|
+
}
|
|
1771
|
+
lines.push("");
|
|
1772
|
+
lines.push("Service State:");
|
|
1773
|
+
lines.push(
|
|
1774
|
+
` Last Change: ${new Date(status.service.lastChangeAt).toISOString()}`
|
|
1775
|
+
);
|
|
1776
|
+
lines.push(
|
|
1777
|
+
` Last Snapshot Ready: ${new Date(status.service.lastSnapshotReadyAt).toISOString()}`
|
|
1778
|
+
);
|
|
1779
|
+
lines.push(
|
|
1780
|
+
` Last Heartbeat: ${status.service.lastHeartbeatEnqueuedAt > 0 ? new Date(status.service.lastHeartbeatEnqueuedAt).toISOString() : "Never"}`
|
|
1781
|
+
);
|
|
1782
|
+
lines.push(` Watcher Ready: ${status.service.watcherReady ? "Yes" : "No"}`);
|
|
1783
|
+
lines.push(` Buffering: ${status.service.buffering ? "Yes" : "No"}`);
|
|
1784
|
+
return lines.join("\n");
|
|
1785
|
+
}
|
|
1786
|
+
function formatBytes(bytes) {
|
|
1787
|
+
if (bytes === 0) return "0 B";
|
|
1788
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
1789
|
+
const k = 1024;
|
|
1790
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
1791
|
+
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${units[i]}`;
|
|
1792
|
+
}
|
|
1708
1793
|
|
|
1709
1794
|
// src/mcp/server.ts
|
|
1710
1795
|
var SERVER_NAME = "coderule-scanner-mcp";
|
|
@@ -1713,6 +1798,37 @@ function createMcpServer({
|
|
|
1713
1798
|
runtime,
|
|
1714
1799
|
runner
|
|
1715
1800
|
}) {
|
|
1801
|
+
async function sleep3(ms) {
|
|
1802
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1803
|
+
}
|
|
1804
|
+
async function waitForLocalSnapshot(deadlineMs) {
|
|
1805
|
+
let latest = runtime.snapshotsRepo.getLatest();
|
|
1806
|
+
while (!latest && Date.now() < deadlineMs) {
|
|
1807
|
+
await sleep3(250);
|
|
1808
|
+
latest = runtime.snapshotsRepo.getLatest();
|
|
1809
|
+
}
|
|
1810
|
+
return latest;
|
|
1811
|
+
}
|
|
1812
|
+
async function waitForServerReady(initialHash, deadlineMs) {
|
|
1813
|
+
let currentHash = initialHash;
|
|
1814
|
+
while (Date.now() < deadlineMs) {
|
|
1815
|
+
try {
|
|
1816
|
+
const status = await runtime.clients.sync.checkSnapshotStatus(currentHash);
|
|
1817
|
+
if (status.status === "READY") {
|
|
1818
|
+
return currentHash;
|
|
1819
|
+
}
|
|
1820
|
+
if (status.status === "FAILED") {
|
|
1821
|
+
}
|
|
1822
|
+
} catch {
|
|
1823
|
+
}
|
|
1824
|
+
await sleep3(500);
|
|
1825
|
+
const latest = runtime.snapshotsRepo.getLatest();
|
|
1826
|
+
if (latest && latest.snapshot_hash !== currentHash) {
|
|
1827
|
+
currentHash = latest.snapshot_hash;
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
return void 0;
|
|
1831
|
+
}
|
|
1716
1832
|
const server = new mcp_js.McpServer({
|
|
1717
1833
|
name: SERVER_NAME,
|
|
1718
1834
|
version: SERVER_VERSION,
|
|
@@ -1727,7 +1843,7 @@ function createMcpServer({
|
|
|
1727
1843
|
},
|
|
1728
1844
|
async () => {
|
|
1729
1845
|
const status = collectIndexingStatus(runtime, runner);
|
|
1730
|
-
const text =
|
|
1846
|
+
const text = formatStatus(status);
|
|
1731
1847
|
return {
|
|
1732
1848
|
content: [{ type: "text", text }]
|
|
1733
1849
|
};
|
|
@@ -1748,38 +1864,37 @@ function createMcpServer({
|
|
|
1748
1864
|
query,
|
|
1749
1865
|
budgetTokens
|
|
1750
1866
|
}) => {
|
|
1751
|
-
const
|
|
1867
|
+
const deadline = Date.now() + runtime.config.maxQueryWaitMs;
|
|
1868
|
+
const latest = await waitForLocalSnapshot(deadline);
|
|
1752
1869
|
if (!latest) {
|
|
1753
|
-
const
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1870
|
+
const statusText = formatStatus(collectIndexingStatus(runtime, runner));
|
|
1871
|
+
const text = `We are not ready....
|
|
1872
|
+
${statusText}`;
|
|
1873
|
+
return { content: [{ type: "text", text }] };
|
|
1874
|
+
}
|
|
1875
|
+
const readyHash = await waitForServerReady(
|
|
1876
|
+
latest.snapshot_hash,
|
|
1877
|
+
deadline
|
|
1878
|
+
);
|
|
1879
|
+
if (!readyHash) {
|
|
1880
|
+
const statusText = formatStatus(collectIndexingStatus(runtime, runner));
|
|
1881
|
+
const text = `We are not ready....
|
|
1882
|
+
${statusText}`;
|
|
1883
|
+
return { content: [{ type: "text", text }] };
|
|
1758
1884
|
}
|
|
1759
1885
|
const effectiveBudget = Math.max(100, budgetTokens ?? 3e3);
|
|
1760
1886
|
try {
|
|
1761
1887
|
const result = await runtime.clients.retrieval.query(
|
|
1762
|
-
|
|
1888
|
+
readyHash,
|
|
1763
1889
|
query,
|
|
1764
1890
|
effectiveBudget,
|
|
1765
|
-
{
|
|
1766
|
-
formatter: runtime.config.retrievalFormatter
|
|
1767
|
-
}
|
|
1891
|
+
{ formatter: runtime.config.retrievalFormatter }
|
|
1768
1892
|
);
|
|
1769
|
-
const summary = {
|
|
1770
|
-
snapshotHash: latest.snapshot_hash,
|
|
1771
|
-
budgetTokens: effectiveBudget,
|
|
1772
|
-
formatter: runtime.config.retrievalFormatter
|
|
1773
|
-
};
|
|
1774
1893
|
return {
|
|
1775
1894
|
content: [
|
|
1776
1895
|
{
|
|
1777
1896
|
type: "text",
|
|
1778
1897
|
text: result.formatted_output ?? "(no formatted output)"
|
|
1779
|
-
},
|
|
1780
|
-
{
|
|
1781
|
-
type: "text",
|
|
1782
|
-
text: JSON.stringify({ summary, result }, null, 2)
|
|
1783
1898
|
}
|
|
1784
1899
|
]
|
|
1785
1900
|
};
|
|
@@ -1812,7 +1927,9 @@ var ENV_FLAG_MAP = {
|
|
|
1812
1927
|
"queue-poll": "CODERULE_QUEUE_POLL_INTERVAL_MS",
|
|
1813
1928
|
"hash-batch": "CODERULE_HASH_BATCH_SIZE",
|
|
1814
1929
|
"hash-lease": "CODERULE_HASH_LEASE_MS",
|
|
1815
|
-
"max-snapshot-attempts": "CODERULE_MAX_SNAPSHOT_ATTEMPTS"
|
|
1930
|
+
"max-snapshot-attempts": "CODERULE_MAX_SNAPSHOT_ATTEMPTS",
|
|
1931
|
+
"upload-chunk-size": "CODERULE_UPLOAD_CHUNK_SIZE",
|
|
1932
|
+
"max-wait-time": "CODERULE_MAX_WAIT_TIME"
|
|
1816
1933
|
};
|
|
1817
1934
|
function printUsage() {
|
|
1818
1935
|
console.log(`Usage: coderule-mcp-server [token] [options]
|
|
@@ -1854,6 +1971,12 @@ function printUsage() {
|
|
|
1854
1971
|
console.log(
|
|
1855
1972
|
" --max-snapshot-attempts <n> Override CODERULE_MAX_SNAPSHOT_ATTEMPTS"
|
|
1856
1973
|
);
|
|
1974
|
+
console.log(
|
|
1975
|
+
" --upload-chunk-size <n> Override CODERULE_UPLOAD_CHUNK_SIZE (default 1)"
|
|
1976
|
+
);
|
|
1977
|
+
console.log(
|
|
1978
|
+
" --max-wait-time <sec> Override CODERULE_MAX_WAIT_TIME (default 50s)"
|
|
1979
|
+
);
|
|
1857
1980
|
console.log(
|
|
1858
1981
|
" KEY=value Set arbitrary environment variable"
|
|
1859
1982
|
);
|
|
@@ -1975,6 +2098,10 @@ async function main() {
|
|
|
1975
2098
|
const runner = new ServiceRunner(runtime);
|
|
1976
2099
|
try {
|
|
1977
2100
|
await runner.prepareWatcher(true);
|
|
2101
|
+
const server = createMcpServer({ runtime, runner });
|
|
2102
|
+
const transport = new stdio_js.StdioServerTransport();
|
|
2103
|
+
await server.connect(transport);
|
|
2104
|
+
runtime.logger.info("MCP server connected via stdio");
|
|
1978
2105
|
let initialCreatedAt;
|
|
1979
2106
|
try {
|
|
1980
2107
|
const initial = await runInitialSyncPipeline(runtime);
|
|
@@ -1983,7 +2110,7 @@ async function main() {
|
|
|
1983
2110
|
snapshotHash: initial.snapshotHash,
|
|
1984
2111
|
filesCount: initial.filesCount
|
|
1985
2112
|
},
|
|
1986
|
-
"Initial sync completed;
|
|
2113
|
+
"Initial sync completed; entering continuous mode"
|
|
1987
2114
|
);
|
|
1988
2115
|
initialCreatedAt = initial.createdAt;
|
|
1989
2116
|
} catch (error) {
|
|
@@ -1998,10 +2125,6 @@ async function main() {
|
|
|
1998
2125
|
}
|
|
1999
2126
|
await runner.startLoops();
|
|
2000
2127
|
await runner.enableWatcherProcessing();
|
|
2001
|
-
const server = createMcpServer({ runtime, runner });
|
|
2002
|
-
const transport = new stdio_js.StdioServerTransport();
|
|
2003
|
-
await server.connect(transport);
|
|
2004
|
-
runtime.logger.info("MCP server connected via stdio");
|
|
2005
2128
|
const signal = await awaitShutdownSignals();
|
|
2006
2129
|
runtime.logger.info({ signal }, "Shutdown signal received");
|
|
2007
2130
|
if (typeof transport.close === "function") {
|