> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/agentclientprotocol/typescript-sdk/llms.txt
> Use this file to discover all available pages before exploring further.

# File System Operations

> Learn how to read and write files in the client's file system

## Overview

The ACP SDK provides methods for agents to read and write files in the client's file system. These operations are only available if the client advertises the corresponding capabilities during initialization.

<Warning>
  File system operations require the client to explicitly enable these capabilities. Always check that the client advertised the capability before attempting to use these methods.
</Warning>

## Reading Files

The `readTextFile` method reads content from a text file:

```typescript theme={null}
async readTextFile(
  params: ReadTextFileRequest
): Promise<ReadTextFileResponse>
```

<ParamField path="params.sessionId" type="string" required>
  The session ID making the request
</ParamField>

<ParamField path="params.path" type="string" required>
  Absolute path to the file to read
</ParamField>

<ResponseField name="content" type="string" required>
  The file contents as a string
</ResponseField>

<Note>
  Only available if the client advertises the `fs.readTextFile` capability during initialization.
</Note>

See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)

### Example

```typescript theme={null}
class MyAgent implements acp.Agent {
  private connection: acp.AgentSideConnection;
  private clientCapabilities: acp.ClientCapabilities;

  async initialize(params: acp.InitializeRequest): Promise<acp.InitializeResponse> {
    // Store client capabilities
    this.clientCapabilities = params.clientCapabilities;

    return {
      protocolVersion: acp.PROTOCOL_VERSION,
      agentCapabilities: {},
    };
  }

  async readFile(sessionId: string, path: string): Promise<string> {
    // Check if capability is available
    if (!this.clientCapabilities.fs?.readTextFile) {
      throw new Error("Client does not support file reading");
    }

    const response = await this.connection.readTextFile({
      sessionId,
      path,
    });

    return response.content;
  }
}
```

<Note>
  All paths in the protocol should be absolute paths.
</Note>

## Writing Files

The `writeTextFile` method writes content to a text file:

```typescript theme={null}
async writeTextFile(
  params: WriteTextFileRequest
): Promise<WriteTextFileResponse>
```

<ParamField path="params.sessionId" type="string" required>
  The session ID making the request
</ParamField>

<ParamField path="params.path" type="string" required>
  Absolute path to the file to write
</ParamField>

<ParamField path="params.content" type="string" required>
  The content to write to the file
</ParamField>

<Note>
  Only available if the client advertises the `fs.writeTextFile` capability during initialization.
</Note>

Allows the agent to create or modify files within the client's environment.

