Skip to main content
Glama

write_file

Write content to a specified file on the Filesystem MCP Server. Creates or overwrites the file and necessary directories based on provided relative or absolute paths.

Instructions

Writes content to a specified file. Creates the file (and necessary directories) if it doesn't exist, or overwrites it if it does. Accepts relative or absolute paths (resolved like readFile).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
contentYesThe content to write to the file. If the file exists, it will be overwritten.
pathYesThe path to the file to write. Can be relative or absolute. If relative, it resolves against the path set by `set_filesystem_default`. If absolute, it is used directly. Missing directories will be created.

Implementation Reference

  • Core handler function that executes the write_file tool logic: resolves the input path using serverState.resolvePath, checks if it's not a directory, creates parent directories if needed, writes the content using fs.promises.writeFile, logs progress, handles errors with McpError, and returns success details including bytes written.
    export const writeFileLogic = async (input: WriteFileInput, context: RequestContext): Promise<WriteFileOutput> => {
      const { path: requestedPath, content } = input;
      logger.debug(`writeFileLogic: Received request for path "${requestedPath}"`, context);
    
      // Resolve the path using serverState (handles relative/absolute logic and sanitization)
      const absolutePath = serverState.resolvePath(requestedPath, context);
      logger.debug(`writeFileLogic: Resolved path to "${absolutePath}"`, { ...context, requestedPath });
    
      try {
        // Ensure the target path is not a directory before attempting to write
        try {
          const stats = await fs.stat(absolutePath);
          if (stats.isDirectory()) {
            logger.warning(`writeFileLogic: Attempted to write to a directory path "${absolutePath}"`, { ...context, requestedPath });
            throw new McpError(BaseErrorCode.VALIDATION_ERROR, `Cannot write file. Path exists and is a directory: ${absolutePath}`, { ...context, requestedPath, resolvedPath: absolutePath });
          }
        } catch (statError: any) {
          // ENOENT (file/dir doesn't exist) is expected and okay, we'll create it.
          // Other errors during stat (like permission issues) should be thrown.
          if (statError.code !== 'ENOENT') {
            throw statError; // Re-throw other stat errors
          }
          // If ENOENT, proceed to create directory and file
          logger.debug(`writeFileLogic: Path "${absolutePath}" does not exist, will create.`, { ...context, requestedPath });
        }
    
        // Ensure the directory exists before writing the file
        const dirName = path.dirname(absolutePath);
        logger.debug(`writeFileLogic: Ensuring directory "${dirName}" exists`, { ...context, requestedPath, resolvedPath: absolutePath });
        await fs.mkdir(dirName, { recursive: true });
        logger.debug(`writeFileLogic: Directory "${dirName}" confirmed/created`, { ...context, requestedPath, resolvedPath: absolutePath });
    
        // Write the file content
        logger.debug(`writeFileLogic: Writing content to "${absolutePath}"`, { ...context, requestedPath });
        await fs.writeFile(absolutePath, content, 'utf8');
        const bytesWritten = Buffer.byteLength(content, 'utf8');
        logger.info(`writeFileLogic: Successfully wrote ${bytesWritten} bytes to "${absolutePath}"`, { ...context, requestedPath });
    
        return {
          message: `Successfully wrote content to ${absolutePath}`,
          writtenPath: absolutePath,
          bytesWritten: bytesWritten,
        };
      } catch (error: any) {
         logger.error(`writeFileLogic: Error writing file to "${absolutePath}"`, { ...context, requestedPath, error: error.message, code: error.code });
        // Handle specific file system errors
        if (error instanceof McpError) {
            throw error; // Re-throw McpErrors (like the directory check)
        }
        // Handle potential I/O errors during mkdir or writeFile
        throw new McpError(BaseErrorCode.INTERNAL_ERROR, `Failed to write file: ${error.message || 'Unknown I/O error'}`, { ...context, requestedPath, resolvedPath: absolutePath, originalError: error });
      }
    };
  • Zod schema defining the input parameters for the write_file tool: 'path' (string, required) and 'content' (string).
    export const WriteFileInputSchema = z.object({
      path: z.string().min(1, 'Path cannot be empty')
        .describe('The path to the file to write. Can be relative or absolute. If relative, it resolves against the path set by `set_filesystem_default`. If absolute, it is used directly. Missing directories will be created.'),
      content: z.string() // Allow empty content
        .describe('The content to write to the file. If the file exists, it will be overwritten.'),
    });
  • Function that registers the 'write_file' tool with the MCP server using server.tool(), providing tool name, description, input schema, and an inline handler that calls the core writeFileLogic function, handles logging and error wrapping.
    export const registerWriteFileTool = async (server: McpServer): Promise<void> => {
      const registrationContext = requestContextService.createRequestContext({ operation: 'RegisterWriteFileTool' });
      logger.info("Attempting to register 'write_file' tool", registrationContext);
    
      await ErrorHandler.tryCatch(
        async () => {
          server.tool(
            'write_file', // Tool name
            'Writes content to a specified file. Creates the file (and necessary directories) if it doesn\'t exist, or overwrites it if it does. Accepts relative or absolute paths (resolved like readFile).', // Description
            WriteFileInputSchema.shape, // Pass the schema shape
            async (params, extra) => {
              const typedParams = params as WriteFileInput;
              const callContext = requestContextService.createRequestContext({ operation: 'WriteFileToolExecution', parentId: registrationContext.requestId });
              logger.info(`Executing 'write_file' tool for path: ${typedParams.path}`, callContext);
    
              // ErrorHandler will catch McpErrors thrown by the logic
              const result = await ErrorHandler.tryCatch(
                () => writeFileLogic(typedParams, callContext),
                {
                  operation: 'writeFileLogic',
                  context: callContext,
                  input: { path: typedParams.path, content: '[CONTENT REDACTED]' }, // Redact content for logging
                  errorCode: BaseErrorCode.INTERNAL_ERROR
                }
              );
    
              logger.info(`Successfully executed 'write_file' for path: ${result.writtenPath}`, callContext);
    
              // Format the successful response
              return {
                content: [{ type: 'text', text: result.message }],
              };
            }
          );
          logger.info("'write_file' tool registered successfully", registrationContext);
        },
        {
          operation: 'registerWriteFileTool',
          context: registrationContext,
          errorCode: BaseErrorCode.CONFIGURATION_ERROR,
          critical: true
        }
      );
    };
  • Top-level call to registerWriteFileTool(server) as part of the array of all filesystem tool registrations during server initialization in createMcpServerInstance.
    const registrationPromises = [
      registerReadFileTool(server),
      registerSetFilesystemDefaultTool(server),
      registerWriteFileTool(server),
      registerUpdateFileTool(server),
      registerListFilesTool(server),
      registerDeleteFileTool(server),
      registerDeleteDirectoryTool(server),
      registerCreateDirectoryTool(server),
      registerMovePathTool(server),
      registerCopyPathTool(server)
    ];
    
    await Promise.all(registrationPromises);
    logger.info("Filesystem tools registered successfully", context);
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 discloses key behaviors: it creates files/directories if missing, overwrites existing files, and resolves paths similarly to readFile. It does not mention permissions, error handling, or rate limits, but covers core mutation traits adequately.

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 essential behavioral details in two concise sentences. Every sentence adds value without redundancy, making it efficient and well-structured.

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?

For a mutation tool with no annotations and no output schema, the description provides good context on behavior and parameters. It could be more complete by mentioning error cases or return values, but it adequately covers the tool's functionality given the structured data available.

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

Parameters3/5

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

Schema description coverage is 100%, so the schema fully documents both parameters. The description adds minimal value beyond the schema, only reiterating overwrite behavior for 'content' and path resolution for 'path', aligning with the baseline for high schema coverage.

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 verb 'writes' and the resource 'content to a specified file', specifying both creation and overwrite behaviors. It distinguishes from siblings like read_file (read-only), update_file (partial updates), and delete_file (removal).

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

Usage Guidelines4/5

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

The description implies usage for writing or creating files, with context on path resolution and directory creation. However, it lacks explicit guidance on when to use this versus alternatives like update_file (for partial updates) or create_directory (for directories only), and no exclusions are stated.

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

Related 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/cyanheads/filesystem-mcp-server'

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