|  | 
| 13 | 13 | 
 | 
| 14 | 14 | logger = logging.getLogger(__name__) | 
| 15 | 15 | 
 | 
|  | 16 | +from .document_structure import DocumentStructure | 
|  | 17 | + | 
| 16 | 18 | class MarkdownStore: | 
| 17 | 19 |     """Manages markdown content and metadata.""" | 
| 18 | 20 | 
 | 
| 19 | 21 |     def __init__(self, storage_path: str): | 
| 20 | 22 |         self.base_path = Path(storage_path) | 
| 21 | 23 |         self.content_cache = {} | 
| 22 | 24 |         self.metadata_cache = {} | 
|  | 25 | +        self.structure_cache = {}  # Cache for parsed document structures | 
| 23 | 26 | 
 | 
| 24 | 27 |     async def get_content(self, file_id: str) -> str: | 
| 25 | 28 |         """Get markdown content.""" | 
| 26 | 29 |         file_path = self.base_path / f"{file_id}.md" | 
| 27 | 30 |         try: | 
| 28 |  | -            return file_path.read_text(encoding='utf-8') | 
|  | 31 | +            content = file_path.read_text(encoding='utf-8') | 
|  | 32 | +            # Parse and cache document structure | 
|  | 33 | +            if file_id not in self.structure_cache: | 
|  | 34 | +                structure = DocumentStructure() | 
|  | 35 | +                structure.parse_document(content) | 
|  | 36 | +                self.structure_cache[file_id] = structure | 
|  | 37 | +            return content | 
| 29 | 38 |         except Exception as e: | 
| 30 | 39 |             logger.error(f"Error reading content for {file_id}: {e}") | 
| 31 | 40 |             return f"Error reading content: {str(e)}" | 
|  | 41 | + | 
|  | 42 | +    async def get_section(self, file_id: str, section_id: str) -> str: | 
|  | 43 | +        """Get a specific section from a markdown file.""" | 
|  | 44 | +        try: | 
|  | 45 | +            if file_id not in self.structure_cache: | 
|  | 46 | +                await self.get_content(file_id)  # This will parse and cache the structure | 
|  | 47 | +             | 
|  | 48 | +            structure = self.structure_cache[file_id] | 
|  | 49 | +            section = structure.get_section_by_id(section_id) | 
|  | 50 | +             | 
|  | 51 | +            if not section: | 
|  | 52 | +                return f"Section '{section_id}' not found in {file_id}" | 
|  | 53 | +                 | 
|  | 54 | +            return f"Section: {section.title}\n\n{section.content}" | 
|  | 55 | +        except Exception as e: | 
|  | 56 | +            logger.error(f"Error getting section {section_id} from {file_id}: {e}") | 
|  | 57 | +            return f"Error getting section: {str(e)}" | 
|  | 58 | + | 
|  | 59 | +    async def get_table_of_contents(self, file_id: str) -> str: | 
|  | 60 | +        """Get table of contents for a markdown file.""" | 
|  | 61 | +        try: | 
|  | 62 | +            if file_id not in self.structure_cache: | 
|  | 63 | +                await self.get_content(file_id)  # This will parse and cache the structure | 
|  | 64 | +             | 
|  | 65 | +            structure = self.structure_cache[file_id] | 
|  | 66 | +            toc = structure.get_table_of_contents() | 
|  | 67 | +             | 
|  | 68 | +            result = [f"Table of Contents for {file_id}:"] | 
|  | 69 | +            for level, title, section_id in toc: | 
|  | 70 | +                indent = "  " * level | 
|  | 71 | +                result.append(f"{indent}- {title} [{section_id}]") | 
|  | 72 | +             | 
|  | 73 | +            return "\n".join(result) | 
|  | 74 | +        except Exception as e: | 
|  | 75 | +            logger.error(f"Error getting table of contents for {file_id}: {e}") | 
|  | 76 | +            return f"Error getting table of contents: {str(e)}" | 
| 32 | 77 | 
 | 
| 33 | 78 |     async def get_metadata(self, file_id: str) -> dict: | 
| 34 | 79 |         """Get metadata as a dictionary.""" | 
| @@ -335,6 +380,38 @@ async def list_tools() -> list[types.Tool]: | 
| 335 | 380 |                         "type": "object", | 
| 336 | 381 |                         "properties": {} | 
| 337 | 382 |                     } | 
|  | 383 | +                ), | 
|  | 384 | +                types.Tool( | 
|  | 385 | +                    name="get_section", | 
|  | 386 | +                    description="Get a specific section from a markdown file", | 
|  | 387 | +                    inputSchema={ | 
|  | 388 | +                        "type": "object", | 
|  | 389 | +                        "properties": { | 
|  | 390 | +                            "file_id": { | 
|  | 391 | +                                "type": "string", | 
|  | 392 | +                                "description": "ID of the file (without .md extension)" | 
|  | 393 | +                            }, | 
|  | 394 | +                            "section_id": { | 
|  | 395 | +                                "type": "string", | 
|  | 396 | +                                "description": "ID of the section to retrieve" | 
|  | 397 | +                            } | 
|  | 398 | +                        }, | 
|  | 399 | +                        "required": ["file_id", "section_id"] | 
|  | 400 | +                    } | 
|  | 401 | +                ), | 
|  | 402 | +                types.Tool( | 
|  | 403 | +                    name="get_table_of_contents", | 
|  | 404 | +                    description="Get table of contents for a markdown file", | 
|  | 405 | +                    inputSchema={ | 
|  | 406 | +                        "type": "object", | 
|  | 407 | +                        "properties": { | 
|  | 408 | +                            "file_id": { | 
|  | 409 | +                                "type": "string", | 
|  | 410 | +                                "description": "ID of the file (without .md extension)" | 
|  | 411 | +                            } | 
|  | 412 | +                        }, | 
|  | 413 | +                        "required": ["file_id"] | 
|  | 414 | +                    } | 
| 338 | 415 |                 ) | 
| 339 | 416 |             ] | 
| 340 | 417 | 
 | 
| @@ -373,6 +450,19 @@ async def call_tool(name: str, arguments: dict) -> list[types.TextContent]: | 
| 373 | 450 |             elif name == "get_stats": | 
| 374 | 451 |                 result = await self.store.get_stats() | 
| 375 | 452 |                 return [types.TextContent(type="text", text=result)] | 
|  | 453 | +            elif name == "get_section": | 
|  | 454 | +                file_id = arguments.get("file_id") | 
|  | 455 | +                section_id = arguments.get("section_id") | 
|  | 456 | +                if not file_id or not section_id: | 
|  | 457 | +                    raise ValueError("file_id and section_id are required") | 
|  | 458 | +                result = await self.store.get_section(file_id, section_id) | 
|  | 459 | +                return [types.TextContent(type="text", text=result)] | 
|  | 460 | +            elif name == "get_table_of_contents": | 
|  | 461 | +                file_id = arguments.get("file_id") | 
|  | 462 | +                if not file_id: | 
|  | 463 | +                    raise ValueError("file_id is required") | 
|  | 464 | +                result = await self.store.get_table_of_contents(file_id) | 
|  | 465 | +                return [types.TextContent(type="text", text=result)] | 
| 376 | 466 |             else: | 
| 377 | 467 |                 raise ValueError(f"Unknown tool: {name}") | 
| 378 | 468 | 
 | 
|  | 
0 commit comments