Skip to main content
Glama

leafwiki-mcp

An MCP server that lets compatible clients discover, read, organize, revise, favorite, create, edit, and delete content in a LeafWiki instance.

Status

The project is at version 0.1.0 and is under early development. It runs as a synchronous MCP server over stdio. Installation from PyPI is not currently documented or supported; run it from GitHub directly or from a source checkout.

Related MCP server: mediawiki-mcp-server

Prerequisites

  • Python 3.12 or newer

  • uv

Installation

Choose the method that matches how you plan to use the server. All methods run the same leafwiki-mcp command over stdio.

Use uvx to run the server directly from the GitHub repository without cloning it or installing it permanently:

uvx --from git+https://github.com/ipapadop/leafwiki-mcp.git leafwiki-mcp

uvx creates an isolated environment and caches downloaded dependencies for later runs.

Install as a persistent tool

Install the server from GitHub when you want the leafwiki-mcp command to remain available on your PATH:

uv tool install git+https://github.com/ipapadop/leafwiki-mcp.git
leafwiki-mcp

If uv reports that its tool directory is not on PATH, run uv tool update-shell and restart your shell.

Run from a source checkout

Clone over HTTPS:

git clone https://github.com/ipapadop/leafwiki-mcp.git
cd leafwiki-mcp
uv sync

Or clone over SSH:

git clone git@github.com:ipapadop/leafwiki-mcp.git
cd leafwiki-mcp
uv sync

Configuration

The server accepts command-line options or equivalent environment variables:

Option

Environment variable

Default

--url

LEAFWIKI_URL

http://localhost:8080

--username

LEAFWIKI_USERNAME

empty

--password

LEAFWIKI_PASSWORD

empty

--read-only / --no-read-only

LEAFWIKI_READ_ONLY

false

Credentials are unnecessary when authentication is disabled. Accounts requiring TOTP are not supported; use a dedicated editor account without TOTP.

The password in the MCP client example below is stored directly in that client's configuration. Protect the configuration as you would any other credential. For an authentication-disabled LeafWiki instance, omit LEAFWIKI_USERNAME and LEAFWIKI_PASSWORD entirely.

Set LEAFWIKI_READ_ONLY=true or pass --read-only to register only discovery and retrieval tools. The environment variable accepts true/false, 1/0, yes/no, and on/off case-insensitively. Either command-line option overrides the environment value, so --no-read-only explicitly enables read-write mode. Read-only mode provides defense in depth against accidental changes; it does not replace LeafWiki's server-side authorization or appropriately scoped credentials.

Running

If you installed leafwiki-mcp as a persistent tool, start it with:

leafwiki-mcp

From a source checkout, start it with:

uv run leafwiki-mcp

In normal use, an MCP client starts the server for you. Use the configuration that matches your installation method.

{
  "mcpServers": {
    "leafwiki": {
      "command": "uvx",
      "args": [
        "--from",
        "git+https://github.com/ipapadop/leafwiki-mcp.git",
        "leafwiki-mcp"
      ],
      "env": {
        "LEAFWIKI_URL": "https://wiki.example.com",
        "LEAFWIKI_USERNAME": "mcp-editor",
        "LEAFWIKI_PASSWORD": "replace-me"
      }
    }
  }
}

Persistent tool installation

{
  "mcpServers": {
    "leafwiki": {
      "command": "leafwiki-mcp",
      "env": {
        "LEAFWIKI_URL": "https://wiki.example.com",
        "LEAFWIKI_USERNAME": "mcp-editor",
        "LEAFWIKI_PASSWORD": "replace-me"
      }
    }
  }
}

Source checkout

Replace /absolute/path/to/leafwiki-mcp with the location of your clone:

{
  "mcpServers": {
    "leafwiki": {
      "command": "uv",
      "args": [
        "--directory",
        "/absolute/path/to/leafwiki-mcp",
        "run",
        "leafwiki-mcp"
      ],
      "env": {
        "LEAFWIKI_URL": "https://wiki.example.com",
        "LEAFWIKI_USERNAME": "mcp-editor",
        "LEAFWIKI_PASSWORD": "replace-me"
      }
    }
  }
}

The server always exposes these discovery and retrieval tools over stdio:

  • search_pages: search page text and tags with pagination

  • get_page: retrieve Markdown content and metadata by ID or path

  • browse_tree: browse the page hierarchy with an optional depth limit

  • get_page_links: retrieve backlinks, outgoing links, and broken links

  • list_tags: discover tags and their usage counts

  • find_pages_by_property: find pages by structured property metadata

  • list_page_revisions: list revision history when revisions are enabled

  • get_page_revision: retrieve a historical page snapshot when revisions are enabled

  • compare_page_revisions: compare two historical snapshots

  • get_indexing_status: inspect full-text indexing state

  • list_property_keys, find_page_by_title, lookup_path, and suggest_slug: discover page metadata and paths

  • list_favorites: list personal bookmarks

