@prmichaelsen/acp-mcp 0.4.1 → 0.5.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.
@@ -0,0 +1,390 @@
1
+ # Task 4: Fix acp_remote_read_file "File Not Found" Bug
2
+
3
+ **Milestone**: M2 - Bug Fixes and Production Readiness
4
+ **Estimated Time**: 3-4 hours
5
+ **Dependencies**: None (critical bug fix)
6
+ **Status**: Not Started
7
+ **Priority**: 🚨 CRITICAL
8
+ **GitHub Issue**: [#1 - File Access Issue](https://github.com/prmichaelsen/acp-mcp/issues/1)
9
+
10
+ ---
11
+
12
+ ## Objective
13
+
14
+ Investigate and fix the critical bug where [`acp_remote_read_file`](../../src/tools/acp-remote-read-file.ts) returns "file not found" errors for paths that exist and are confirmed by [`acp_remote_list_files`](../../src/tools/acp-remote-list-files.ts).
15
+
16
+ This bug prevents agents from reading files on remote machines, breaking the core functionality of the acp-mcp server and blocking production deployment to agentbase.me platform.
17
+
18
+ ---
19
+
20
+ ## Context
21
+
22
+ **GitHub Issue #1** reports that after successfully listing files in a directory (e.g., `/home/prmichaelsen/agentbase.me`), attempting to read those files (e.g., `/home/prmichaelsen/agentbase.me/package.json`) results in "file not found" errors.
23
+
24
+ **Impact**:
25
+ - Breaks agent's ability to inspect source code on remote machines
26
+ - Inconsistent behavior between list and read operations
27
+ - Blocks production deployment to agentbase.me platform
28
+ - Affects all users trying to use remote development features
29
+
30
+ **Possible Root Causes**:
31
+ 1. Path resolution issue in [`SSHConnectionManager.readFile()`](../../src/utils/ssh-connection.ts:207)
32
+ 2. SFTP `stat()` call failing silently or with wrong error
33
+ 3. Working directory context not properly set
34
+ 4. Symbolic link handling issue
35
+ 5. Race condition between list and read operations
36
+ 6. Permissions problem (though list works)
37
+ 7. Path normalization differences between list and read
38
+
39
+ ---
40
+
41
+ ## Steps
42
+
43
+ ### 1. Reproduce the Bug Locally
44
+
45
+ Set up a test environment to reproduce the issue:
46
+
47
+ **Actions**:
48
+ - Configure SSH connection to a test remote machine
49
+ - Create test directory structure with known files
50
+ - Run `acp_remote_list_files` on test directory
51
+ - Attempt to read files returned by list operation
52
+ - Document exact error messages and behavior
53
+
54
+ **Expected Outcome**: Bug reproduced locally with clear error messages
55
+
56
+ ### 2. Add Enhanced Debug Logging
57
+
58
+ Add comprehensive logging to diagnose the issue:
59
+
60
+ **File**: [`src/utils/ssh-connection.ts`](../../src/utils/ssh-connection.ts:207)
61
+
62
+ **Add logging to `readFile()` method**:
63
+ ```typescript
64
+ async readFile(
65
+ path: string,
66
+ encoding: string = 'utf-8',
67
+ maxSize: number = 1048576
68
+ ): Promise<{ content: string; size: number; encoding: string }> {
69
+ const startTime = Date.now();
70
+ logger.debug('readFile() called', {
71
+ path,
72
+ encoding,
73
+ maxSize,
74
+ pathType: typeof path,
75
+ pathLength: path.length,
76
+ pathBytes: Buffer.from(path).toString('hex')
77
+ });
78
+
79
+ const sftp = await this.getSFTP();
80
+ logger.debug('SFTP connection obtained', { connected: this.connected });
81
+
82
+ return new Promise((resolve, reject) => {
83
+ // Log before stat call
84
+ logger.debug('Calling sftp.stat()', { path });
85
+
86
+ sftp.stat(path, (err, stats) => {
87
+ if (err) {
88
+ logger.error('SFTP stat() failed', {
89
+ path,
90
+ error: err.message,
91
+ errorCode: err.code,
92
+ errorStack: err.stack
93
+ });
94
+ reject(new Error(`File not found or inaccessible: ${path}`));
95
+ return;
96
+ }
97
+
98
+ logger.debug('SFTP stat() succeeded', {
99
+ path,
100
+ size: stats.size,
101
+ mode: stats.mode,
102
+ isFile: stats.isFile(),
103
+ isDirectory: stats.isDirectory()
104
+ });
105
+
106
+ // ... rest of method
107
+ });
108
+ });
109
+ }
110
+ ```
111
+
112
+ **Expected Outcome**: Detailed logs showing exactly where the failure occurs
113
+
114
+ ### 3. Compare List vs Read Path Handling
115
+
116
+ Analyze how paths are handled differently:
117
+
118
+ **Actions**:
119
+ - Review [`listFiles()`](../../src/utils/ssh-connection.ts:184) method
120
+ - Compare path handling with [`readFile()`](../../src/utils/ssh-connection.ts:207)
121
+ - Check if list uses different SFTP methods
122
+ - Verify path normalization in both methods
123
+ - Test with absolute vs relative paths
124
+
125
+ **Expected Outcome**: Identify any path handling differences
126
+
127
+ ### 4. Test SFTP Operations Directly
128
+
129
+ Test SFTP operations in isolation:
130
+
131
+ **Create test script**:
132
+ ```typescript
133
+ // test-sftp.ts
134
+ import { SSHConnectionManager } from './src/utils/ssh-connection.js';
135
+
136
+ async function testSFTP() {
137
+ const ssh = new SSHConnectionManager({
138
+ host: process.env.SSH_HOST!,
139
+ port: 22,
140
+ username: process.env.SSH_USERNAME!,
141
+ privateKey: process.env.SSH_PRIVATE_KEY!
142
+ });
143
+
144
+ await ssh.connect();
145
+
146
+ // Test list
147
+ const files = await ssh.listFiles('/home/prmichaelsen/agentbase.me');
148
+ console.log('Files:', files);
149
+
150
+ // Test stat on first file
151
+ const sftp = await ssh.getSFTP();
152
+ const testPath = '/home/prmichaelsen/agentbase.me/package.json';
153
+
154
+ sftp.stat(testPath, (err, stats) => {
155
+ if (err) {
156
+ console.error('Stat failed:', err);
157
+ } else {
158
+ console.log('Stat succeeded:', stats);
159
+ }
160
+ });
161
+
162
+ // Test readFile
163
+ try {
164
+ const result = await ssh.readFile(testPath);
165
+ console.log('Read succeeded:', result.size, 'bytes');
166
+ } catch (error) {
167
+ console.error('Read failed:', error);
168
+ }
169
+
170
+ ssh.disconnect();
171
+ }
172
+
173
+ testSFTP();
174
+ ```
175
+
176
+ **Expected Outcome**: Isolated test reveals exact failure point
177
+
178
+ ### 5. Implement Fix
179
+
180
+ Based on findings, implement the appropriate fix:
181
+
182
+ **Possible Fixes**:
183
+
184
+ **Fix A: Path Normalization**
185
+ ```typescript
186
+ async readFile(path: string, ...): Promise<...> {
187
+ // Normalize path (remove trailing slashes, resolve .., etc.)
188
+ const normalizedPath = path.replace(/\/+/g, '/').replace(/\/$/, '');
189
+ logger.debug('Path normalized', { original: path, normalized: normalizedPath });
190
+
191
+ // Use normalized path for SFTP operations
192
+ sftp.stat(normalizedPath, (err, stats) => {
193
+ // ...
194
+ });
195
+ }
196
+ ```
197
+
198
+ **Fix B: Better Error Handling**
199
+ ```typescript
200
+ sftp.stat(path, (err, stats) => {
201
+ if (err) {
202
+ // Provide more detailed error message
203
+ const errorDetails = {
204
+ path,
205
+ errorCode: err.code,
206
+ errorMessage: err.message,
207
+ suggestion: 'Verify file exists and you have read permissions'
208
+ };
209
+ logger.error('File stat failed', errorDetails);
210
+ reject(new Error(`Failed to access file: ${JSON.stringify(errorDetails)}`));
211
+ return;
212
+ }
213
+ // ...
214
+ });
215
+ ```
216
+
217
+ **Fix C: Working Directory Context**
218
+ ```typescript
219
+ // If paths are relative, prepend working directory
220
+ async readFile(path: string, ...): Promise<...> {
221
+ let fullPath = path;
222
+ if (!path.startsWith('/')) {
223
+ // Get home directory or working directory
224
+ const homeDir = await this.exec('pwd');
225
+ fullPath = `${homeDir.trim()}/${path}`;
226
+ logger.debug('Resolved relative path', { original: path, full: fullPath });
227
+ }
228
+ // ...
229
+ }
230
+ ```
231
+
232
+ **Expected Outcome**: Bug fixed with appropriate solution
233
+
234
+ ### 6. Add Regression Tests
235
+
236
+ Create tests to prevent this bug from recurring:
237
+
238
+ **File**: `src/utils/ssh-connection.test.ts`
239
+
240
+ ```typescript
241
+ describe('SSHConnectionManager', () => {
242
+ describe('readFile', () => {
243
+ it('should read files that exist per listFiles', async () => {
244
+ const ssh = new SSHConnectionManager(testConfig);
245
+ await ssh.connect();
246
+
247
+ // List files
248
+ const files = await ssh.listFiles('/test/directory');
249
+ expect(files.length).toBeGreaterThan(0);
250
+
251
+ // Read first file
252
+ const firstFile = files[0];
253
+ const fullPath = `/test/directory/${firstFile.name}`;
254
+
255
+ const result = await ssh.readFile(fullPath);
256
+ expect(result.content).toBeDefined();
257
+ expect(result.size).toBeGreaterThan(0);
258
+ });
259
+
260
+ it('should handle various path formats', async () => {
261
+ const ssh = new SSHConnectionManager(testConfig);
262
+ await ssh.connect();
263
+
264
+ const paths = [
265
+ '/absolute/path/file.txt',
266
+ '/path/with//double//slashes.txt',
267
+ '/path/with/trailing/slash/',
268
+ ];
269
+
270
+ for (const path of paths) {
271
+ // Test should not throw
272
+ await expect(ssh.readFile(path)).resolves.toBeDefined();
273
+ }
274
+ });
275
+ });
276
+ });
277
+ ```
278
+
279
+ **Expected Outcome**: Tests pass and prevent regression
280
+
281
+ ### 7. Update Documentation
282
+
283
+ Document the fix and any path handling requirements:
284
+
285
+ **Update**: [`README.md`](../../README.md)
286
+ - Document path format requirements for `acp_remote_read_file`
287
+ - Add examples of correct path usage
288
+ - Note any limitations or edge cases
289
+
290
+ **Update**: [`CHANGELOG.md`](../../CHANGELOG.md)
291
+ - Add entry for v0.4.2 with bug fix details
292
+ - Reference GitHub Issue #1
293
+
294
+ **Expected Outcome**: Users understand how to use the tool correctly
295
+
296
+ ### 8. Version Bump and Release
297
+
298
+ Prepare for release:
299
+
300
+ **Actions**:
301
+ - Update version in [`package.json`](../../package.json) to `0.4.2`
302
+ - Update [`CHANGELOG.md`](../../CHANGELOG.md) with fix details
303
+ - Build and test: `npm run build && npm test`
304
+ - Commit changes with message: `fix: resolve acp_remote_read_file file not found bug (fixes #1)`
305
+ - Tag release: `git tag v0.4.2`
306
+
307
+ **Expected Outcome**: Version 0.4.2 ready for deployment
308
+
309
+ ---
310
+
311
+ ## Verification
312
+
313
+ - [ ] Bug reproduced locally with clear steps
314
+ - [ ] Enhanced debug logging added to readFile() method
315
+ - [ ] Root cause identified through logging and testing
316
+ - [ ] Fix implemented and tested locally
317
+ - [ ] Regression tests added and passing
318
+ - [ ] All existing tests still pass
319
+ - [ ] TypeScript compiles without errors
320
+ - [ ] Build completes successfully
321
+ - [ ] Documentation updated (README, CHANGELOG)
322
+ - [ ] Version bumped to 0.4.2
323
+ - [ ] GitHub Issue #1 can be closed after deployment
324
+ - [ ] Fix verified on actual remote machine (agentbase.me environment)
325
+
326
+ ---
327
+
328
+ ## Expected Output
329
+
330
+ **Files Modified**:
331
+ - [`src/utils/ssh-connection.ts`](../../src/utils/ssh-connection.ts) - Enhanced logging and bug fix
332
+ - [`src/utils/ssh-connection.test.ts`](../../src/utils/ssh-connection.test.ts) - New regression tests (create if doesn't exist)
333
+ - [`README.md`](../../README.md) - Updated documentation
334
+ - [`CHANGELOG.md`](../../CHANGELOG.md) - v0.4.2 entry
335
+ - [`package.json`](../../package.json) - Version bump to 0.4.2
336
+
337
+ **CHANGELOG Entry**:
338
+ ```markdown
339
+ ## [0.4.2] - 2026-02-23
340
+
341
+ ### Fixed
342
+ - **CRITICAL**: Fixed acp_remote_read_file returning "file not found" for existing paths (Issue #1)
343
+ - [Specific fix description based on root cause]
344
+ - Enhanced error messages with detailed diagnostics
345
+ - Added regression tests to prevent recurrence
346
+ - Files confirmed by list_files can now be reliably read
347
+ ```
348
+
349
+ ---
350
+
351
+ ## Common Issues and Solutions
352
+
353
+ ### Issue 1: Cannot reproduce bug locally
354
+ **Symptom**: Bug doesn't occur in local test environment
355
+ **Solution**: Test with exact same paths and SSH configuration as production (agentbase.me). May need to test on actual remote server.
356
+
357
+ ### Issue 2: SFTP stat() succeeds but readFile() fails
358
+ **Symptom**: stat() returns file info but readFile() still fails
359
+ **Solution**: Check file permissions, encoding issues, or file size limits. May be a different error than "file not found".
360
+
361
+ ### Issue 3: Fix works for some paths but not others
362
+ **Symptom**: Inconsistent behavior across different paths
363
+ **Solution**: Test with various path formats (absolute, relative, with/without trailing slashes, special characters). May need comprehensive path normalization.
364
+
365
+ ---
366
+
367
+ ## Resources
368
+
369
+ - [ssh2 SFTP Documentation](https://github.com/mscdex/ssh2#sftp) - SFTP API reference
370
+ - [Node.js Path Module](https://nodejs.org/api/path.html) - Path normalization utilities
371
+ - [GitHub Issue #1](https://github.com/prmichaelsen/acp-mcp/issues/1) - Original bug report
372
+ - [SFTP Protocol RFC](https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02) - SFTP protocol specification
373
+
374
+ ---
375
+
376
+ ## Notes
377
+
378
+ - This is a **CRITICAL** bug that blocks production deployment
379
+ - Must be fixed before deploying to agentbase.me platform
380
+ - Enhanced logging should remain in place for future debugging
381
+ - Consider adding integration tests with real SSH server
382
+ - May need to test with different SSH server implementations
383
+ - Path handling should be consistent across all tools (list, read, write, execute)
384
+
385
+ ---
386
+
387
+ **Next Task**: Task 5 - Deploy to Production (after this fix)
388
+ **Related Design Docs**: [ACP MCP Core Tools](../../agent/design/acp-mcp-core-tools.md)
389
+ **GitHub Issue**: [#1](https://github.com/prmichaelsen/acp-mcp/issues/1)
390
+ **Estimated Completion Date**: 2026-02-23