Blog

The indexer of The Smarter Project’s blog.

index_blog() brings the blog’s RepositoryIndex up to date with its published posts, so that the MCP server’s search_blog and read_blog_post tools can read them as they read a repository’s documentation:

  1. It reads every published post from the blog’s public WordPress REST api, /wp-json/wp/v2/posts, 100 posts a request.

  2. It fingerprints the posts by their ids and modification times. If the fingerprint is already indexed, there is nothing to do.

  3. Otherwise it converts each post’s html to Markdown-like text, with its illustrations as Markdown images, ![alt](url), which Smarter Chat displays, and replaces the blog’s files in one transaction, with store_files(). A post’s path is its permalink’s path, e.g. 2026/10/26/release-smarter-api-v0-6, and the fingerprint is stored as the index’s commit_sha.

The url is the fixed url of the project’s public blog, not a user-supplied one, so the requests do not go through smarter.apps.llmtool.plugin.safe_http.

Note

Experimental. The MCPServer was designed and coded by Claude Code (Anthropic’s Claude Opus 5.5), with Lawrence McDaniel as co-author. It is experimental, and will be documented.

class smarter.apps.mcpserver.blog.WordPressPost(*, id, date, modified, slug, link, title, content)[source]

Bases: BaseModel

A post, as the WordPress REST api returns it.

Only the fields that the indexer reads are modeled. WordPress and its plugins add others, e.g. protected and raw, which are ignored.

content: WordPressRendered
date: datetime
id: int
model_config = {'extra': 'ignore'}

Configuration for the model, should be a dictionary conforming to [ConfigDict][pydantic.config.ConfigDict].

modified: datetime
slug: str
title: WordPressRendered
smarter.apps.mcpserver.blog.fetch_posts(blog)[source]

Return the blog’s published posts, newest first, from its WordPress REST api.

Parameters:

blog (ProjectBlog) – The blog.

Return type:

list[WordPressPost]

Returns:

The posts.

Raises:

SmarterMCPServerIndexError – If the blog does not answer with posts.

smarter.apps.mcpserver.blog.html_to_text(content, base_url='')[source]

Return a post’s html as Markdown-like text, for search and for an LLM to read.

Parameters:
  • content (str) – The post’s rendered html.

  • base_url (str) – The url that relative image urls are relative to, e.g. the blog’s.

Return type:

str

Returns:

The text, one block per paragraph, heading, list, table, code block or image.

smarter.apps.mcpserver.blog.index_blog(force=False, blog=None)[source]

Bring the blog’s index up to date with its published posts.

Parameters:
Return type:

RepositoryIndex

Returns:

The blog’s index. If indexing fails, its error says why, and an existing index is kept.

Example usage:

index = index_blog()
index.file_count  # the number of posts
smarter.apps.mcpserver.blog.post_file(blog, post)[source]

Return a post as a file of the blog’s index.

The text starts with the post’s title, dates and url, so that a reader of any part of it can cite it. A post longer than MAX_FILE_BYTES is truncated.

Parameters:
Return type:

ArchiveFile

Returns:

The file.

smarter.apps.mcpserver.blog.posts_fingerprint(posts)[source]

Return a fingerprint of the posts, which changes when a post is published, changed or removed.

Parameters:

posts (list[WordPressPost]) – The posts.

Return type:

str

Returns:

40 hexadecimal characters, the size of a commit’s sha.