Skip to main content
Glama

add_chapter

Insert new chapters into knowledge documents with precise positioning. Use this tool to expand documentation, add examples, or include reference material while maintaining logical flow.

Instructions

Add a new chapter to an existing knowledge document with positioning control.

When to use this tool:

  • Expanding document with new topics

  • Adding examples or case studies

  • Including additional reference material

  • Inserting clarifying chapters

  • Growing documentation organically

Key features:

  • Flexible positioning (before/after/end)

  • Maintains document flow

  • Maximum 50 chapters per document

  • Reference-based positioning

You should:

  1. Choose clear, descriptive chapter titles

  2. Position chapter logically in document flow

  3. Keep chapters focused on single topics

  4. Use reference_chapter for precise placement

  5. Consider reader's journey through document

  6. Check current chapter count (max 50)

  7. Include practical, actionable content

DO NOT use when:

  • Chapter already exists

  • Document has 50 chapters already

  • Content belongs in existing chapter

Position options: "before", "after", "end" (default) Returns: {success: bool, message?: str, error?: str}

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
chapter_titleYesTitle for the new chapter
contentYesContent for the new chapter
filenameYesKnowledge file name (must include .md extension)
positionNoWhere to insert the chapter (default: "end")
project_idYesThe project identifier
reference_chapterNoThe chapter title to use as reference point for before/after positioning