See protocol docs: [Client](https://agentclientprotocol.com/protocol/overview#client)

### Example

```typescript theme={null}
async writeFile(
  sessionId: string,
  path: string,
  content: string
): Promise<void> {
  // Check if capability is available
  if (!this.clientCapabilities.fs?.writeTextFile) {
    throw new Error("Client does not support file writing");
  }

  await this.connection.writeTextFile({
    sessionId,
    path,
    content,
  });
}
```

## Error Handling

Handle errors appropriately when working with file system operations:

```typescript theme={null}
import * as acp from "@agentprotocol/acp";

async readFileWithErrorHandling(
  sessionId: string,
  path: string
): Promise<string | null> {
  try {
    const response = await this.connection.readTextFile({
      sessionId,
      path,
    });
    return response.content;
  } catch (error) {
    if (error instanceof acp.RequestError) {
      // Handle specific error codes
      if (error.code === -32002) {
        // Resource not found
        console.error(`File not found: ${path}`);
        return null;
      }
    }
    throw error;
  }
}

async writeFileWithErrorHandling(
  sessionId: string,
  path: string,
  content: string
): Promise<boolean> {
  try {
    await this.connection.writeTextFile({
      sessionId,
      path,
      content,
    });
    return true;
  } catch (error) {
    if (error instanceof acp.RequestError) {
      console.error(`Failed to write file: ${error.message}`);
      return false;
    }
    throw error;
  }
}
```

### Error Codes

<ParamField path="-32002" type="number">
  Resource not found - the file does not exist
</ParamField>

<ParamField path="-32603" type="number">
  Internal error - an error occurred while reading or writing the file
</ParamField>

## Security Considerations

<Warning>
  File system operations can be sensitive. Always follow these security best practices:
</Warning>

### Path Validation

Ensure paths are absolute and within expected boundaries:

```typescript theme={null}
import * as path from "node:path";

function validatePath(filePath: string, workingDirectory: string): boolean {
  // Ensure path is absolute
  if (!path.isAbsolute(filePath)) {
    return false;
  }

  // Ensure path is within working directory
  const normalized = path.normalize(filePath);
  const workdir = path.normalize(workingDirectory);
  
  return normalized.startsWith(workdir);
}
```

### Permission Requests

Request user permission before writing sensitive files:

```typescript theme={null}
async writeSensitiveFile(
  sessionId: string,
  filePath: string,
  content: string
): Promise<void> {
  // Send tool call for visibility
  await this.connection.sessionUpdate({
    sessionId,
    update: {
      sessionUpdate: "tool_call",
      toolCallId: "write_1",
      title: `Writing to ${path.basename(filePath)}`,
      kind: "edit",
      status: "pending",
      locations: [{ path: filePath }],
      rawInput: { path: filePath, content },
    },
  });

  // Request permission
  const permission = await this.connection.requestPermission({
    sessionId,
    toolCall: {
      toolCallId: "write_1",
      title: `Writing to ${path.basename(filePath)}`,
      kind: "edit",
      status: "pending",
      locations: [{ path: filePath }],
      rawInput: { path: filePath, content },
    },
    options: [
      { kind: "allow_once", name: "Allow", optionId: "allow" },
      { kind: "reject_once", name: "Reject", optionId: "reject" },
    ],
  });

  if (permission.outcome.optionId === "allow") {
    await this.connection.writeTextFile({ sessionId, path: filePath, content });
  }
}
```

### Capability Checking

Always verify capabilities before attempting operations:

```typescript theme={null}
class FileSystemHelper {
  constructor(
    private connection: acp.AgentSideConnection,
    private capabilities: acp.ClientCapabilities
  ) {}

  canRead(): boolean {
    return this.capabilities.fs?.readTextFile === true;
  }

  canWrite(): boolean {
    return this.capabilities.fs?.writeTextFile === true;
  }

  async read(sessionId: string, path: string): Promise<string> {
    if (!this.canRead()) {
      throw new Error("Read capability not available");
    }
    const response = await this.connection.readTextFile({ sessionId, path });
    return response.content;
  }

  async write(sessionId: string, path: string, content: string): Promise<void> {
    if (!this.canWrite()) {
      throw new Error("Write capability not available");
    }
    await this.connection.writeTextFile({ sessionId, path, content });
  }
}
```

## Complete Example

Here's a complete example that demonstrates safe file operations:

```typescript theme={null}
import * as acp from "@agentprotocol/acp";
import * as path from "node:path";

class FileOperationAgent implements acp.Agent {
  private connection: acp.AgentSideConnection;
  private capabilities: acp.ClientCapabilities | null = null;

  constructor(connection: acp.AgentSideConnection) {
    this.connection = connection;
  }

  async initialize(params: acp.InitializeRequest): Promise<acp.InitializeResponse> {
    this.capabilities = params.clientCapabilities;
    return {
      protocolVersion: acp.PROTOCOL_VERSION,
      agentCapabilities: {},
    };
  }

  async readProjectFiles(sessionId: string, directory: string): Promise<Map<string, string>> {
    if (!this.capabilities?.fs?.readTextFile) {
      throw new Error("File reading not supported");
    }

    const files = new Map<string, string>();
    const filePaths = await this.listFiles(directory);

    for (const filePath of filePaths) {
      try {
        const response = await this.connection.readTextFile({
          sessionId,
          path: filePath,
        });
        files.set(filePath, response.content);
      } catch (error) {
        console.error(`Failed to read ${filePath}:`, error);
      }
    }

    return files;
  }

  async updateFile(
    sessionId: string,
    filePath: string,
    newContent: string
  ): Promise<boolean> {
    if (!this.capabilities?.fs?.writeTextFile) {
      throw new Error("File writing not supported");
    }

    // Request permission
    const permission = await this.connection.requestPermission({
      sessionId,
      toolCall: {
        toolCallId: crypto.randomUUID(),
        title: `Update ${path.basename(filePath)}`,
        kind: "edit",
        status: "pending",
        locations: [{ path: filePath }],
        rawInput: { path: filePath, content: newContent },
      },
      options: [
        { kind: "allow_once", name: "Allow", optionId: "allow" },
        { kind: "reject_once", name: "Reject", optionId: "reject" },
      ],
    });

    if (permission.outcome.optionId !== "allow") {
      return false;
    }

    await this.connection.writeTextFile({
      sessionId,
      path: filePath,
      content: newContent,
    });

    return true;
  }

  private async listFiles(directory: string): Promise<string[]> {
    // Implementation depends on your file listing strategy
    return [];
  }

  // ... implement other required Agent methods
}
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Check Capabilities" icon="shield-check">
    Always verify the client supports file operations before attempting them
  </Card>

  <Card title="Use Absolute Paths" icon="route">
    All paths in the protocol must be absolute, not relative
  </Card>

  <Card title="Handle Errors" icon="triangle-exclamation">
    Gracefully handle file not found and permission errors
  </Card>

  <Card title="Request Permission" icon="hand">
    Request user permission before writing sensitive files
  </Card>
</CardGroup>
