Skip to main content
Glama

Add Chapter

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}`,
Behavior4/5

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

With no annotations provided, the description carries full burden and does well by disclosing key behavioral traits: maximum 50 chapters per document constraint, positioning options with default value, and return format structure. It also mentions 'Maintains document flow' as a behavioral characteristic. The only gap is lack of explicit mention about whether this is a write/mutation operation.

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

Conciseness3/5

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

The description is well-structured with clear sections but contains some redundancy. The 'Position options' line repeats what's in the schema enum, and the 7-point 'You should' list includes some generic advice ('Include practical, actionable content') that doesn't add tool-specific value. However, it's front-loaded with the core purpose.

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 mutation tool with no annotations and no output schema, the description does well by explaining the return format, constraints (max 50 chapters), and behavioral context. It could be more complete by explicitly stating this is a write operation and mentioning potential error conditions beyond what's implied in the return format.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so baseline is 3. The description adds meaningful context beyond the schema: explains the purpose of 'reference_chapter' for precise placement, clarifies that position options are for 'before/after/end' (though schema already has enum), and provides guidance on how to use parameters effectively (e.g., 'Choose clear, descriptive chapter titles').

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 specific action ('Add a new chapter to an existing knowledge document') and distinguishes it from siblings like 'create_knowledge_file' (creates new file) and 'update_chapter' (modifies existing chapter). It includes the key differentiator of 'positioning control' which is not present in other 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 'When to use this tool' with 5 specific use cases and 'DO NOT use when' with 3 clear exclusions. It also implicitly distinguishes from alternatives like 'update_chapter' (for modifying existing chapters) and 'create_knowledge_file' (for creating new documents).

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

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

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