@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.
- package/CHANGELOG.md +31 -0
- package/README.md +2 -0
- package/agent/milestones/milestone-2-bug-fixes-production-readiness.md +159 -0
- package/agent/progress.yaml +92 -13
- package/agent/tasks/task-4-fix-read-file-not-found-bug.md +390 -0
- package/dist/server-factory.js +212 -19
- package/dist/server-factory.js.map +3 -3
- package/dist/server.js +206 -22
- package/dist/server.js.map +3 -3
- package/dist/utils/logger.d.ts +43 -0
- package/package.json +1 -1
- package/src/server-factory.ts +45 -16
- package/src/server.ts +36 -18
- package/src/tools/acp-remote-execute-command.ts +11 -0
- package/src/tools/acp-remote-list-files.ts +7 -4
- package/src/tools/acp-remote-read-file.ts +6 -0
- package/src/tools/acp-remote-write-file.ts +6 -0
- package/src/utils/logger.ts +131 -0
- package/src/utils/ssh-connection.ts +59 -0
|
@@ -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
|