can1357/oh-my-pi · error · Error

Tool "write" not available: this session cannot download res

Error message

Tool "write" not available: this session cannot download resources to disk.

What it means

Reading an MCP resource with a downloadPath requires the session to allow direct file mutation (equivalent to the 'write' tool being enabled). If the session was started without file-write permissions, the download is rejected up front rather than failing later at the write step. This is a capability gate, not a bug.

Source

Thrown at packages/coding-agent/src/cursor.ts:793

	 * workspace-relative path and answer with the path alone, so a large binary
	 * lands on disk instead of in the model's context. That makes it a workspace
	 * mutation reached without a registry tool, so it is gated exactly like the
	 * native `delete` frame — on the session actually granting a file-writing
	 * tool, and on the user's `write`-tier policy. The gate runs before the read
	 * so a refused download never fetches the resource either.
	 */
	async readMcpResource({
		server,
		uri,
		downloadPath,
	}: {
		server: string;
		uri: string;
		downloadPath?: string;
	}): Promise<CursorMcpResourceContent | null> {
		if (downloadPath) {
			if (!allowsDirectFileMutation(this.options)) {
				throw new Error('Tool "write" not available: this session cannot download resources to disk.');
			}
			const refusal = refuseByWritePolicy(this.options, "write", downloadPath);
			if (refusal) throw new Error(refusal);
		}
		const mcp = this.options.mcpResources;
		if (!mcp) return null;
		const read = await mcp.readServerResource(server, uri);
		if (!read) return null;
		// The mime type must describe the bytes actually sent, not whatever item
		// happened to be first: an image blob followed by a text note would
		// otherwise label the text `image/png` and mislead the model about what
		// it is holding. Each branch below takes the type from its own producer.
		const textItems = read.contents.filter(item => item.text !== undefined);
		const texts = textItems.map(item => item.text as string);
		const blobItem = read.contents.find(item => item.blob !== undefined);
		const blob = blobItem?.blob;
		const textMimeType = textItems[0]?.mimeType;
		const blobMimeType = blobItem?.mimeType;

View on GitHub (pinned to 9690622007)

Solutions

  1. Enable the write tool / direct file mutation in the session options, or restart the session in a mode that grants it.
  2. Omit downloadPath and consume the resource bytes in-memory instead of writing to disk.
  3. If running via the CLI, remove the permission restriction or approve write access for the session/workspace.

Example fix

// before: read-only session options
const handlers = new CursorExecHandlers({ ...options, toolPermissions: { write: false } });
// after: allow writes (or don't pass downloadPath)
const handlers = new CursorExecHandlers({ ...options, toolPermissions: { write: true } });
Defensive patterns

Strategy: try-catch

Validate before calling

if (downloadPath && !sessionAllowsDirectFileMutation) {
  // don't request a download at all; read in-memory instead
  delete request.downloadPath;
}

Try / catch

try {
  return await readMcpResource({ server, uri, downloadPath });
} catch (e) {
  if (String(e.message).includes('Tool "write" not available')) {
    return readMcpResource({ server, uri }); // fall back to in-memory content
  } else throw e;
}

Prevention

When it happens

Trigger: Calling readMcpResource (Cursor exec bridge) with downloadPath set in a session whose options disable direct file mutation — e.g. write tool disabled, sandboxed/read-only session mode, or an SDK embedding that did not grant file tools.

Common situations: Running the agent in a restricted mode (plan/read-only) and asking it to save a fetched MCP resource to disk; embedding the library with a limited tool allowlist; CI sessions without write access.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/acdeaf5f6fe49446. Report an issue: GitHub.