update_project_section
Modify or update a specific section in a project’s main.md file without altering other parts. Use for targeted edits, error fixes, or adding new configurations, ensuring document structure and integrity are preserved.
Instructions
Update a specific section within the project main.md file efficiently.
When to use this tool:
Modifying a single section without affecting others
Adding new configuration or guidelines to existing section
Fixing errors in specific sections
Updating outdated information in targeted areas
Making incremental improvements
Key features:
Preserves all other sections intact (non-destructive)
More efficient than full file replacement
Maintains document structure
Atomic section-level updates
You should:
Identify the exact section header including "## " prefix
Read the current section content first if needed
Preserve section formatting conventions
Use this instead of update_project_main for small changes
Verify section exists before attempting update
Keep section content focused and relevant
Consider impact on related sections
DO NOT use when:
Section doesn't exist (use add_project_section)
Need to update multiple sections (batch operations)
Restructuring entire document
Section header must match exactly (e.g., "## Installation") Returns: {success: bool, message?: str, error?: str}
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| new_content | Yes | The new content for this section (without the header) | |
| project_id | Yes | The project identifier | |
| section_header | Yes | The exact section header to update (e.g., "## Installation") |
Implementation Reference
- Asynchronous implementation of the tool handler. Locates the specified section header (## ) in the project's main.md file, replaces the section content with new_content, updates the file, auto-commits the change, and returns a formatted response./** * Async version of updateProjectSection */ async updateProjectSectionAsync(params: { project_id: z.infer<typeof secureProjectIdSchema>; section_header: z.infer<typeof secureSectionHeaderSchema>; new_content: z.infer<typeof secureContentSchema>; }): Promise<string> { const context = this.createContext('update_project_section', params); try { const { project_id, section_header, new_content } = params; const projectInfo = await getProjectDirectoryAsync(this.storagePath, project_id); // Check if project exists if (!projectInfo) { throw new MCPError(MCPErrorCode.PROJECT_NOT_FOUND, `Project ${project_id} not found`, { project_id, traceId: context.traceId, }); } const [originalId, projectPath] = projectInfo; const mainFile = join(projectPath, 'main.md'); try { await access(mainFile); } catch { throw new MCPError(MCPErrorCode.PROJECT_NOT_FOUND, `Project ${originalId} does not exist`, { project_id, section_header, traceId: context.traceId, }); } const content = await readFile(mainFile, 'utf8'); const lines = content.split('\n'); // Find the section let sectionStart = -1; let sectionEnd = lines.length; for (let i = 0; i < lines.length; i++) { if (lines[i].trim() === section_header.trim()) { sectionStart = i; // Find the end of this section (next ## header or end of file) for (let j = i + 1; j < lines.length; j++) { if (lines[j].startsWith('## ')) { sectionEnd = j; break; } } break; } } if (sectionStart === -1) { throw new MCPError( MCPErrorCode.SECTION_NOT_FOUND, `Section "${section_header}" not found in project main`, { project_id, section_header, traceId: context.traceId } ); } // Replace the section content const newLines = [ ...lines.slice(0, sectionStart + 1), '', new_content, '', ...lines.slice(sectionEnd), ]; const updatedContent = newLines.join('\n'); await writeFile(mainFile, updatedContent); await autoCommitAsync( this.storagePath, `Update section "${section_header}" in ${originalId}` ); await this.logSuccessAsync('update_project_section', { project_id, section_header }, context); return this.formatSuccessResponse({ message: `Section "${section_header}" updated in project ${originalId}`, }); } catch (error) { const mcpError = error instanceof MCPError ? error : new MCPError( MCPErrorCode.INTERNAL_ERROR, `Failed to update project section: ${error instanceof Error ? error.message : String(error)}`, { project_id: params.project_id, section_header: params.section_header, traceId: context.traceId, } ); await this.logErrorAsync( 'update_project_section', { project_id: params.project_id, section_header: params.section_header, }, mcpError, context ); return this.formatErrorResponse(mcpError, context); } }
- src/knowledge-mcp/server.ts:93-123 (registration)Registers the 'update_project_section' tool with the MCP server, specifying title, description, input schema, and the handler invocation.server.registerTool( 'update_project_section', { title: 'Update Project Section', description: TOOL_DESCRIPTIONS.update_project_section, inputSchema: { project_id: secureProjectIdSchema.describe('The project identifier'), section_header: secureSectionHeaderSchema.describe( 'The exact section header to update (e.g., "## Installation")' ), new_content: secureContentSchema.describe( 'The new content for this section (without the header)' ), }, }, async ({ project_id, section_header, new_content }) => { const result = await projectHandler.updateProjectSectionAsync({ project_id, section_header, new_content, }); return { content: [ { type: 'text', text: result, }, ], }; } );
- Zod schema definitions for input parameters: secureProjectIdSchema, secureContentSchema, and secureSectionHeaderSchema used for validating project_id, new_content, and section_header.export const secureProjectIdSchema = z .string() .min(1, 'Project ID cannot be empty') .max(100, 'Project ID too long') .refine( (val) => !val.includes('..') && !val.startsWith('.') && !val.endsWith('.'), 'Project ID cannot contain path traversal patterns' ) .refine( (val) => !/[/\\:*?"<>|\0]/.test(val), 'Project ID cannot contain filesystem reserved characters or null bytes' ) .refine((val) => val.trim() === val, 'Project ID cannot have leading/trailing spaces'); export const secureFilenameSchema = z .string() .min(1, 'Filename cannot be empty') .max(255, 'Filename too long') .refine( (val) => !val.includes('/') && !val.includes('\\') && !val.includes('\0'), 'Filename contains invalid path characters' ) .refine( (val) => !val.includes('..') && val.trim() === val, 'Filename cannot contain path traversal or leading/trailing spaces' ); export const secureContentSchema = z .string() .max(10 * 1024 * 1024, 'Content too large (max 10MB)') .refine((val) => !val.includes('\0'), 'Content cannot contain null bytes'); export const secureSectionHeaderSchema = z .string() .min(1, 'Section header cannot be empty') .max(200, 'Section header too long') .regex(/^##\s+/, 'Section header must start with "## "') .refine( (val) => !val.includes('\0') && val.trim() === val, 'Section header contains invalid characters' );