Skip to main content
Glama

format_layout

Apply layout controls to DOCX files by adjusting paragraph spacing, table row height, and cell padding. Google Docs supports paragraph spacing adjustments only.

Instructions

Apply layout controls (paragraph spacing, table row height, cell padding). Google Docs supports paragraph spacing only.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
file_pathNoPath to the DOCX file.
google_doc_idNoGoogle Doc ID or URL (alternative to file_path). Extract from URL: docs.google.com/document/d/{ID}/edit
strictNo
paragraph_spacingNo
row_heightNo
cell_paddingNo

Implementation Reference

  • The main handler function for the `format_layout` tool, which manages session resolution, validates layout parameters (paragraph spacing, row heights, cell padding), performs preflight checks if in strict mode, and executes the layout mutations on the document.
    export async function formatLayout(
      manager: SessionManager,
      params: FormatLayoutParams,
    ): Promise<ToolResponse> {
      try {
        const resolved = await resolveSessionForTool(manager, params, { toolName: 'format_layout' });
        if (!resolved.ok) return resolved.response;
        const { session, metadata } = resolved;
    
        const strict = params.strict ?? true;
        if (typeof strict !== 'boolean') {
          return err('VALIDATION_ERROR', 'strict must be a boolean.', 'Set strict to true or false.');
        }
    
        const paragraphSpacing = parseParagraphSpacingMutation(params.paragraph_spacing);
        if (isValidationError(paragraphSpacing)) {
          return err('VALIDATION_ERROR', paragraphSpacing.message, paragraphSpacing.hint);
        }
    
        const rowHeight = parseRowHeightMutation(params.row_height);
        if (isValidationError(rowHeight)) {
          return err('VALIDATION_ERROR', rowHeight.message, rowHeight.hint);
        }
    
        const cellPadding = parseCellPaddingMutation(params.cell_padding);
        if (isValidationError(cellPadding)) {
          return err('VALIDATION_ERROR', cellPadding.message, cellPadding.hint);
        }
    
        if (!paragraphSpacing && !rowHeight && !cellPadding) {
          return err(
            'VALIDATION_ERROR',
            'No layout operation was provided.',
            'Provide at least one of paragraph_spacing, row_height, or cell_padding.',
          );
        }
    
        if (paragraphSpacing && strict) {
          const missing = paragraphSpacing.paragraphIds.filter((id) => session.doc.getParagraphElementById(id) === null);
          if (missing.length > 0) {
            return err(
              'INVALID_SELECTOR',
              `paragraph_spacing references missing paragraph IDs: ${missing.join(', ')}`,
              'Set strict=false to ignore missing selectors and apply best-effort changes.',
            );
          }
        }
    
        if (strict && (rowHeight || cellPadding)) {
          // Preflight selectors against a cloned document so strict-mode failures
          // do not partially mutate the active session document.
          const snapshot = await session.doc.toBuffer({ cleanBookmarks: false });
          const previewDoc = await DocxDocument.load(snapshot.buffer);
    
          if (rowHeight) {
            const rowPreview = previewDoc.setTableRowHeight(rowHeight);
            if (rowPreview.missingTableIndexes.length > 0) {
              return err(
                'INVALID_SELECTOR',
                `row_height missing table indexes: ${rowPreview.missingTableIndexes.join(', ')}`,
                'Set strict=false to allow best-effort application.',
              );
            }
            if (rowPreview.missingRowIndexes.length > 0) {
              return err(
                'INVALID_SELECTOR',
                `row_height missing row indexes: ${rowPreview.missingRowIndexes
                  .map((x) => `${x.tableIndex}:${x.rowIndex}`)
                  .join(', ')}`,
                'Set strict=false to allow best-effort application.',
              );
            }
          }
    
          if (cellPadding) {
            const cellPreview = previewDoc.setTableCellPadding(cellPadding);
            if (cellPreview.missingTableIndexes.length > 0) {
              return err(
                'INVALID_SELECTOR',
                `cell_padding missing table indexes: ${cellPreview.missingTableIndexes.join(', ')}`,
                'Set strict=false to allow best-effort application.',
              );
            }
            if (cellPreview.missingRowIndexes.length > 0) {
              return err(
                'INVALID_SELECTOR',
                `cell_padding missing row indexes: ${cellPreview.missingRowIndexes
                  .map((x) => `${x.tableIndex}:${x.rowIndex}`)
                  .join(', ')}`,
                'Set strict=false to allow best-effort application.',
              );
            }
            if (cellPreview.missingCellIndexes.length > 0) {
              return err(
                'INVALID_SELECTOR',
                `cell_padding missing cell indexes: ${cellPreview.missingCellIndexes
                  .map((x) => `${x.tableIndex}:${x.rowIndex}:${x.cellIndex}`)
                  .join(', ')}`,
                'Set strict=false to allow best-effort application.',
              );
            }
          }
        }
    
        const paragraphCountBefore = session.doc.getParagraphs().length;
        const warnings: string[] = [];
    
        const paragraphSpacingResult = paragraphSpacing ? session.doc.setParagraphSpacing(paragraphSpacing) : null;
        const rowHeightResult = rowHeight ? session.doc.setTableRowHeight(rowHeight) : null;
        const cellPaddingResult = cellPadding ? session.doc.setTableCellPadding(cellPadding) : null;
    
        const paragraphCountAfter = session.doc.getParagraphs().length;
        if (paragraphCountAfter !== paragraphCountBefore) {
          return err(
            'INVARIANT_VIOLATION',
            `Layout formatting changed paragraph count (${paragraphCountBefore} -> ${paragraphCountAfter}).`,
            'Layout operations must only mutate OOXML spacing/geometry and must not insert spacer paragraphs.',
          );
        }
    
        if (paragraphSpacingResult && paragraphSpacingResult.missingParagraphIds.length > 0) {
          const message = `paragraph_spacing skipped missing paragraph IDs: ${paragraphSpacingResult.missingParagraphIds.join(', ')}`;
          if (strict) {
            return err('INVALID_SELECTOR', message, 'Set strict=false to allow best-effort application.');
          }
          warnings.push(message);
        }
        if (rowHeightResult) {
          if (rowHeightResult.missingTableIndexes.length > 0) {
            const message = `row_height missing table indexes: ${rowHeightResult.missingTableIndexes.join(', ')}`;
            if (strict) return err('INVALID_SELECTOR', message, 'Set strict=false to allow best-effort application.');
            warnings.push(message);
          }
          if (rowHeightResult.missingRowIndexes.length > 0) {
            const message = `row_height missing row indexes: ${rowHeightResult.missingRowIndexes
              .map((x) => `${x.tableIndex}:${x.rowIndex}`)
              .join(', ')}`;
            if (strict) return err('INVALID_SELECTOR', message, 'Set strict=false to allow best-effort application.');
            warnings.push(message);
          }
        }
        if (cellPaddingResult) {
          if (cellPaddingResult.missingTableIndexes.length > 0) {
            const message = `cell_padding missing table indexes: ${cellPaddingResult.missingTableIndexes.join(', ')}`;
            if (strict) return err('INVALID_SELECTOR', message, 'Set strict=false to allow best-effort application.');
            warnings.push(message);
          }
          if (cellPaddingResult.missingRowIndexes.length > 0) {
            const message = `cell_padding missing row indexes: ${cellPaddingResult.missingRowIndexes
              .map((x) => `${x.tableIndex}:${x.rowIndex}`)
              .join(', ')}`;
            if (strict) return err('INVALID_SELECTOR', message, 'Set strict=false to allow best-effort application.');
            warnings.push(message);
          }
          if (cellPaddingResult.missingCellIndexes.length > 0) {
            const message = `cell_padding missing cell indexes: ${cellPaddingResult.missingCellIndexes
              .map((x) => `${x.tableIndex}:${x.rowIndex}:${x.cellIndex}`)
              .join(', ')}`;
            if (strict) return err('INVALID_SELECTOR', message, 'Set strict=false to allow best-effort application.');
            warnings.push(message);
          }
        }
    
        const affectedParagraphs = paragraphSpacingResult?.affectedParagraphs ?? 0;
        const affectedRows = rowHeightResult?.affectedRows ?? 0;
        const affectedCells = cellPaddingResult?.affectedCells ?? 0;
        const totalAffected = affectedParagraphs + affectedRows + affectedCells;
        if (totalAffected > 0) {
          manager.markEdited(session);
        }
        manager.touch(session);
    
        return ok(mergeSessionResolutionMetadata({
          file_path: manager.normalizePath(session.originalPath),
          strict,
          mutation_summary: {
            affected_paragraphs: affectedParagraphs,
            affected_rows: affectedRows,
            affected_cells: affectedCells,
          },
          paragraph_spacing_result: paragraphSpacingResult ?? undefined,
          row_height_result: rowHeightResult ?? undefined,
          cell_padding_result: cellPaddingResult ?? undefined,
          no_spacer_paragraphs: paragraphCountBefore === paragraphCountAfter,
          paragraph_count_before: paragraphCountBefore,
          paragraph_count_after: paragraphCountAfter,
          warnings,
          message: totalAffected > 0
            ? 'Layout formatting applied with deterministic OOXML geometry mutations.'
            : 'No document nodes matched the provided selectors.',
        }, metadata));
      } catch (e: unknown) {
        const message = errorMessage(e);
        return err('FORMAT_LAYOUT_ERROR', `Failed to apply layout formatting: ${message}`, 'Check selector inputs and retry.');
      }
    }
  • Input parameter type definition for the `format_layout` tool, defining the structure of expected mutations.
    type FormatLayoutParams = {
      file_path?: string;
      strict?: boolean;
      paragraph_spacing?: ParagraphSpacingInput;
      row_height?: RowHeightInput;
      cell_padding?: CellPaddingInput;
    };
  • Registration and dispatch logic for the `format_layout` tool within the MCP server, routing requests between generic docx and Google Docs implementations.
    case 'format_layout':
      if (isGDocsRequest(args)) return await dispatchGDocs(sessions, args, 'format_layout');
      return await formatLayout(sessions, args as Parameters<typeof formatLayout>[1]);

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/UseJunior/safe-docx'

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