Implementation Reference

  • Core handler function implementing add_chapter tool: validates params, reads/parses knowledge document, inserts new chapter at specified position (end/before/after reference), updates metadata, writes document, auto-commits to git, returns success/error response.
    async addChapterAsync(params: { project_id: z.infer<typeof secureProjectIdSchema>; filename: z.infer<typeof secureFilenameSchema>; chapter_title: z.infer<typeof secureChapterTitleSchema>; content: z.infer<typeof secureChapterContentSchema>; position?: z.infer<typeof sectionPositionSchema>; reference_chapter?: z.infer<typeof secureChapterTitleSchema>; }): Promise<string> { const context = this.createContext('add_chapter', params); try { const { project_id, filename, chapter_title, content, position = 'end', reference_chapter, } = params; const projectInfo = await getProjectDirectoryAsync(this.storagePath, project_id); // Project doesn't exist - should not create ghost entries if (!projectInfo) { throw new MCPError(MCPErrorCode.PROJECT_NOT_FOUND, `Project ${project_id} not found`, { project_id, filename, chapter_title, traceId: context.traceId, }); } const [originalId, projectPath] = projectInfo; const knowledgePath = join(projectPath, 'knowledge'); const filePath = join(knowledgePath, filename); // Check if file exists try { await access(filePath); } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') { throw new MCPError( MCPErrorCode.DOCUMENT_NOT_FOUND, `Knowledge file ${filename} not found in project ${originalId}`, { project_id, filename, traceId: context.traceId } ); } throw error; } // Read and parse the document const fileContent = await readFile(filePath, 'utf8'); const [metadata, body] = parseDocument(fileContent); const chapters = parseChapters(body); // Check if chapter already exists for (const chapter of chapters) { if (chapter.title === chapter_title) { throw new MCPError( MCPErrorCode.FILE_ALREADY_EXISTS, `Chapter "${chapter_title}" already exists in ${filename}`, { project_id, filename, chapter_title, traceId: context.traceId } ); } } // Extract introduction (content before first chapter) const firstChapterIndex = body.indexOf('\n##'); const introduction = firstChapterIndex > 0 ? body.slice(0, firstChapterIndex).trim() : ''; // Create new chapter const newChapter: Chapter = { title: chapter_title, level: 2, content: `## ${chapter_title}\n\n${content}`, summary: content.split('\n')[0].slice(0, 100) + '...', }; let updatedChapters: Chapter[]; if (position === 'end') { // Add at the end updatedChapters = [...chapters, newChapter]; } else if ((position === 'before' || position === 'after') && reference_chapter) { // Find reference chapter const refIndex = chapters.findIndex((ch) => ch.title === reference_chapter); if (refIndex === -1) { throw new MCPError( MCPErrorCode.CHAPTER_NOT_FOUND, `Reference chapter "${reference_chapter}" not found in ${filename}`, { project_id, filename, reference_chapter, traceId: context.traceId } ); } if (position === 'before') { // Insert before reference chapter updatedChapters = [ ...chapters.slice(0, refIndex), newChapter, ...chapters.slice(refIndex), ]; } else { // Insert after reference chapter updatedChapters = [ ...chapters.slice(0, refIndex + 1), newChapter, ...chapters.slice(refIndex + 1), ]; } } else { throw new MCPError( MCPErrorCode.INVALID_INPUT, 'Position "before" or "after" requires reference_chapter', { project_id, position, traceId: context.traceId } ); } // Update metadata metadata.updated = new Date().toISOString(); // Build document content const documentContent = [introduction, '', ...updatedChapters.map((ch) => ch.content)].join( '\n\n' ); // Write updated document const validatedPath = await validatePathAsync(knowledgePath, filename); await writeDocumentAsync(validatedPath, metadata, documentContent); // Auto-commit await autoCommitAsync(this.storagePath, `Add chapter "${chapter_title}" to ${filename}`); this.logSuccess('add_chapter', { project_id, filename, chapter_title }, context); return this.formatSuccessResponse({ message: `Chapter "${chapter_title}" added to ${filename}`, }); } catch (error) { const mcpError = error instanceof MCPError ? error : new MCPError( MCPErrorCode.FILE_SYSTEM_ERROR, `Failed to add chapter: ${error instanceof Error ? error.message : String(error)}`, { project_id: params.project_id, filename: params.filename, chapter_title: params.chapter_title, traceId: context.traceId, } ); this.logError( 'add_chapter', { project_id: params.project_id, filename: params.filename, chapter_title: params.chapter_title, }, mcpError, context ); return this.formatErrorResponse(mcpError, context); }
  • Registers the 'add_chapter' tool with MCP server, defines input schema using zod schemas, description from TOOL_DESCRIPTIONS, and handler invocation via chapterHandler.addChapterAsync.
    server.registerTool( 'add_chapter', { title: 'Add Chapter', description: TOOL_DESCRIPTIONS.add_chapter, inputSchema: { project_id: secureProjectIdSchema.describe('The project identifier'), filename: secureFilenameSchema.describe('Knowledge file name (must include .md extension)'), chapter_title: secureChapterTitleSchema.describe('Title for the new chapter'), content: secureChapterContentSchema.describe('Content for the new chapter'), position: sectionPositionSchema .optional() .describe('Where to insert the chapter (default: "end")'), reference_chapter: secureChapterTitleSchema .optional() .describe('The chapter title to use as reference point for before/after positioning'), }, }, async ({ project_id, filename, chapter_title, content, position, reference_chapter }) => { const result = await chapterHandler.addChapterAsync({ project_id, filename, chapter_title, content, position, reference_chapter, }); return { content: [ { type: 'text', text: result, }, ], }; }
  • Zod input schema definition for add_chapter tool parameters including project_id, filename, chapter_title, content, optional position and reference_chapter.
    inputSchema: { project_id: secureProjectIdSchema.describe('The project identifier'), filename: secureFilenameSchema.describe('Knowledge file name (must include .md extension)'), chapter_title: secureChapterTitleSchema.describe('Title for the new chapter'), content: secureChapterContentSchema.describe('Content for the new chapter'), position: sectionPositionSchema .optional() .describe('Where to insert the chapter (default: "end")'), reference_chapter: secureChapterTitleSchema .optional() .describe('The chapter title to use as reference point for before/after positioning'), },
  • Synchronous counterpart to addChapterAsync with identical logic for non-async contexts.
    addChapter(params: { project_id: z.infer<typeof secureProjectIdSchema>; filename: z.infer<typeof secureFilenameSchema>; chapter_title: z.infer<typeof secureChapterTitleSchema>; content: z.infer<typeof secureChapterContentSchema>; position?: z.infer<typeof sectionPositionSchema>; reference_chapter?: z.infer<typeof secureChapterTitleSchema>; }): string { const context = this.createContext('add_chapter', params); try { const { project_id, filename, chapter_title, content, position = 'end', reference_chapter, } = params; const projectInfo = getProjectDirectory(this.storagePath, project_id); // Project doesn't exist - should not create ghost entries if (!projectInfo) { throw new MCPError(MCPErrorCode.PROJECT_NOT_FOUND, `Project ${project_id} not found`, { project_id, filename, chapter_title, traceId: context.traceId, }); } const [originalId, projectPath] = projectInfo; const knowledgePath = join(projectPath, 'knowledge'); const filePath = join(knowledgePath, filename); // Check if file exists if (!existsSync(filePath)) { throw new MCPError( MCPErrorCode.DOCUMENT_NOT_FOUND, `Knowledge file ${filename} not found in project ${originalId}`, { project_id, filename, traceId: context.traceId } ); } // Read and parse the document const fileContent = readFileSync(filePath, 'utf8'); const [metadata, body] = parseDocument(fileContent); const chapters = parseChapters(body); // Check if chapter already exists for (const chapter of chapters) { if (chapter.title === chapter_title) { throw new MCPError( MCPErrorCode.FILE_ALREADY_EXISTS, `Chapter "${chapter_title}" already exists in ${filename}`, { project_id, filename, chapter_title, traceId: context.traceId } ); } } // Extract introduction (content before first chapter) const firstChapterIndex = body.indexOf('\n##'); const introduction = firstChapterIndex > 0 ? body.slice(0, firstChapterIndex).trim() : ''; // Create new chapter const newChapter: Chapter = { title: chapter_title, level: 2, content: `## ${chapter_title}\n\n${content}`, summary: content.split('\n')[0].slice(0, 100) + '...', }; let updatedChapters: Chapter[]; if (position === 'end') { // Add at the end updatedChapters = [...chapters, newChapter]; } else if ((position === 'before' || position === 'after') && reference_chapter) { // Find reference chapter const refIndex = chapters.findIndex((ch) => ch.title === reference_chapter); if (refIndex === -1) { throw new MCPError( MCPErrorCode.CHAPTER_NOT_FOUND, `Reference chapter "${reference_chapter}" not found in ${filename}`, { project_id, filename, reference_chapter, traceId: context.traceId } ); } if (position === 'before') { // Insert before reference chapter updatedChapters = [ ...chapters.slice(0, refIndex), newChapter, ...chapters.slice(refIndex), ]; } else { // Insert after reference chapter updatedChapters = [ ...chapters.slice(0, refIndex + 1), newChapter, ...chapters.slice(refIndex + 1), ]; } } else { throw new MCPError( MCPErrorCode.INVALID_INPUT, 'Position "before" or "after" requires reference_chapter', { project_id, position, traceId: context.traceId } ); } // Update metadata metadata.updated = new Date().toISOString(); // Build document content const documentContent = [introduction, '', ...updatedChapters.map((ch) => ch.content)].join( '\n\n' ); // Write updated document const validatedPath = validatePath(knowledgePath, filename); writeDocument(validatedPath, metadata, documentContent); // Auto-commit autoCommit(this.storagePath, `Add chapter "${chapter_title}" to ${filename}`); this.logSuccess('add_chapter', { project_id, filename, chapter_title }, context); return this.formatSuccessResponse({ message: `Chapter "${chapter_title}" added to ${filename}`, }); } catch (error) { const mcpError = error instanceof MCPError ? error : new MCPError( MCPErrorCode.FILE_SYSTEM_ERROR, `Failed to add chapter: ${error instanceof Error ? error.message : String(error)}`, { project_id: params.project_id, filename: params.filename, chapter_title: params.chapter_title, traceId: context.traceId, } ); this.logError( 'add_chapter', { project_id: params.project_id, filename: params.filename, chapter_title: params.chapter_title, }, mcpError, context ); return this.formatErrorResponse(mcpError, context); }
  • Detailed tool description and usage guidelines for add_chapter, used in registration.
    add_chapter: `Add a new chapter to an existing knowledge document with positioning control. When to use this tool: - Expanding document with new topics - Adding examples or case studies - Including additional reference material - Inserting clarifying chapters - Growing documentation organically Key features: - Flexible positioning (before/after/end) - Maintains document flow - Maximum 50 chapters per document - Reference-based positioning You should: 1. Choose clear, descriptive chapter titles 2. Position chapter logically in document flow 3. Keep chapters focused on single topics 4. Use reference_chapter for precise placement 5. Consider reader's journey through document 6. Check current chapter count (max 50) 7. Include practical, actionable content DO NOT use when: - Chapter already exists - Document has 50 chapters already - Content belongs in existing chapter Position options: "before", "after", "end" (default) Returns: {success: bool, message?: str, error?: str}`,

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/sven-borkert/knowledge-mcp'

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