In the default read-write mode, the server also exposes these mutation tools:

  • move_page, copy_page, ensure_path, convert_page, and pin_page: organize pages

  • add_favorite and remove_favorite: change personal bookmarks

  • append_to_page, update_page_tags, and update_page_properties: make focused page updates

  • sort_pages: set the ordering of children under a page

  • add_page, edit_page, and delete_page: mutate pages using optimistic concurrency

Tools that select a page accept an ID or slash-separated path and prefer a non-empty ID when both are supplied. Revision tools require revisions to be enabled in LeafWiki. Favorites require an authenticated user when authentication is enabled.

Page edits and deletions use LeafWiki versions for optimistic concurrency. A conflict is returned as an error so callers can re-read the page and retry deliberately. Deletion is non-recursive by default; recursive deletion must be requested explicitly. Creating a page with content, tags, or properties performs a create followed by a versioned update, so creation may succeed even if the follow-up update fails.

Troubleshooting

  • Cannot connect: confirm LEAFWIKI_URL includes http:// or https://, points to a running LeafWiki instance, and is reachable from the MCP client process. The server starts even when LeafWiki is unreachable on the network, reports the problem on stderr, and connects when a tool is first called, so a host that is briefly down does not require restarting the MCP client. A LeafWiki instance that answers with an error status still stops startup.

  • Intermittent failures during a long session: the server logs in again and repeats the request when LeafWiki rejects an expired session. It also retries a request once when a pooled connection is dropped, except for state-changing requests that LeafWiki may already have processed; those are reported so they can be retried deliberately.

  • Authentication required: set both LEAFWIKI_USERNAME and LEAFWIKI_PASSWORD, or omit them when LeafWiki reports that authentication is disabled.

  • TOTP required: TOTP accounts are unsupported. Use a dedicated editor account without TOTP.

  • Revision request fails: confirm revisions are enabled in the LeafWiki instance.

  • Update or deletion conflicts: re-read the page to obtain its current version, review the intervening change, and retry deliberately.

Development

The tests use mocked HTTP transports and clients. They do not require a live LeafWiki instance, network access, or real credentials.

uv sync
uv run ruff check .
uv run ruff format --check .
uv run pyright
uv run pytest

The Quality GitHub Actions workflow runs Ruff lint, Ruff formatting verification, Pyright, and Pytest for every pull request, every push to main, and manual dispatches. Run the same commands locally before pushing.

License

This project is licensed under the MIT License.

Available Tools

29 tools
add_favoriteC

Favorite a page for the authenticated user.

Args: id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path.

Returns: Serializable favorite confirmation.

Inputs: id, path. Returns: object

add_pageB

Create a page or section, optionally with content and metadata.

Args: title: Display title for the new page. slug: URL path segment for the new page. parent_id: Parent identifier, or empty for the root. kind: Page kind, either page or section. content: Optional Markdown content. tags: Optional tags. properties: Optional structured properties.

Returns: JSON-compatible created page.

Inputs: slug (required), title (required), kind, tags, +3 more. Returns: object

append_to_pageB

Append Markdown content to a page.

Args: content: Markdown content to append. id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path.

Returns: JSON-compatible updated page.

Inputs: content (required), id, path. Returns: object

browse_treeC

Browse the page hierarchy, optionally limited to a depth.

Args: depth: Optional maximum tree depth.

Returns: LeafWiki page-tree object.

Inputs: depth. Returns: object

compare_page_revisionsB

Compare two historical page snapshots.

Args: base_revision_id: Revision used as the comparison base. target_revision_id: Revision compared with the base. id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path.

Returns: Revision comparison returned by LeafWiki.

Inputs: base_revision_id (required), target_revision_id (required), id, path. Returns: object

convert_pageB

Convert a page to a section or a section to a page.

Args: target_kind: Destination kind, either page or section. id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path.

Returns: Serializable conversion confirmation.

Inputs: target_kind (required), id, path. Returns: object

copy_pageB

Copy a page and its assets.

Args: title: Title for the copied page. slug: Slug for the copied page. id: Source page identifier, preferred when both selectors are supplied. path: Slash-separated source page path. target_parent_id: Destination parent identifier, or empty for root.

Returns: JSON-compatible copied page.

Inputs: slug (required), title (required), id, path, +1 more. Returns: object

delete_pageB

Delete a page after fetching its current version.

Args: id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path. recursive: Whether to delete descendants recursively. Defaults to False.

Returns: Serializable deletion confirmation.

Inputs: id, path, recursive. Returns: object

edit_pageB

Edit a page while preserving omitted fields.

Args: id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path. title: Optional replacement title; omitted values are preserved. slug: Optional replacement slug; omitted values are preserved. content: Optional replacement content; omitted values are preserved. tags: Optional replacement tags; omitted values are preserved. properties: Optional replacement properties; omitted values are preserved.

Returns: JSON-compatible updated page.

Inputs: id, path, slug, tags, +3 more. Returns: object

ensure_pathB

Ensure a nested page path exists.

Args: path: Slash-separated path to ensure. title: Title for the final node. kind: Kind for the final node, either page or section.

Returns: JSON-compatible final page.

Inputs: path (required), title (required), kind. Returns: object

find_page_by_titleC

Find pages by title.

Args: title: Title to match.

