cf-memory-mcp 3.9.0 → 3.9.2
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/bin/cf-memory-mcp.js +49 -43
- package/package.json +1 -1
package/bin/cf-memory-mcp.js
CHANGED
|
@@ -809,25 +809,31 @@ class CFMemoryMCP {
|
|
|
809
809
|
* a full re-index, just refresh the affected files.
|
|
810
810
|
*/
|
|
811
811
|
async handleRefreshFiles(message) {
|
|
812
|
-
const
|
|
812
|
+
const payload = await this.refreshFilesCore(message.params?.arguments || {});
|
|
813
|
+
process.stdout.write(JSON.stringify({
|
|
814
|
+
jsonrpc: '2.0',
|
|
815
|
+
id: message.id,
|
|
816
|
+
result: { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] },
|
|
817
|
+
}) + '\n');
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Core refresh-files logic: reads local files, uploads them, returns a
|
|
822
|
+
* payload. Extracted so the auto-refresh path can call it without
|
|
823
|
+
* needing to capture stdout (the previous approach of swapping
|
|
824
|
+
* process.stdout.write was racy against other concurrent messages).
|
|
825
|
+
*/
|
|
826
|
+
async refreshFilesCore(args) {
|
|
813
827
|
const projectIdOrName = args.project_id;
|
|
814
828
|
const filePaths = Array.isArray(args.file_paths) ? args.file_paths : [];
|
|
815
829
|
const projectRoot = args.project_root ? path.resolve(args.project_root)
|
|
816
830
|
: (process.env.CF_MEMORY_WATCH_PATH || process.cwd());
|
|
817
831
|
|
|
818
|
-
const respond = (payload) => {
|
|
819
|
-
process.stdout.write(JSON.stringify({
|
|
820
|
-
jsonrpc: '2.0',
|
|
821
|
-
id: message.id,
|
|
822
|
-
result: { content: [{ type: 'text', text: JSON.stringify(payload, null, 2) }] },
|
|
823
|
-
}) + '\n');
|
|
824
|
-
};
|
|
825
|
-
|
|
826
832
|
if (!projectIdOrName) {
|
|
827
|
-
return
|
|
833
|
+
return { error: 'project_id is required' };
|
|
828
834
|
}
|
|
829
835
|
if (filePaths.length === 0) {
|
|
830
|
-
return
|
|
836
|
+
return { error: 'file_paths (string[]) is required' };
|
|
831
837
|
}
|
|
832
838
|
|
|
833
839
|
// Resolve to project ID via list_projects if a name was given.
|
|
@@ -872,26 +878,26 @@ class CFMemoryMCP {
|
|
|
872
878
|
}
|
|
873
879
|
|
|
874
880
|
if (files.length === 0) {
|
|
875
|
-
return
|
|
881
|
+
return {
|
|
876
882
|
project_id: projectId,
|
|
877
883
|
files_refreshed: 0,
|
|
878
884
|
skipped,
|
|
879
885
|
hint: `All ${filePaths.length} file path(s) failed to read. Check that paths are relative to project_root (${projectRoot}). If the project was indexed from a different directory, pass project_root=<that directory>.`,
|
|
880
|
-
}
|
|
886
|
+
};
|
|
881
887
|
}
|
|
882
888
|
|
|
883
889
|
const uploadResult = await this.uploadFileBatch(projectId, files);
|
|
884
890
|
const refreshed = (uploadResult && typeof uploadResult.files_indexed === 'number') ? uploadResult.files_indexed : 0;
|
|
885
891
|
const chunks = (uploadResult && typeof uploadResult.chunks_created === 'number') ? uploadResult.chunks_created : 0;
|
|
886
892
|
|
|
887
|
-
return
|
|
893
|
+
return {
|
|
888
894
|
project_id: projectId,
|
|
889
895
|
files_attempted: files.length,
|
|
890
896
|
files_refreshed: refreshed,
|
|
891
897
|
chunks_created: chunks,
|
|
892
898
|
skipped,
|
|
893
899
|
errors: uploadResult && Array.isArray(uploadResult.errors) ? uploadResult.errors : undefined,
|
|
894
|
-
}
|
|
900
|
+
};
|
|
895
901
|
}
|
|
896
902
|
|
|
897
903
|
/**
|
|
@@ -1656,10 +1662,15 @@ class CFMemoryMCP {
|
|
|
1656
1662
|
|
|
1657
1663
|
/**
|
|
1658
1664
|
* If the just-returned retrieve_context response had stale files,
|
|
1659
|
-
* refresh them via
|
|
1665
|
+
* refresh them via refreshFilesCore, re-run the original query, and
|
|
1660
1666
|
* return the fresh response. Opt-in via CF_MEMORY_AUTO_REFRESH or
|
|
1661
1667
|
* per-call `auto_refresh:true`. Guards against infinite loops by
|
|
1662
1668
|
* stripping auto_refresh from the re-query.
|
|
1669
|
+
*
|
|
1670
|
+
* Uses refreshFilesCore (returns data directly) rather than
|
|
1671
|
+
* handleRefreshFiles (writes to stdout). Avoids globally overriding
|
|
1672
|
+
* process.stdout.write, which was racy against any other MCP message
|
|
1673
|
+
* being handled concurrently in the same isolate.
|
|
1663
1674
|
*/
|
|
1664
1675
|
async maybeAutoRefreshAndRequery(response, originalMessage) {
|
|
1665
1676
|
try {
|
|
@@ -1676,33 +1687,27 @@ class CFMemoryMCP {
|
|
|
1676
1687
|
|
|
1677
1688
|
_mcpTrace('AUTO_REFRESH', `${stalePaths.length} stale files; refreshing then re-querying`);
|
|
1678
1689
|
|
|
1679
|
-
//
|
|
1680
|
-
const
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
return
|
|
1699
|
-
};
|
|
1700
|
-
try {
|
|
1701
|
-
await this.handleRefreshFiles(refreshMessage);
|
|
1702
|
-
} finally {
|
|
1703
|
-
process.stdout.write = origWrite;
|
|
1690
|
+
// Call core directly — no stdout override needed.
|
|
1691
|
+
const refreshResult = await this.refreshFilesCore({
|
|
1692
|
+
project_id: projectId,
|
|
1693
|
+
file_paths: stalePaths,
|
|
1694
|
+
});
|
|
1695
|
+
_mcpTrace('AUTO_REFRESH_DONE', `refreshed=${refreshResult.files_refreshed || 0}`);
|
|
1696
|
+
|
|
1697
|
+
// If refresh itself errored or refreshed 0 files, return the
|
|
1698
|
+
// original response with a clear note rather than re-querying
|
|
1699
|
+
// and pretending the refresh succeeded.
|
|
1700
|
+
if (refreshResult.error || (refreshResult.files_refreshed || 0) === 0) {
|
|
1701
|
+
try {
|
|
1702
|
+
parsed.auto_refresh_failed = {
|
|
1703
|
+
attempted: stalePaths.length,
|
|
1704
|
+
error: refreshResult.error || 'refresh ran but indexed 0 files (paths may be unreadable or project_root mismatched)',
|
|
1705
|
+
skipped: refreshResult.skipped,
|
|
1706
|
+
};
|
|
1707
|
+
response.result.content[0].text = JSON.stringify(parsed);
|
|
1708
|
+
} catch (_) { /* best-effort tag */ }
|
|
1709
|
+
return response;
|
|
1704
1710
|
}
|
|
1705
|
-
_mcpTrace('AUTO_REFRESH_DONE', `refreshed ${stalePaths.length} files; captured=${captured.length}b`);
|
|
1706
1711
|
|
|
1707
1712
|
// Re-run the original retrieve_context (without auto_refresh
|
|
1708
1713
|
// to prevent loops). Server cache invalidates on write so the
|
|
@@ -1722,7 +1727,8 @@ class CFMemoryMCP {
|
|
|
1722
1727
|
if (freshText) {
|
|
1723
1728
|
const freshParsed = JSON.parse(freshText);
|
|
1724
1729
|
freshParsed.auto_refreshed = {
|
|
1725
|
-
files_refreshed:
|
|
1730
|
+
files_refreshed: refreshResult.files_refreshed,
|
|
1731
|
+
chunks_created: refreshResult.chunks_created || 0,
|
|
1726
1732
|
file_paths: stalePaths,
|
|
1727
1733
|
};
|
|
1728
1734
|
fresh.result.content[0].text = JSON.stringify(freshParsed);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cf-memory-mcp",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.2",
|
|
4
4
|
"description": "Cloudflare-hosted MCP server for code indexing, retrieval, and assistant memory with a direct remote MCP endpoint and local stdio bridge.",
|
|
5
5
|
"main": "bin/cf-memory-mcp.js",
|
|
6
6
|
"bin": {
|