patch_apply
Apply unified diff patches to files on remote servers via SSH to modify configurations, fix bugs, or update code without manual editing.
Instructions
Applies a patch to a file
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| sessionId | Yes | SSH session ID | |
| path | Yes | File path to patch | |
| patch | Yes | Patch content (unified diff format) |
Implementation Reference
- src/ensure.ts:329-412 (handler)The `applyPatch` function handles the logic for applying a unified diff patch to a remote file. It performs a dry run first and handles temporary file cleanup.
export async function applyPatch( sessionId: string, filePath: string, diff: string, sudoPassword?: string ): Promise<PatchResult> { logger.debug('Applying patch to file', { sessionId, filePath }); try { const osInfo = await sessionManager.getOSInfo(sessionId); // Check if patch command is available const hasPatch = await commandExists(sessionId, 'patch'); if (!hasPatch) { throw createPatchError( 'patch command not found on remote system', 'Install patch utility or apply changes manually' ); } // Write patch to temporary file const tempDir = resolveRemoteTempDir(osInfo); const baseTempDir = tempDir.replace(/\/+$/, ''); const tempPatchFile = `${baseTempDir}/ssh-mcp-patch-${Date.now()}.patch`; await writeFile(sessionId, tempPatchFile, diff); try { // Test patch first (dry run) const testResult = await execCommand( sessionId, `patch --dry-run -p0 ${filePath} < ${tempPatchFile}` ); if (testResult.code !== 0) { throw createPatchError( 'Patch would fail to apply', `Patch test failed: ${testResult.stderr}` ); } // Apply patch const applyCommand = `patch -p0 ${filePath} < ${tempPatchFile}`; let result; if (sudoPassword) { result = await execSudo(sessionId, applyCommand, sudoPassword); } else { result = await execCommand(sessionId, applyCommand); } const patchResult: PatchResult = { ok: result.code === 0, changed: result.code === 0 }; if (result.code === 0) { logger.info('Patch applied successfully', { sessionId, filePath }); } else { logger.error('Patch application failed', { sessionId, filePath, code: result.code, stderr: result.stderr }); } return patchResult; } finally { // Clean up temporary patch file try { const cleanupCommand = osInfo.platform === 'windows' ? `Remove-Item -Path ${tempPatchFile} -Force -ErrorAction SilentlyContinue` : `rm -f ${tempPatchFile}`; await execCommand(sessionId, cleanupCommand); } catch (error) { logger.warn('Failed to clean up temporary patch file', { tempPatchFile, error }); } } } catch (error) { logger.error('Failed to apply patch', { sessionId, filePath, error }); throw error; } } - src/types.ts:302-307 (schema)Schema definition for the "patch_apply" tool inputs.
export const PatchApplySchema = z.object({ sessionId: z.string().min(1), path: z.string().min(1), diff: z.string(), sudoPassword: z.string().optional() }); - src/mcp.ts:496-501 (registration)Tool handler registration for "patch_apply" in `src/mcp.ts`.
case 'patch_apply': { const params = PatchApplySchema.parse(args); const result = await applyPatch(params.sessionId, params.path, params.diff, params.sudoPassword); logger.info('Patch applied', { sessionId: params.sessionId, path: params.path }); return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] }; }