diff --git a/.gitignore b/.gitignore
index 16c5d7b..62a1f92 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,9 @@ mcp_server_debug.log
 # ---- Local development files -------------------------------------------
 /.credentials
 /.claude
+.serena/
+Caddyfile
+ecosystem.config.cjs
 
 # ---- Agent instructions (not for distribution) -------------------------
 .github/instructions/
diff --git a/gdocs/docs_helpers.py b/gdocs/docs_helpers.py
index 800995c..78cd6ae 100644
--- a/gdocs/docs_helpers.py
+++ b/gdocs/docs_helpers.py
@@ -98,6 +98,86 @@ def build_text_style(
     return text_style, fields
 
 
+def build_paragraph_style(
+    heading_level: int = None,
+    alignment: str = None,
+    line_spacing: float = None,
+    indent_first_line: float = None,
+    indent_start: float = None,
+    indent_end: float = None,
+    space_above: float = None,
+    space_below: float = None,
+) -> tuple[Dict[str, Any], list[str]]:
+    """
+    Build paragraph style object for Google Docs API requests.
+
+    Args:
+        heading_level: Heading level 0-6 (0 = NORMAL_TEXT, 1-6 = HEADING_N)
+        alignment: Text alignment - 'START', 'CENTER', 'END', or 'JUSTIFIED'
+        line_spacing: Line spacing multiplier (1.0 = single, 2.0 = double)
+        indent_first_line: First line indent in points
+        indent_start: Left/start indent in points
+        indent_end: Right/end indent in points
+        space_above: Space above paragraph in points
+        space_below: Space below paragraph in points
+
+    Returns:
+        Tuple of (paragraph_style_dict, list_of_field_names)
+    """
+    paragraph_style = {}
+    fields = []
+
+    if heading_level is not None:
+        if heading_level < 0 or heading_level > 6:
+            raise ValueError("heading_level must be between 0 (normal text) and 6")
+        if heading_level == 0:
+            paragraph_style["namedStyleType"] = "NORMAL_TEXT"
+        else:
+            paragraph_style["namedStyleType"] = f"HEADING_{heading_level}"
+        fields.append("namedStyleType")
+
+    if alignment is not None:
+        valid_alignments = ["START", "CENTER", "END", "JUSTIFIED"]
+        alignment_upper = alignment.upper()
+        if alignment_upper not in valid_alignments:
+            raise ValueError(
+                f"Invalid alignment '{alignment}'. Must be one of: {valid_alignments}"
+            )
+        paragraph_style["alignment"] = alignment_upper
+        fields.append("alignment")
+
+    if line_spacing is not None:
+        if line_spacing <= 0:
+            raise ValueError("line_spacing must be positive")
+        paragraph_style["lineSpacing"] = line_spacing * 100
+        fields.append("lineSpacing")
+
+    if indent_first_line is not None:
+        paragraph_style["indentFirstLine"] = {
+            "magnitude": indent_first_line,
+            "unit": "PT",
+        }
+        fields.append("indentFirstLine")
+
+    if indent_start is not None:
+        paragraph_style["indentStart"] = {"magnitude": indent_start, "unit": "PT"}
+        fields.append("indentStart")
+
+    if indent_end is not None:
+        paragraph_style["indentEnd"] = {"magnitude": indent_end, "unit": "PT"}
+        fields.append("indentEnd")
+
+    if space_above is not None:
+        paragraph_style["spaceAbove"] = {"magnitude": space_above, "unit": "PT"}
+        fields.append("spaceAbove")
+
+    if space_below is not None:
+        paragraph_style["spaceBelow"] = {"magnitude": space_below, "unit": "PT"}
+        fields.append("spaceBelow")
+
+    return paragraph_style, fields
+
+
 def create_insert_text_request(index: int, text: str) -> Dict[str, Any]:
     """
     Create an insertText request for Google Docs API.
@@ -196,6 +276,59 @@ def create_format_text_request(
     }
 
 
+def create_update_paragraph_style_request(
+    start_index: int,
+    end_index: int,
+    heading_level: int = None,
+    alignment: str = None,
+    line_spacing: float = None,
+    indent_first_line: float = None,
+    indent_start: float = None,
+    indent_end: float = None,
+    space_above: float = None,
+    space_below: float = None,
+) -> Optional[Dict[str, Any]]:
+    """
+    Create an updateParagraphStyle request for Google Docs API.
+
+    Args:
+        start_index: Start position of paragraph range
+        end_index: End position of paragraph range
+        heading_level: Heading level 0-6 (0 = NORMAL_TEXT, 1-6 = HEADING_N)
+        alignment: Text alignment - 'START', 'CENTER', 'END', or 'JUSTIFIED'
+        line_spacing: Line spacing multiplier (1.0 = single, 2.0 = double)
+        indent_first_line: First line indent in points
+        indent_start: Left/start indent in points
+        indent_end: Right/end indent in points
+        space_above: Space above paragraph in points
+        space_below: Space below paragraph in points
+
+    Returns:
+        Dictionary representing the updateParagraphStyle request, or None if no styles provided
+    """
+    paragraph_style, fields = build_paragraph_style(
+        heading_level,
+        alignment,
+        line_spacing,
+        indent_first_line,
+        indent_start,
+        indent_end,
+        space_above,
+        space_below,
+    )
+
+    if not paragraph_style:
+        return None
+
+    return {
+        "updateParagraphStyle": {
+            "range": {"startIndex": start_index, "endIndex": end_index},
+            "paragraphStyle": paragraph_style,
+            "fields": ",".join(fields),
+        }
+    }
+
+
 def create_find_replace_request(
     find_text: str, replace_text: str, match_case: bool = False
 ) -> Dict[str, Any]:
@@ -387,6 +520,7 @@ def validate_operation(operation: Dict[str, Any]) -> tuple[bool, str]:
         "delete_text": ["start_index", "end_index"],
         "replace_text": ["start_index", "end_index", "text"],
         "format_text": ["start_index", "end_index"],
+        "update_paragraph_style": ["start_index", "end_index"],
         "insert_table": ["index", "rows", "columns"],
         "insert_page_break": ["index"],
         "find_replace": ["find_text", "replace_text"],
diff --git a/gdocs/managers/batch_operation_manager.py b/gdocs/managers/batch_operation_manager.py
index 2d69f3e..ad01d86 100644
--- a/gdocs/managers/batch_operation_manager.py
+++ b/gdocs/managers/batch_operation_manager.py
@@ -13,6 +13,7 @@ from gdocs.docs_helpers import (
     create_insert_text_request,
     create_delete_range_request,
     create_format_text_request,
+    create_update_paragraph_style_request,
     create_find_replace_request,
     create_insert_table_request,
     create_insert_page_break_request,
@@ -211,6 +212,55 @@ class BatchOperationManager:
 
             description = f"format text {op['start_index']}-{op['end_index']} ({', '.join(format_changes)})"
 
+        elif op_type == "update_paragraph_style":
+            request = create_update_paragraph_style_request(
+                op["start_index"],
+                op["end_index"],
+                op.get("heading_level"),
+                op.get("alignment"),
+                op.get("line_spacing"),
+                op.get("indent_first_line"),
+                op.get("indent_start"),
+                op.get("indent_end"),
+                op.get("space_above"),
+                op.get("space_below"),
+            )
+
+            if not request:
+                raise ValueError("No paragraph style options provided")
+
+            style_changes = []
+            for param, name in [
+                ("heading_level", "heading"),
+                ("alignment", "alignment"),
+                ("line_spacing", "line spacing"),
+                ("indent_first_line", "first line indent"),
+                ("indent_start", "start indent"),
+                ("indent_end", "end indent"),
+                ("space_above", "space above"),
+                ("space_below", "space below"),
+            ]:
+                if op.get(param) is not None:
+                    value = (
+                        f"H{op[param]}"
+                        if param == "heading_level"
+                        else f"{op[param]}x"
+                        if param == "line_spacing"
+                        else f"{op[param]}pt"
+                        if param
+                        in (
+                            "indent_first_line",
+                            "indent_start",
+                            "indent_end",
+                            "space_above",
+                            "space_below",
+                        )
+                        else op[param]
+                    )
+                    style_changes.append(f"{name}: {value}")
+
+            description = f"paragraph style {op['start_index']}-{op['end_index']} ({', '.join(style_changes)})"
+
         elif op_type == "insert_table":
             request = create_insert_table_request(
                 op["index"], op["rows"], op["columns"]
@@ -233,6 +283,7 @@ class BatchOperationManager:
                 "delete_text",
                 "replace_text",
                 "format_text",
+                "update_paragraph_style",
                 "insert_table",
                 "insert_page_break",
                 "find_replace",
@@ -318,6 +369,20 @@ class BatchOperationManager:
                     ],
                     "description": "Apply formatting to text range",
                 },
+                "update_paragraph_style": {
+                    "required": ["start_index", "end_index"],
+                    "optional": [
+                        "heading_level",
+                        "alignment",
+                        "line_spacing",
+                        "indent_first_line",
+                        "indent_start",
+                        "indent_end",
+                        "space_above",
+                        "space_below",
+                    ],
+                    "description": "Apply paragraph-level styling (headings, alignment, spacing, indentation)",
+                },
                 "insert_table": {
                     "required": ["index", "rows", "columns"],
                     "description": "Insert table at specified index",
@@ -341,5 +406,12 @@ class BatchOperationManager:
                     "bold": True,
                 },
                 {"type": "insert_table", "index": 20, "rows": 2, "columns": 3},
+                {
+                    "type": "update_paragraph_style",
+                    "start_index": 1,
+                    "end_index": 20,
+                    "heading_level": 1,
+                    "alignment": "CENTER",
+                },
             ],
         }
diff --git a/gdocs/managers/validation_manager.py b/gdocs/managers/validation_manager.py
index db31f5b..9248f70 100644
--- a/gdocs/managers/validation_manager.py
+++ b/gdocs/managers/validation_manager.py
@@ -38,6 +38,8 @@ class ValidationManager:
             "valid_section_types": ["header", "footer"],
             "valid_list_types": ["UNORDERED", "ORDERED"],
             "valid_element_types": ["table", "list", "page_break"],
+            "valid_alignments": ["START", "CENTER", "END", "JUSTIFIED"],
+            "heading_level_range": (0, 6),
         }
 
     def validate_document_id(self, document_id: str) -> Tuple[bool, str]:
@@ -240,6 +242,102 @@ class ValidationManager:
 
         return True, ""
 
+    def validate_paragraph_style_params(
+        self,
+        heading_level: Optional[int] = None,
+        alignment: Optional[str] = None,
+        line_spacing: Optional[float] = None,
+        indent_first_line: Optional[float] = None,
+        indent_start: Optional[float] = None,
+        indent_end: Optional[float] = None,
+        space_above: Optional[float] = None,
+        space_below: Optional[float] = None,
+    ) -> Tuple[bool, str]:
+        """
+        Validate paragraph style parameters.
+
+        Args:
+            heading_level: Heading level 0-6 (0 = NORMAL_TEXT, 1-6 = HEADING_N)
+            alignment: Text alignment - 'START', 'CENTER', 'END', or 'JUSTIFIED'
+            line_spacing: Line spacing multiplier (must be positive)
+            indent_first_line: First line indent in points
+            indent_start: Left/start indent in points
+            indent_end: Right/end indent in points
+            space_above: Space above paragraph in points
+            space_below: Space below paragraph in points
+
+        Returns:
+            Tuple of (is_valid, error_message)
+        """
+        style_params = [
+            heading_level,
+            alignment,
+            line_spacing,
+            indent_first_line,
+            indent_start,
+            indent_end,
+            space_above,
+            space_below,
+        ]
+        if all(param is None for param in style_params):
+            return (
+                False,
+                "At least one paragraph style parameter must be provided (heading_level, alignment, line_spacing, indent_first_line, indent_start, indent_end, space_above, or space_below)",
+            )
+
+        if heading_level is not None:
+            if not isinstance(heading_level, int):
+                return (
+                    False,
+                    f"heading_level must be an integer, got {type(heading_level).__name__}",
+                )
+            min_level, max_level = self.validation_rules["heading_level_range"]
+            if not (min_level <= heading_level <= max_level):
+                return (
+                    False,
+                    f"heading_level must be between {min_level} and {max_level}, got {heading_level}",
+                )
+
+        if alignment is not None:
+            if not isinstance(alignment, str):
+                return (
+                    False,
+                    f"alignment must be a string, got {type(alignment).__name__}",
+                )
+            valid = self.validation_rules["valid_alignments"]
+            if alignment.upper() not in valid:
+                return (
+                    False,
+                    f"alignment must be one of: {', '.join(valid)}, got '{alignment}'",
+                )
+
+        if line_spacing is not None:
+            if not isinstance(line_spacing, (int, float)):
+                return (
+                    False,
+                    f"line_spacing must be a number, got {type(line_spacing).__name__}",
+                )
+            if line_spacing <= 0:
+                return False, "line_spacing must be positive"
+
+        for param, name in [
+            (indent_first_line, "indent_first_line"),
+            (indent_start, "indent_start"),
+            (indent_end, "indent_end"),
+            (space_above, "space_above"),
+            (space_below, "space_below"),
+        ]:
+            if param is not None:
+                if not isinstance(param, (int, float)):
+                    return (
+                        False,
+                        f"{name} must be a number, got {type(param).__name__}",
+                    )
+                if name != "indent_first_line" and param < 0:
+                    return False, f"{name} must be non-negative, got {param}"
+
+        return True, ""
+
     def validate_color_param(
         self, color: Optional[str], param_name: str
     ) -> Tuple[bool, str]:
@@ -489,6 +587,32 @@ class ValidationManager:
                 if not is_valid:
                     return False, f"Operation {i + 1} (format_text): {error_msg}"
 
+            elif op_type == "update_paragraph_style":
+                is_valid, error_msg = self.validate_paragraph_style_params(
+                    op.get("heading_level"),
+                    op.get("alignment"),
+                    op.get("line_spacing"),
+                    op.get("indent_first_line"),
+                    op.get("indent_start"),
+                    op.get("indent_end"),
+                    op.get("space_above"),
+                    op.get("space_below"),
+                )
+                if not is_valid:
+                    return (
+                        False,
+                        f"Operation {i + 1} (update_paragraph_style): {error_msg}",
+                    )
+
+                is_valid, error_msg = self.validate_index_range(
+                    op["start_index"], op["end_index"]
+                )
+                if not is_valid:
+                    return (
+                        False,
+                        f"Operation {i + 1} (update_paragraph_style): {error_msg}",
+                    )
+
         return True, ""
 
     def validate_text_content(
@@ -524,7 +648,12 @@ class ValidationManager:
             "constraints": self.validation_rules.copy(),
             "supported_operations": {
                 "table_operations": ["create_table", "populate_table"],
-                "text_operations": ["insert_text", "format_text", "find_replace"],
+                "text_operations": [
+                    "insert_text",
+                    "format_text",
+                    "find_replace",
+                    "update_paragraph_style",
+                ],
                 "element_operations": [
                     "insert_table",
                     "insert_list",
