Repository navigation
Conversation
|
Hi I really need this feature. could you please take a look? |
This commit adds two new tools to the filesystem server: 1. **append_file**: Appends content to the end of an existing file. - File must already exist - Preserves existing content, adds new content at the end 2. **write_or_update_file**: Creates a new file or appends to an existing file. - If file doesn't exist: creates it with the provided content - If file exists: appends new content to the end - Useful when you want to add content while preserving existing data Changes include: - Added `appendFileContent` and `writeOrUpdateFileContent` functions to lib.ts - Added tool registrations and schemas to index.ts - Updated README.md with new tool documentation - Added comprehensive tests for the new functions 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> Signed-off-by: Benyamin <benyaminjmf@gmail.com>
d4d0d52 to
d15eb75
Compare
|
I just resolved the merge conflicts in my previously submitted PR. Could you please review it to help prevent future conflicts? |
|
@claude review |
|
Claude finished @cliffhall's task in 1m 43s —— View job PR Review: feat(filesystem): add append_file and write_or_update_file tools
Thanks for this PR, @agn-7. The feature direction is sound — there's a real gap between "always overwrite" and "no append option." A few issues need addressing before merge. Critical Issues1. Memory-inefficient read-modify-write in Both new functions read the entire file into memory, concatenate, then write atomically. For their stated use case (log files, incrementally-built documents), this is O(n) memory in file size — exactly where you'd expect large files. Node's export async function appendFileContent(filePath: string, content: string): Promise<void> {
try {
await fs.access(filePath);
} catch (error) {
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
throw new Error(`File does not exist: ${filePath}`);
}
throw error;
}
await fs.appendFile(filePath, content, 'utf-8');
}The existing atomic-rename pattern in 2. TOCTOU race between The check-then-act pattern is inherently racy. Cleaner approach — just attempt the read directly: let existingContent: string;
try {
existingContent = await readFileContent(filePath);
} catch (error) {
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
throw new Error(`File does not exist: ${filePath}`);
}
throw error;
}This eliminates the race and removes a redundant syscall. 3. Unreliable const message = stats.birthtime.getTime() === stats.mtime.getTime()
? `Successfully created ${args.path} with content`
: `Successfully appended content to ${args.path}`;After an atomic const existed = await fs.access(validPath).then(() => true).catch(() => false);
await writeOrUpdateFileContent(validPath, args.content);
const message = existed
? `Successfully appended content to ${args.path}`
: `Successfully created ${args.path} with content`;Design Issues4. Code duplication ( The if (fileExists) {
await appendFileContent(filePath, content);
} else {
await writeFileContent(filePath, content);
}5. Semantics of "Update" typically implies overwriting or modifying existing content. The actual behavior here is "create or append," which may surprise users who interpret "update" as replacing content. Consider Minor Issues6. Three identical Zod schemas (
7. Tests verify mock calls, not behavior The new tests (e.g., What Looks Good
|
- Use fs.appendFile directly instead of read-modify-write (O(1) memory) - Eliminate TOCTOU race between fs.access and fs.readFile - Replace unreliable birthtime===mtime heuristic with pre-op existence check - Have writeOrUpdateFileContent delegate to appendFileContent (no duplication) - Consolidate three identical Zod schemas into one PathContentArgsSchema Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Benyamin <benyaminjmf@gmail.com>
|
Pushed 54ed837 addressing the review. Per-item response: 1. Memory-inefficient read-modify-write — fixed. 2. TOCTOU race — fixed. Removed the 3. Unreliable 4. Code duplication — fixed. 6. Three identical Zod schemas — consolidated into 5. 7. Mock-based tests — left as-is, per the reviewer's note that this matches the existing style. Tests were updated to reflect the new direct- |
…_file Renames the tool, schema, and underlying function to accurately describe the behavior (create-or-append, not update/overwrite) per review feedback. - Tool: write_or_update_file -> create_or_append_file - Schema: WriteOrUpdateFileArgsSchema -> CreateOrAppendFileArgsSchema - Function: writeOrUpdateFileContent -> createOrAppendFileContent - README updated; tests updated; append_file description updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Benyamin <benyaminjmf@gmail.com>
|
Follow-up on item 5: rename applied in ebc44b2.
|
|
@claude review, I fixed the things you mentioned. |
|
this PR also resolves this issue #1208 |
CONTRIBUTING.md, the PR template and the issue forms, byte-identical to v2/main, so the default branch states the issues-not-PRs policy before external pull requests are turned off. Nothing else changes and nothing is released. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
Takes CONTRIBUTING.md from v2/main, which dropped the transitional blank-issue fallback (modelcontextprotocol#4940), so this branch stays byte-identical to v2/main and main does not receive the stale sentence. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
Takes .github/pull_request_template.md from v2/main, which reduced it to the issues-not-PRs banner (modelcontextprotocol#4960), so this branch stays byte-identical to v2/main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
…ol#4965 Takes .github/ISSUE_TEMPLATE/1-bug_report.yml from v2/main, whose Server version placeholder now shows both version schemes (modelcontextprotocol#4964), so this branch stays byte-identical to v2/main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
…l/v2/chore/4938-contribution-policy-to-main chore: bring the contribution policy to main ahead of the v2.0.0 merge
Brings main's CONTRIBUTING.md byte-identical to v2/main's, so the v1.0.0 milestone merge (modelcontextprotocol#5080) produces v2/main's tree without a conflict. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
…l/v2/chore/5080-contributing-sync-to-main docs(contributing): sync main with v2/main ahead of the v1.0.0 merge
|
One remaining correctness issue in the latest head ( I reproduced this with an isolated real filesystem test in Node 22: Could you make This overlaps a maintained downstream's verified physical EOF append requirement, so I'd rather help the existing PR reach correct semantics than open a duplicate PR. |
…l/v2/chore/5080-release-v1.0.0-sdk1 Release v1.0.0: merge v2/release/1.0.0 into main
…erge The guard (modelcontextprotocol#5119, PR modelcontextprotocol#5120 on v2/main) runs on pull_request_target, which loads the workflow and script from main. Bring the same three files here byte-for-byte, so the check can run on, and be required for, PRs into main before the next milestone merge carries them. Part of modelcontextprotocol#5119 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: cliffhall <cliff@futurescale.com>
…l/v2/chore/5119-main-source-bootstrap ci: bring the main-source guard to main (bootstrap for modelcontextprotocol#5119)
Open existing files with O_APPEND and without O_CREAT; preserve inode and symlink-swap checks across platforms. Use exclusive creation for create_or_append_file and append after a competing creation instead of overwriting. Move tool registrations into the refactored server and add real filesystem regression coverage. Signed-off-by: Benyamin <benyaminjmf@gmail.com>
|
Thanks @X1pheR — fixed in 9bb47c9. This same PR is now updated against @cliffhall, I'd like to carry this existing filesystem contribution through review and contribute more in this area. Could you review the updated PR or advise the next step within the maintainers' workflow? I'd appreciate the opportunity to complete this contribution in coordination with you. |
2925cdf to
b545744
Compare
🦋 Changeset detectedLatest commit: 9bb47c9 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Signed-off-by: Benyamin <benyaminjmf@gmail.com>
Closes #1208
Description
Add
append_fileandcreate_or_append_fileto the filesystem server.append_fileadds the supplied text at physical EOF of an existing file and rejects missing files without creating them.create_or_append_filecreates a missing file or appends to an existing one without overwriting previous content.This update addresses the correctness report in #2816 (comment) and resolves conflicts and integrates the current
v2/maindevelopment branch (SDK v2). Tool registrations now live inserver.ts, matching the upstream refactor. The earlier namewrite_or_update_filewas renamed tocreate_or_append_filebefore merge.Server Details
Motivation and Context
Support append-only logs and incremental documents without reading and rewriting the whole file. The original request #2807 was closed as a duplicate of #1208.
Both tools append the content exactly as provided. Callers include a newline in
contentwhen desired.How Has This Been Tested?
npm run local:gatepasses (exit 0) on the exact published tree in an isolated checkout: install freshness, DCO, all TypeScript validation and coverage, all Python validation and coverage, the pinned skills validator, and every server transport smoke.tools/callrequests pass with explicit--protocol-era legacyand--protocol-era modern. Appending-aftertoexisting.txtyieldsbefore-afterwith the same inode; appending tomissing.txtreturnsFile does not existand leaves it absent;create_or_append_filecreatesfresh.txtwithcreated.before-after, missing remains absent, andcreated. This client exposes no protocol-era switch, so the LLM run's era is not labelled.Breaking Changes
No existing published tool is changed. These are new tools.
Types of changes
Checklist
Additional context
The append path uses
O_WRONLY | O_APPENDwithoutO_CREATorO_TRUNC, addsO_NOFOLLOWandO_NONBLOCKwhere supported, and compares the opened handle's regular-file status, device and inode with the pre-open lstat. Writes use that checked handle and close it in finally. This follows the current upstream file-identity boundary; it does not claim to eliminate every possible parent-directory race.Creation uses an exclusive
wxopen. If another process creates the file first, the tool returns to the checked append path. The success message comes from the actual operation, replacing the old pre-operation existence guess.The three historical feature commits now carry DCO signoffs. Their code, authorship and merge structure are preserved.