Balzac
Balzac MCP server: research keywords, write and publish SEO blog articles, manage workspaces, suggestions, briefings and Search Console data from Claude, ChatGPT or Cursor.
Hosted MCP Server
npx add-mcp 'https://mcp.hirebalzac.ai'Installs into Claude Code, Codex, Cursor and more
Documentation
Balzac MCP Server
MCP server for the Balzac AI content platform -- give AI agents native access to keyword research, article writing, and CMS publishing.
The Balzac MCP server implements the Model Context Protocol so that AI agents like Claude Desktop, OpenClaw, Claude Code, and any MCP-compatible client can manage your entire content pipeline through structured tool calls.
Not sure whether your site lets AI crawlers in? The free AI crawler checker and the other free SEO tools need no signup.
Remote server (Claude, ChatGPT, and other connectors)
Balzac is also available as a hosted MCP server, with nothing to install:
https://mcp.hirebalzac.ai
- Claude (claude.ai, Desktop, mobile): Settings > Connectors > Add custom connector, paste the URL, then sign in to Balzac and allow access.
- ChatGPT: turn on developer mode under Settings > Apps & Connectors > Advanced settings, create a connector with the URL and OAuth authentication, then sign in to Balzac.
- Claude Code:
claude mcp add --transport http balzac https://mcp.hirebalzac.ai, then run/mcpto sign in.
Clients that can't do OAuth can send an API key instead, as an Authorization: Bearer bz_... header. You can disconnect apps at any time from your Balzac profile page.
A connected app acts with the role of the person who approved it. Members don't get the admin-only tools (see Roles).
Content created through the remote connector never gets AI-generated images. Workspaces it creates start with ai_images: false, so the setup that follows create_workspace picks stock photos or title covers on a gradient of the brand color. The articles it writes (create_briefing, accept_suggestion), rewrites or gives a new cover keep AI out of all their covers, even in a workspace that allows AI images. Without AI images, a title cover is the article title on a gradient of the brand color, any other cover is a stock photo, and a first cover with no matching stock photo gets the title gradient instead.
The tools follow the same rule: they offer no AI picture style and no ai mode, and title_based_featured_image and ai_images can only be turned off. update_settings with ai_images: false keeps every cover of a workspace free of AI images, including articles written in the Balzac app or by autopilot; turning ai_images back on is done in the Balzac app (Settings > Images), and the API answers 403 forbidden to a connector that tries. Turning auto_accept_suggestions on in a workspace whose ai_images is true also returns 403, unless the same call sends ai_images: false. regenerate_article_picture has two modes: title or stock (a stock photo, which never falls back to AI: when no photo matches, the tool returns 422 no_stock_photo and nothing is counted, so try again with search words in additional_instructions, or use title).
The guarantee covers OAuth sign-ins, which is how Claude and ChatGPT connect. With a bz_ API key, the remote server's tools still never ask for AI images, but the covers of new articles follow the workspace's ai_images setting.
The local server below offers the same tools, plus AI picture styles and the ai cover mode, using an API key.
Registry listing
This repository includes a server.json (and mcpName in package.json: io.github.hirebalzac/mcp) prepared for the official MCP Registry. Registry listing is not live until it has been published; check the registry for current status.
Balzac is an AI SEO agent that researches keywords, then writes and publishes blog articles. The free tier includes 3 articles; paid plans start at $79/month.
The local (npm, stdio) server reads BALZAC_API_KEY (required) and BALZAC_API_URL (optional). Never commit your key.
Quick Start
1. Get your API key
Log in to Balzac, go to Settings > API Keys, and generate a key.
2. Configure your MCP client
Add to your MCP configuration (Claude Desktop, OpenClaw, or any MCP-compatible host):
{
"mcpServers": {
"balzac": {
"command": "npx",
"args": ["-y", "balzac-mcp"],
"env": {
"BALZAC_API_KEY": "bz_your_api_key_here"
}
}
}
}
For Claude Desktop, this file lives at ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows).
3. Start using it
Once configured, your AI agent can directly call Balzac tools:
"Research keywords for my site and write an SEO article about the best opportunity"
"Write 3 articles about our top keywords and publish them as drafts to WordPress"
"Rewrite my latest article with a more professional tone"
Environment Variables
| Variable | Required | Description |
|---|---|---|
BALZAC_API_KEY | Yes | Your Balzac API key (starts with bz_) |
BALZAC_API_URL | No | API base URL (default: https://api.hirebalzac.ai/v1) |
The remote server (npm run start:http, deployed from the Dockerfile) takes its credentials from each request instead of BALZAC_API_KEY, and reads:
| Variable | Description |
|---|---|
MCP_PUBLIC_URL | Public URL of the server (https://mcp.hirebalzac.ai in production) |
BALZAC_AUTH_URL | OAuth authorization server (default: https://app.hirebalzac.ai) |
BALZAC_API_URL | API base URL (default: https://api.hirebalzac.ai/v1) |
PORT | Port to listen on (default: 3001) |
Available Tools
Account
| Tool | Description |
|---|---|
get_account | Account, available credits, and whether the credentials act as an admin |
Workspaces
| Tool | Description |
|---|---|
list_workspaces | List all workspaces (filter by status: new, running, ready, imported, not_imported) |
get_workspace | Get workspace details |
create_workspace | Create a workspace from a domain |
update_workspace | Update workspace settings |
delete_workspace | Delete a workspace (admins only) |
Keywords
| Tool | Description |
|---|---|
list_keywords | List keywords (filter by status) |
get_keyword | Get keyword details (volume, competition, intent, difficulty, GSC metrics) |
create_keyword | Add a keyword |
enable_keyword | Enable a keyword |
disable_keyword | Disable a keyword |
delete_keyword | Delete a keyword |
generate_keywords | Generate new keywords with AI (async) |
Suggestions
| Tool | Description |
|---|---|
list_suggestions | List content suggestions |
get_suggestion | Get suggestion details |
generate_suggestions | Generate 10 new suggestions (1 credit) |
accept_suggestion | Accept and start writing (5 credits) |
reject_suggestion | Reject a suggestion |
Briefings
| Tool | Description |
|---|---|
list_briefings | List briefings |
get_briefing | Get briefing details |
create_briefing | Create a briefing and start writing (5 credits) |
Articles
| Tool | Description |
|---|---|
list_articles | List articles (filter by status, published), with each one's live_url, rewrites_left and new_covers_left |
get_article | Get article details and content, live_url, publications, and the free rewrites_left and new_covers_left |
update_article | Update article metadata |
delete_article | Delete an article |
rewrite_article | Rewrite article content (free, 2 per article) |
regenerate_article_picture | Generate a new cover (free, 2 per article): title, stock photo or, on the local server, an AI style |
publish_article | Publish to an integration (the live URL shows up later in get_article) |
schedule_article | Schedule future publication |
export_article | Export as HTML, Markdown, or XML |
rewrite_article, publish_article and schedule_article return the article without html_content, published_html and schema_json_ld: get_article has them. When the article is already on that integration, publish_article creates no new publication and passes on the API's message, which says when nothing was sent because the integration can't take updates (GoHighLevel, or a webhook with webhook_updates off).
Competitors
| Tool | Description |
|---|---|
list_competitors | List competitor domains |
create_competitor | Add a competitor |
delete_competitor | Remove a competitor |
Links
| Tool | Description |
|---|---|
list_links | List reference links |
create_link | Add a reference link |
delete_link | Remove a link |
Settings
| Tool | Description |
|---|---|
get_settings | Get workspace settings, including ai_images and cover_mode |
update_settings | Update workspace settings, including ai_images (on the remote server, only false) |
Tones of Voice
| Tool | Description |
|---|---|
list_tones | List available tones |
get_tone | Get tone details |
Integrations
| Tool | Description |
|---|---|
list_integrations | List publishing integrations |
get_integration | Get integration details (credentials are never returned) |
create_integration | Create an integration (WordPress, Webflow, Wix, GoHighLevel, Webhook), admins only |
update_integration | Update integration settings, admins only |
delete_integration | Delete an integration, admins only |
reconnect_integration | Re-test integration connection, admins only |
When update_integration moves wordpress_url to another site, send wordpress_application_password in the same call; when it changes webhook_url on an integration with a bearer token, send webhook_bearer_token. Otherwise the update fails with 422 validation_failed.
For webhooks, webhook_updates (on create_integration and update_integration) says whether the endpoint gets an article.updated call when an article already published there changes. New webhooks have it on; send false for an endpoint that creates a post on every call. Webhooks connected before updates existed have it off: turn it on once the endpoint updates the post it created. While it is off, publish_article on an article already there sends nothing and says so.
Credit Costs
| Action | Credits |
|---|---|
| Writing an article (accept suggestion or create briefing) | 5 |
| Generating 10 new suggestions | 1 |
If your account doesn't have enough credits, the tool returns an error with the required and available credit counts. get_account shows the credits left.
Rewriting an article and generating a new cover are free. Each article includes 2 rewrites and 2 new covers; one counts when it finishes, and get_article shows what is left (rewrites_left, new_covers_left). Starting another while one runs returns 409 conflict, and once an article has used its 2 the tool returns 422 free_limit_reached.
Roles
Only admins can manage integrations (create_integration, update_integration, delete_integration, reconnect_integration) and delete workspaces (delete_workspace). Members get 403 forbidden there, and can still list integrations and publish to them.
API keys count as admin, so the local server keeps every tool. On the remote server, an app connected by a member doesn't list the admin-only tools at all. Apps cache the tool list, so after a role change (or for a connection made before this update), reconnect the app to refresh it: until then a member calling an admin-only tool gets a "Tool ... not found" error instead of the 403 message, and a newly promoted admin doesn't see those tools yet.
Errors
Tool errors start with the HTTP status and the API's error type, then the message, for example:
[422 free_limit_reached] You've used the 2 free rewrites for this article.
[422 plan_limit_reached] Your Columnist plan includes 1 website. Upgrade your plan to add another one.
[403 forbidden] Only company admins can do this. Ask an admin of your Balzac account.
[422 no_stock_photo] No stock photo matches this article. Send a few search words in additional_instructions (for example "laptop on a desk"), or use picture_mode: title.
See the API documentation for every error type.
See Also
- Balzac CLI -- Command-line interface
- API Documentation -- Full REST API reference
License
MIT