get_page
Retrieve the full markdown content of a Confluence page by its ID, exact title, or title substring, with optional section filtering.
Instructions
Return the full markdown of a page.
Args: page_ref: Confluence page id, exact title, or substring of the title. section: Optional heading text. If provided, only the matching section (and its sub-sections) is returned.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| page_ref | Yes | ||
| section | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| result | Yes |
Implementation Reference
- src/of_mcp/server.py:77-101 (handler)The MCP tool handler for 'get_page'. Accepts page_ref (Confluence id, exact title, or substring) and optional section heading. Looks up the page via db.find_page_by_slug_or_title, then optionally slices to a heading section via _slice_section.
@mcp.tool() def get_page(page_ref: str, section: str | None = None) -> str: """Return the full markdown of a page. Args: page_ref: Confluence page id, exact title, or substring of the title. section: Optional heading text. If provided, only the matching section (and its sub-sections) is returned. """ with closing(_conn()) as conn: row = db.find_page_by_slug_or_title(conn, page_ref) if not row: return f"No page found for {page_ref!r}." header = ( f"# {row['title']}\n\n" f"_Breadcrumb:_ {row['breadcrumb']}\n" f"_URL:_ {row['url']}\n" f"_Page id:_ {row['id']}\n\n---\n\n" ) body = row["body_md"] if section: body = _slice_section(body, section) if not body: return header + f"_(section {section!r} not found in page)_" return header + body - src/of_mcp/server.py:77-78 (registration)The @mcp.tool() decorator registers get_page as an MCP tool on the FastMCP server instance (line 34: mcp = FastMCP('of-mcp')).
@mcp.tool() def get_page(page_ref: str, section: str | None = None) -> str: - src/of_mcp/server.py:150-174 (helper)Helper function _slice_section used by get_page to extract a single heading section (and its sub-sections) from the markdown body, based on case-insensitive heading text matching.
def _slice_section(body_md: str, section: str) -> str: """Return the heading whose text contains `section` (case-insensitive), plus everything until the next heading of equal or higher level. """ import re needle = section.strip().lower() lines = body_md.splitlines() start = -1 start_level = 0 for i, line in enumerate(lines): m = re.match(r"^(#{1,6})\s+(.*?)\s*$", line) if m and needle in m.group(2).lower(): start = i start_level = len(m.group(1)) break if start < 0: return "" end = len(lines) for j in range(start + 1, len(lines)): m = re.match(r"^(#{1,6})\s+", lines[j]) if m and len(m.group(1)) <= start_level: end = j break return "\n".join(lines[start:end]).strip() - src/of_mcp/db.py:180-191 (helper)Database helper find_page_by_slug_or_title used by get_page handler. Attempts lookup by exact id, exact title, then substring (LIKE) match on title.
def find_page_by_slug_or_title(conn: sqlite3.Connection, needle: str) -> sqlite3.Row | None: # 1) exact id, 2) exact title, 3) LIKE on title row = conn.execute("SELECT * FROM pages WHERE id = ? LIMIT 1", (needle,)).fetchone() if row: return row row = conn.execute("SELECT * FROM pages WHERE title = ? LIMIT 1", (needle,)).fetchone() if row: return row return conn.execute( "SELECT * FROM pages WHERE title LIKE ? ORDER BY length(title) ASC LIMIT 1", (f"%{needle}%",), ).fetchone() - src/of_mcp/db.py:24-35 (schema)The SQLite schema for the 'pages' table defines the shape of page data (id, title, url, parent_id, breadcrumb, space_key, version, updated_at, body_md) returned by the get_page tool.
SCHEMA = """ CREATE TABLE IF NOT EXISTS pages ( id TEXT PRIMARY KEY, -- Confluence content id title TEXT NOT NULL, url TEXT NOT NULL, parent_id TEXT, -- Confluence parent page id (or NULL) breadcrumb TEXT NOT NULL, -- "Home > Section > Page" space_key TEXT NOT NULL, version INTEGER NOT NULL DEFAULT 0, updated_at TEXT, -- ISO8601 body_md TEXT NOT NULL, -- full page as markdown fetched_at TEXT NOT NULL DEFAULT (datetime('now'))