A CLI + TUI toolkit + MCP server for Jekyll blogging with an embedded Neovim editor
BlogHelper9000 is a .NET toolset for managing Jekyll blog posts. It ships as three executables: a CLI tool (bloghelper) for scripting and quick operations, a TUI workspace with an embedded Neovim editor for an integrated writing experience, and an MCP server (bloghelper-mcp) that exposes blog management capabilities to AI agents (like GitHub Copilot, Claude, and VS Code) — all from your terminal.
- CLI tool — Create posts & drafts, publish drafts, inspect blog stats, and batch-fix YAML front matter
- TUI workspace — Full terminal UI with file browser, command palette, and embedded Neovim editor
- MCP server — Expose blog management tools to AI agents via the Model Context Protocol (stdio transport)
- Embedded Neovim — Real Neovim running headless via MsgPack-RPC, rendered as a Terminal.Gui view with full mode/cursor support
- Featured image generation — Search Unsplash, download images, and generate branded featured images with ImageSharp
- YAML front matter management — Parse, serialise, and bulk-fix front matter (published status, descriptions, tags)
- Safe mode — Run the TUI with
--no-nvimfor a plain-text editor fallback when Neovim isn't available
dotnet tool install --global BlogHelper9000Once installed, the bloghelper command is available globally.
git clone https://github.com/sgrassie/BlogHelper9000.git
cd BlogHelper9000
./build.shAll CLI commands operate on a Jekyll blog directory. Pass --base-directory to point at your blog root, or configure it in bloghelper9000.json.
bloghelper info --base-directory /path/to/blogShows total post count, drafts, days since last post, and recent posts.
bloghelper add "My New Post" --base-directory /path/to/blog --is-draftbloghelper add "My New Post" --base-directory /path/to/blogbloghelper publish "my-draft-post" --base-directory /path/to/blogMoves the draft from _drafts/ into _posts/<year>/, sets the published date, and updates the YAML front matter.
# Fix published status (extract dates from filenames)
bloghelper fix --base-directory /path/to/blog --status
# Migrate legacy descriptions
bloghelper fix --base-directory /path/to/blog --description
# Normalize and de-duplicate tags
bloghelper fix --base-directory /path/to/blog --tagsbloghelper add-image "my-post" --image-query "mountains" --base-directory /path/to/blogbloghelper unsplash-credentials --access-key YOUR_KEY --secret-key YOUR_SECRETCredentials are stored in ~/Documents/bloghelper9000.json.
The TUI provides a full terminal workspace for writing and managing blog posts. It uses Terminal.Gui v2 and embeds a real Neovim instance for editing.
dotnet run --project BlogHelper9000.Tui -- --base-directory /path/to/blogdotnet run --project BlogHelper9000.Tui -- --no-nvim --base-directory /path/to/blog| Shortcut | Action |
|---|---|
Ctrl+B |
Toggle file browser |
Ctrl+P |
Open command palette |
Ctrl+Q |
Quit |
Press Ctrl+P to open the command palette. It provides filtered access to blog operations:
| Command | Description |
|---|---|
| New Draft | Create a new draft post |
| New Post | Create a new published post |
| Publish Draft | Publish an existing draft |
| Blog Info | Show blog statistics |
| Fix Metadata: Status | Fix published status on all posts |
| Fix Metadata: Description | Migrate legacy descriptions |
| Fix Metadata: Tags | Fix and normalize tags |
The MCP server exposes BlogHelper9000's blog management capabilities to AI agents via the Model Context Protocol. This allows AI assistants like Claude Desktop, GitHub Copilot, and VS Code to directly manage your Jekyll blog posts.
| Tool Name | Description |
|---|---|
add_post |
Create a new blog post or draft with title and metadata |
publish_post |
Publish a draft from _drafts/ to _posts/ with date prefix |
get_blog_info |
Get blog statistics (post count, drafts, recent posts, days since last post) |
list_drafts |
List all draft blog posts in the _drafts/ directory |
fix_metadata |
Batch-fix YAML front matter (published status, descriptions, tags) |
add_featured_image |
Generate and apply a featured image from Unsplash with title overlay |
Install as a .NET global tool:
dotnet tool install --global BlogHelper9000.McpOr run directly from source:
dotnet run --project BlogHelper9000.McpThe MCP server reads the blog base directory from:
BLOG_BASE_DIRECTORYenvironment variable- First positional argument
- Current working directory (fallback)
Add to your Claude Desktop configuration (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
{
"mcpServers": {
"bloghelper9000": {
"command": "bloghelper-mcp",
"args": ["/path/to/your/jekyll/blog"]
}
}
}Or use the environment variable approach:
{
"mcpServers": {
"bloghelper9000": {
"command": "bloghelper-mcp",
"env": {
"BLOG_BASE_DIRECTORY": "/path/to/your/jekyll/blog"
}
}
}
}Add to your workspace or user settings (.vscode/settings.json):
{
"mcp.servers": {
"bloghelper9000": {
"command": "dotnet",
"args": ["run", "--project", "/path/to/BlogHelper9000.Mcp"],
"env": {
"BLOG_BASE_DIRECTORY": "/path/to/your/jekyll/blog"
}
}
}
}Once configured, you can ask your AI assistant to:
- "Create a new draft post titled 'Understanding MCP'"
- "List all my draft posts"
- "Show me blog statistics"
- "Publish the draft 'understanding-mcp'"
- "Add a featured image about 'programming' to my latest post"
The AI will use the appropriate MCP tool to execute your request.
BlogHelper9000.sln
├── BlogHelper9000.Core/ Core domain logic
├── BlogHelper9000.Imaging/ Image processing
├── BlogHelper9000/ CLI executable
├── BlogHelper9000.Mcp/ MCP server executable
├── BlogHelper9000.Nvim/ Neovim client
├── BlogHelper9000.Tui/ TUI executable
├── BlogHelper9000.Tests/ CLI tests
├── BlogHelper9000.Mcp.Tests/ MCP server tests
├── BlogHelper9000.Nvim.Tests/ Neovim tests
├── BlogHelper9000.Tui.Tests/ TUI tests
└── BlogHelper9000.TestHelpers/ Shared test infra
| Project | Type | Description |
|---|---|---|
| BlogHelper9000.Core | classlib | Domain logic — PostManager, MarkdownHandler, YamlConvert, BlogService. No UI dependencies. |
| BlogHelper9000.Imaging | classlib | Featured image generation — ImageProcessor, UnsplashClient, FontManager. Uses SixLabors.ImageSharp. |
| BlogHelper9000 | exe | CLI tool using TimeWarp.Nuru mediator pattern. Packaged as a dotnet tool. |
| BlogHelper9000.Mcp | exe | MCP server exposing blog management tools via stdio transport for AI agents (Claude, VS Code, etc.). Packaged as a dotnet tool. |
| BlogHelper9000.Nvim | classlib | Embedded Neovim client. NvimProcess manages nvim --embed --headless. MsgPackRpcClient handles MsgPack-RPC framing. NvimGrid maintains a 2D screen buffer. |
| BlogHelper9000.Tui | exe | Terminal.Gui v2 workspace. NvimEditorView renders the Neovim grid. CommandPalette exposes blog operations. KeyTranslator maps Terminal.Gui keys to Neovim notation. |
| BlogHelper9000.Tests | test | xUnit tests for CLI commands |
| BlogHelper9000.Mcp.Tests | test | xUnit tests for MCP server tools |
| BlogHelper9000.Nvim.Tests | test | Tests for grid operations, MsgPack serialization, and UI event parsing (no nvim required) |
| BlogHelper9000.Tui.Tests | test | Tests for TUI workspace components |
| BlogHelper9000.TestHelpers | classlib | JekyllBlogFilesystemBuilder and shared test infrastructure using MockFileSystem |
| Dependency | Version | Purpose |
|---|---|---|
| .NET | 10.0 | Runtime & SDK |
| C# | latest | Language version with nullable reference types |
| Terminal.Gui | 2.0.0-develop.5027 | TUI framework (v2 develop track) |
| MessagePack | v3 | MsgPack-RPC serialization for Neovim communication |
| SixLabors.ImageSharp | 3.1.12 | Image processing & featured image generation |
| SixLabors.Fonts | 2.1.3 | Font rendering for image overlays (bundled Ubuntu fonts) |
| TimeWarp.Nuru | 2.1.0-beta.32 | Mediator pattern for CLI command routing |
| Spectre.Console | 0.54.0 | CLI console output formatting |
| MinVer | 7.0.0 | Git-based semantic versioning |
| Cake Build | — | Build orchestration |
| xUnit v3 | — | Test framework |
| FluentAssertions | — | Test assertion library |
| NSubstitute | — | Mocking framework |
| System.IO.Abstractions | 22.1.0 | File system abstraction for testability |
BlogHelper9000 uses Cake for build orchestration.
# Build
./build.sh
# Build and run tests
./build.sh --target=tests
# Build, test, and pack as NuGet tool
./build.sh --target=pack --configuration=release
# Run all tests directly
dotnet test BlogHelper9000.sln
# Run specific test projects
dotnet test BlogHelper9000.Tests/BlogHelper9000.Tests.csproj
dotnet test BlogHelper9000.Nvim.Tests/BlogHelper9000.Nvim.Tests.csproj
# Run a single test by name
dotnet test BlogHelper9000.Tests/BlogHelper9000.Tests.csproj --filter "FullyQualifiedName~YamlConvertTests.Can_Serialise"To use featured image generation, configure your Unsplash API credentials:
bloghelper unsplash-credentials --access-key YOUR_ACCESS_KEY --secret-key YOUR_SECRET_KEYThis stores credentials in ~/Documents/bloghelper9000.json.
All commands accept --base-directory to specify the Jekyll blog root directory. This should point to the directory containing your _posts/ and _drafts/ folders.
This project is licensed under the MIT License.
Copyright (c) 2022 Stuart Grassie
See the original blog post: Automating Jekyll with .NET