Returns: Title-match result returned by LeafWiki.

Inputs: title (required). Returns: object

find_pages_by_propertyB

Find pages carrying a property key and optional value.

Args: key: Required property key. value: Optional property value to match.

Returns: Matching page summaries.

Inputs: key (required), value. Returns: result

get_indexing_statusB

Get full-text search indexing status.

Returns: Indexing status returned by LeafWiki.

Inputs: no named parameters. Returns: object

get_pageB

Get a page by ID or path, including content and metadata.

Args: id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path.

Returns: JSON-compatible page.

Inputs: id, path. Returns: object

get_page_revisionA

Get a page revision; requires revisions to be enabled.

Args: revision_id: Revision identifier to retrieve. id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path.

Returns: Historical page snapshot.

Inputs: revision_id (required), id, path. Returns: object

list_favoritesB

List the authenticated user's favorite pages.

Returns: Favorite page summaries.

Inputs: no named parameters. Returns: result

list_page_revisionsA

List page revisions; requires revisions to be enabled.

Args: id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path. cursor: Optional pagination cursor. limit: Maximum number of revisions.

Returns: Revision metadata and pagination information.

Inputs: id, path, limit, cursor. Returns: object

list_property_keysC

List available property keys.

Args: query: Optional property-key filter. limit: Maximum number of keys.

Returns: Property-key objects returned by LeafWiki.

Inputs: limit, query. Returns: result

list_tagsB

List tags and usage counts, optionally filtering by name.

Args: query: Optional tag-name filter. selected: Tags LeafWiki should retain in the result set. limit: Maximum number of tags.

Returns: Tag objects returned by LeafWiki.

Inputs: limit, query, selected. Returns: result

lookup_pathB

Resolve a path into tree segments.

Args: path: Slash-separated page path to resolve.

Returns: Existing and missing path segments.

Inputs: path (required). Returns: object

move_pageB

Move a page under a new parent.

Args: id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path. parent_id: Destination parent identifier, or empty for root. position: Optional zero-based position under the parent.

Returns: Serializable move confirmation.

Inputs: id, path, position, parent_id. Returns: object

pin_pageB

Pin or unpin a page.

Args: pinned: Whether the page should be pinned. id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path.

Returns: JSON-compatible updated page.

Inputs: id, path, pinned. Returns: object

remove_favoriteA

Remove a page from the authenticated user's favorites.

Args: id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path.

Returns: Serializable removal confirmation.

Inputs: id, path. Returns: object

search_pagesA

Search pages by text and tags, returning excerpts and tag facets.

Args: query: Full-text search query. tags: Optional tags used to filter results. offset: Zero-based result offset. limit: Maximum number of results.

Returns: Search result object returned by LeafWiki.

Inputs: tags, limit, query, offset. Returns: object

sort_pagesB

Set the child ordering under a parent page.

Args: parent_id: Parent identifier, or empty for root. ordered_ids: Child identifiers in their desired order.

Returns: Serializable sort confirmation.

Inputs: parent_id (required), ordered_ids (required). Returns: object

suggest_slugB

Suggest an available slug for a title.

Args: title: Title from which to derive a slug. parent_id: Parent used to determine availability. current_id: Existing page to exclude from conflicts.

Returns: Slug suggestion returned by LeafWiki.

Inputs: title (required), parent_id, current_id. Returns: object

update_page_propertiesB

Replace a page's properties.

Args: properties: Complete replacement property mapping. id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path.

Returns: JSON-compatible updated page.

Inputs: properties (required), id, path. Returns: object

update_page_tagsA

Replace a page's tags.

Args: tags: Complete replacement tag list. id: Page identifier, preferred when both selectors are supplied. path: Slash-separated page path.

Returns: JSON-compatible updated page.

Inputs: tags (required), id, path. Returns: object

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    B3.1/5.0

    Scored across 29 tools

    Disambiguation3/5

    Most tools have distinct purposes, but several boundaries are blurred: edit_page can replace tags, properties, and content, duplicating update_page_tags, update_page_properties, and append_to_page; move_page overlaps sort_pages, and add_page overlaps ensure_path. Descriptions help, but the set still presents multiple overlapping update and path-creation options.

    Naming Consistency5/5

    All tool names use consistent snake_case with a verb-first pattern (get_, list_, find_, update_, add_, delete_, etc.). Synonym variation (get/find/search/browse/lookup) exists but does not break the predictable naming convention.

    Tool Count2/5

    At 29 tools, the surface is heavy for a wiki MCP server, and several tools are redundant conveniences (e.g., update_page_tags, update_page_properties, append_to_page) that edit_page already covers. The count exceeds the 25+ threshold for 'too many' in the rubric.

    Completeness4/5

    The set covers the core wiki lifecycle well: page CRUD, move, copy, sort, revisions, tags, properties, favorites, search, tree browsing, and link inspection. Minor gaps exist (e.g., dedicated asset management, tag rename/delete, bulk operations), but no major dead ends.

    Maintenance

    ActivitySlowing
    ResponsivenessOutdated data

    Related MCP Connectors

    Related MCP Servers