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