Skip to main content
Glama

vm_file_write

Write content to files on remote virtual machines using SSH. This tool enables secure file management on Incus VMs by piping content through SSH stdin.

Instructions

Write content to a file on a remote host via SSH.

Handles arbitrary content safely by piping through SSH stdin.

Args:
    vm: Name of the host (as configured in hosts.toml).
    path: Absolute path where the file should be written.
    content: The content to write to the file.

Returns:
    Success confirmation or error message.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
vmYes
pathYes
contentYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • Core implementation of vm_file_write that writes content to a remote file via SSH using tee for safe content transfer without shell escaping issues.
    async def vm_file_write(vm: str, path: str, content: str) -> ExecResult:
        """Write content to a file on a remote host.
    
        Pipes content via stdin to `ssh user@host tee <path>` for safe transfer
        of arbitrary content (no shell escaping issues).
    
        Args:
            vm: Name of the host (as configured in hosts.toml).
            path: Absolute path where the file should be written.
            content: The content to write.
    
        Returns:
            ExecResult with operation details.
        """
        host = _resolve_host(vm)
        _validate_path(path)
    
        # Use ssh with stdin piped to tee — handles arbitrary content safely
        # Redirect tee's stdout to /dev/null so we don't echo the content back
        result = await _run_ssh(
            host,
            ["tee", path],
            stdin_data=content.encode("utf-8"),
        )
        # tee echoes stdin to stdout, so clear that from the result
        return ExecResult(
            stdout="",
            stderr=result.stderr,
            exit_code=result.exit_code,
        )
  • MCP tool registration using @mcp.tool() decorator that wraps the transport implementation and returns a user-friendly success/error message.
    @mcp.tool()
    async def vm_file_write(vm: str, path: str, content: str) -> str:
        """Write content to a file on a remote host via SSH.
    
        Handles arbitrary content safely by piping through SSH stdin.
    
        Args:
            vm: Name of the host (as configured in hosts.toml).
            path: Absolute path where the file should be written.
            content: The content to write to the file.
    
        Returns:
            Success confirmation or error message.
        """
        try:
            result = await _vm_file_write(vm, path, content)
            if result.exit_code == 0:
                return f"Successfully wrote {len(content)} bytes to {vm}:{path}"
            return f"ERROR writing file: {result.stderr.strip()}"
        except (ValueError, KeyError, RuntimeError, OSError) as e:
            return f"ERROR: {e}"
  • ExecResult class definition - the schema for command execution results containing stdout, stderr, and exit_code fields.
    class ExecResult:
        """Result of running a command on a remote host."""
    
        stdout: str
        stderr: str
        exit_code: int
  • _run_ssh helper function that executes SSH commands with optional stdin data, handling timeouts and process management.
    async def _run_ssh(
        host_config: HostConfig,
        remote_command: list[str],
        timeout: int = 120,
        stdin_data: bytes | None = None,
    ) -> ExecResult:
        """Run a command on a remote host via SSH.
    
        Args:
            host_config: SSH connection details for the target host.
            remote_command: Command and arguments to run remotely.
            timeout: Maximum seconds to wait.
            stdin_data: Optional bytes to pipe to stdin.
    
        Returns:
            ExecResult with stdout, stderr, and exit code.
    
        Raises:
            TimeoutError: If the command exceeds the timeout.
            OSError: If the ssh binary is not found.
        """
        ssh_args = host_config.ssh_args()
        full_cmd = ["ssh"] + ssh_args + remote_command
    
        proc = await asyncio.create_subprocess_exec(
            *full_cmd,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.PIPE,
            stdin=asyncio.subprocess.PIPE if stdin_data else asyncio.subprocess.DEVNULL,
        )
    
        try:
            stdout_bytes, stderr_bytes = await asyncio.wait_for(
                proc.communicate(input=stdin_data),
                timeout=timeout,
            )
        except asyncio.TimeoutError:
            proc.kill()
            await proc.wait()
            raise TimeoutError(
                f"SSH command timed out after {timeout}s on {host_config.name}"
            )
    
        return ExecResult(
            stdout=stdout_bytes.decode("utf-8", errors="replace"),
            stderr=stderr_bytes.decode("utf-8", errors="replace"),
            exit_code=proc.returncode or 0,
        )
  • _validate_path helper function that performs safety checks on file paths before writing.
    def _validate_path(path: str) -> None:
        """Validate a file path on the remote host (basic safety checks)."""
        if not path or not path.strip():
            raise ValueError("Path cannot be empty")
        if "\x00" in path:
            raise ValueError("Path cannot contain null bytes")
        if not path.startswith("/"):
            raise ValueError(f"Path must be absolute (start with /): '{path}'")
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden and does well by disclosing key behavioral traits: it's a write operation (implied mutation), handles content 'safely' via SSH stdin, and mentions error handling in returns. However, it doesn't specify permissions needed, rate limits, or whether files are overwritten, leaving some gaps.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose, followed by a concise explanation of the method, and then structured parameter and return sections. Every sentence earns its place without redundancy, making it efficient and easy to parse.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's complexity (remote file write via SSH), no annotations, and an output schema present (which covers returns), the description is largely complete. It explains the action, method, and parameters well, but could improve by mentioning side effects (e.g., file overwriting) or SSH-specific requirements.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema description coverage is 0%, so the description must compensate fully. It does so by clearly explaining all three parameters: 'vm' as the host name from configuration, 'path' as an absolute file path, and 'content' as the data to write. This adds essential meaning beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the specific action ('Write content to a file') and resource ('on a remote host via SSH'), distinguishing it from siblings like vm_file_read (read) and vm_file_push/pull (transfer). It explicitly mentions the method ('piping through SSH stdin'), which helps differentiate from other file operations.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for writing files remotely via SSH, but doesn't explicitly state when to use this vs. alternatives like vm_file_push (which might handle file transfers differently) or vm_exec (which could execute commands). It provides basic context but lacks explicit guidance on exclusions or prerequisites.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/bobbyhiddn/Sympathy-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server