Skip to main content
Glama

write_resource

Edit files on the Backlog MCP Server by replacing, inserting, or appending content to manage task backlog updates.

Instructions

Edit existing files on the MCP server. All file creation goes through backlog_create.

  • The append command will add content to the end of an existing file, automatically adding a newline if the file doesn't end with one. Notes for using the str_replace command:

  • The old_str parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!

  • If the old_str parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in old_str to make it unique

  • The new_str parameter should contain the edited lines that should replace the old_str

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
uriYesMCP resource URI, e.g. mcp://backlog/path/to/file.md
operationYesOperation to apply

Implementation Reference

  • The write_resource tool handler callback that executes the write operation via resourceManager.write() and returns the result as JSON
      async ({ uri, operation }) => {
        const result = this.write(uri, operation);
        return {
          content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
        };
      }
    );
  • Core write method that resolves URIs to file paths, applies operations, updates task timestamps, and handles file operations
    write(uri: string, operation: Operation): WriteResourceResult {
      try {
        const filePath = this.resolve(uri);
        const isTask = this.isTaskUri(uri);
        
        if (!existsSync(filePath)) {
          return {
            success: false,
            message: 'File not found',
            error: `Resource not found: ${uri} (${operation.type} requires existing file). Use backlog_create to create new entities.`,
          };
        }
    
        const fileContent = readFileSync(filePath, 'utf-8');
    
        let newContent = applyOperation(fileContent, operation);
        
        // Update timestamp for task files
        if (isTask) {
          newContent = this.updateTaskTimestamp(newContent);
        }
        
        writeFileSync(filePath, newContent, 'utf-8');
    
        return {
          success: true,
          message: `Successfully applied ${operation.type} to ${uri}`,
        };
      } catch (error) {
        return {
          success: false,
          message: 'Operation failed',
          error: error instanceof Error ? error.message : String(error),
        };
      }
    }
  • Zod schema definition for write_resource tool input parameters, validating URI and operation types (str_replace, insert, append)
    inputSchema: z.object({
      uri: z.string().describe('MCP resource URI, e.g. mcp://backlog/path/to/file.md'),
      operation: z.preprocess(
        // Workaround: MCP clients stringify object params with $ref/oneOf schemas
        // https://github.com/anthropics/claude-code/issues/18260
        (val) => typeof val === 'string' ? JSON.parse(val) : val,
        z.discriminatedUnion('type', [
        z.object({
          type: z.literal('str_replace'),
          old_str: z.string().describe('String in file to replace (must match exactly)'),
          new_str: z.string().describe('New string to replace old_str with'),
        }),
        z.object({
          type: z.literal('insert'),
          insert_line: z.number().describe('Line number after which new_str will be inserted'),
          new_str: z.string().describe('String to insert'),
        }),
        z.object({
          type: z.literal('append'),
          new_str: z.string().describe('Content to append to the file'),
        }),
      ])).describe('Operation to apply'),
    }),
  • Method that registers the write_resource tool with the MCP server, including description, input schema, and handler callback
     registerWriteTool(server: McpServer) {
       server.registerTool(
         'write_resource',
         {
           description: `Edit existing files on the MCP server. All file creation goes through backlog_create.
    * The \`append\` command will add content to the end of an existing file, automatically adding a newline if the file doesn't end with one.
    Notes for using the \`str_replace\` command:
    * The \`old_str\` parameter should match EXACTLY one or more consecutive lines from the original file. Be mindful of whitespaces!
    * If the \`old_str\` parameter is not unique in the file, the replacement will not be performed. Make sure to include enough context in \`old_str\` to make it unique
    * The \`new_str\` parameter should contain the edited lines that should replace the \`old_str\``,
           inputSchema: z.object({
             uri: z.string().describe('MCP resource URI, e.g. mcp://backlog/path/to/file.md'),
             operation: z.preprocess(
               // Workaround: MCP clients stringify object params with $ref/oneOf schemas
               // https://github.com/anthropics/claude-code/issues/18260
               (val) => typeof val === 'string' ? JSON.parse(val) : val,
               z.discriminatedUnion('type', [
               z.object({
                 type: z.literal('str_replace'),
                 old_str: z.string().describe('String in file to replace (must match exactly)'),
                 new_str: z.string().describe('New string to replace old_str with'),
               }),
               z.object({
                 type: z.literal('insert'),
                 insert_line: z.number().describe('Line number after which new_str will be inserted'),
                 new_str: z.string().describe('String to insert'),
               }),
               z.object({
                 type: z.literal('append'),
                 new_str: z.string().describe('Content to append to the file'),
               }),
             ])).describe('Operation to apply'),
           }),
         },
         async ({ uri, operation }) => {
           const result = this.write(uri, operation);
           return {
             content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],
           };
         }
       );
     }
  • Helper function that applies the actual text operations (str_replace, insert, append) to file content
    export function applyOperation(content: string, operation: Operation): string {
      switch (operation.type) {
        case 'str_replace': {
          const { old_str, new_str } = operation;
          if (!content.includes(old_str)) {
            throw new Error(`str_replace failed: old_str not found in content`);
          }
          // Check uniqueness - fail if old_str appears more than once
          const firstIndex = content.indexOf(old_str);
          const secondIndex = content.indexOf(old_str, firstIndex + 1);
          if (secondIndex !== -1) {
            throw new Error(`str_replace failed: old_str is not unique in file. Include more context to make it unique.`);
          }
          return content.replace(old_str, new_str);
        }
    
        case 'insert': {
          // insert_line: insert AFTER this line (1-based, like fs_write)
          const lines = content.split('\n');
          const lineNum = operation.insert_line;
          if (lineNum < 0 || lineNum > lines.length) {
            throw new Error(`insert failed: line ${lineNum} out of range (0-${lines.length})`);
          }
          lines.splice(lineNum, 0, operation.new_str);
          return lines.join('\n');
        }
    
        case 'append': {
          // Add newline if file doesn't end with one (like fs_write)
          const needsNewline = content.length > 0 && !content.endsWith('\n');
          return content + (needsNewline ? '\n' : '') + operation.new_str;
        }
    
        default:
          throw new Error(`Unknown operation type: ${(operation as any).type}`);
      }
    }
Behavior4/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. It effectively describes key behavioral traits: the append operation's automatic newline handling, str_replace's exact matching requirement and uniqueness constraint, and the tool's destructive nature (editing files). However, it doesn't mention error conditions, permissions, or rate limits.

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

Conciseness4/5

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

The description is well-structured with clear bullet points for different operations, but could be more front-loaded. The first sentence establishes the core purpose, but the detailed operation notes could be slightly more streamlined while maintaining clarity.

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 file editing tool with no annotations and no output schema, the description provides substantial context about different operation types and their behaviors. However, it lacks information about return values, error responses, or what happens when operations fail, leaving some gaps in completeness.

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 already documents all parameters thoroughly. The description adds some semantic context about str_replace's line-based matching and uniqueness requirements, but doesn't significantly enhance understanding beyond what the schema provides. This meets the baseline expectation.

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 tool's purpose with specific verbs ('Edit existing files') and resource ('files on the MCP server'), and explicitly distinguishes it from sibling 'backlog_create' for file creation. This provides excellent differentiation from related tools.

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

Usage Guidelines5/5

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

The description provides explicit guidance on when to use this tool ('Edit existing files') versus when not to ('All file creation goes through backlog_create'). This clear boundary with a named alternative tool gives perfect usage context.

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/gkoreli/backlog-mcp'

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