New:Microsoft Teams Notifications Are Now Available in Socket.Learn more
Get Started

@motiblog/mcp

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@motiblog/mcp

Agent-first SEO research and publishing: competitors, SERPs, internal links, and Search Console

latest
Source
npmnpm
Version
0.6.0
Version published
Weekly downloads
96
-43.2%
Maintainers
1
Weekly downloads
 
Created
Source

MotiBlog MCP server

Agent-first control surface over the MotiBlog API (docs/product/agent-first-direction.md, Direction B). Any MCP-capable agent — Claude Code, Cursor, Codex — can run a whole blog: propose topics, generate, review fact-check reports, approve through the governed gate, publish to targets, or export content to deploy into its own codebase.

Quick start — hosted (what a customer uses)

The transport is mounted inside the API process, so the deployed API is the MCP endpoint. No install, no repository checkout, no separate service:

# 1. Get a per-project API key (dashboard: Project → Blog API → copy/rotate)
# 2. Point any MCP-capable agent at the hosted endpoint:
claude mcp add --transport http motiblog https://api.motiblog.ai/mcp \
  --header "x-api-key: <your-key>"

Verify by hand:

curl -s https://api.motiblog.ai/mcp \
  -H 'content-type: application/json' \
  -H 'accept: application/json, text/event-stream' \
  -H 'x-api-key: <your-key>' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'

Quick start — local stdio (development)

Running from the repository, for work on the tool layer itself:

claude mcp add motiblog \
  --env MOTIBLOG_API_KEY=<your-key> \
  --env MOTIBLOG_PROJECT_ID=<optional-default-project> \
  -- pnpm --filter @motiblog/mcp start

The package is also published to npm for local stdio use; the hosted endpoint is the recommended customer path because OAuth needs no copied project key.

Or use the committed .mcp.json: set MOTIBLOG_API_KEY in your environment and start Claude Code in the repo root.

Env varRequiredMeaning
MOTIBLOG_API_KEYyesPer-project key (Project.blogApiKey). Rotatable from dashboard.
MOTIBLOG_API_URLnoAPI base URL. Default http://localhost:3001.
MOTIBLOG_PROJECT_IDnoDefault project; tools still accept explicit project_id.

Remote agents: streamable HTTP

How it is served in production

The handler (src/http-handler.ts) is mounted inside the API process as Express middleware — apps/api/src/mcp/mcp.middleware.ts, registered from apps/api/src/main.ts. Consequences worth knowing:

  • POST api.motiblog.ai/mcp works with the ordinary API deploy. There is no second container and no extra reverse-proxy rule — which is exactly why the endpoint was unreachable before: the code existed, the route did not.
  • It is registered with app.use() before app.listen(), so it sits ahead of Nest's router. The global ValidationPipe and TransformInterceptor never see it; if they did, they would rewrap the JSON-RPC envelope in the API's { success, data } shape and no client could parse it.
  • Nest's body-parser has already drained the request stream by then, so the parsed body is handed to the transport explicitly. Skip that and the transport waits on a stream that will never emit again.
  • Tool calls loop back to the same API over loopback, so an agent's key passes the same guards an external caller meets. Being mounted in-process buys no privileged shortcut. Override the loopback base with MOTIBLOG_MCP_API_URL.

Covered by apps/api/src/mcp/mcp.middleware.spec.ts, which boots a real Nest app with those globals installed and asserts a full handshake survives.

Standalone server

For local work, or to serve MCP from somewhere other than the API:

pnpm --filter @motiblog/mcp start:http
# → http://127.0.0.1:3021/mcp  (POST, JSON-RPC; stateless)
Env varDefaultMeaning
MOTIBLOG_HTTP_PORT3021Listen port
MOTIBLOG_HTTP_HOST127.0.0.1Bind host (use 0.0.0.0 behind a proxy)

Every request must carry the key (x-api-key or Authorization: Bearer); requests are authenticated and served independently (stateless — no session affinity).

The hosted endpoint supports OAuth 2.1 discovery, dynamic client registration, PKCE, refresh rotation, and human-selected project access. A person can allow all projects, an explicit subset, or only projects the agent creates. Per-project keys remain available for automations and local stdio use.

Publishing to npm

Published as @motiblog/mcp under the motiblog org. Consumers run it with:

claude mcp add motiblog --env MOTIBLOG_API_KEY=<key> -- npx -y @motiblog/mcp

Prerequisites for whoever publishes: membership of the motiblog org, npm login, and 2FA enabled — npm rejects publishes without it (403 … Two-factor authentication or granular access token with bypass 2fa enabled is required). Either pass --otp=<code> or use a granular access token with Bypass 2FA in ~/.npmrc.

Then, from apps/mcp:

pnpm publish --access public          # runs tsup via prepublishOnly

Verify before you push the button:

pnpm pack                              # writes motiblog-mcp-<version>.tgz
tar -tzf motiblog-mcp-*.tgz            # dist/ + README.md, nothing else
tar -xzOf motiblog-mcp-*.tgz package/package.json | grep -E '"main"|"bin"'

Three things that must stay true, and all are easy to break:

  • The mcp bin alias. npx -y @motiblog/mcp resolves the bin whose key matches the package's unscoped name. Published 0.1.0 had only motiblog-mcp and motiblog-mcp-http, so the obvious command failed with "could not determine executable to run" — fixed in 0.1.1 by aliasing mcp to the stdio entry. Test the bare npx -y @motiblog/mcp after any change to publishConfig.bin, not just the long form.

  • @motiblog/shared is bundled, not depended on. It is a workspace package that will never exist on npm, so tsup.config.ts lists it under noExternal and the manifest keeps it in devDependencies. Move it back to dependencies and every install resolves fine right up until the first export_blog call. grep -c '@motiblog/shared' dist/*.js must print 0.

  • main differs between the workspace and the tarball. The repo consumes raw TypeScript (src/lib.ts) because apps/api imports this package in process; npm consumers get dist/lib.js. publishConfig performs that swap at publish time, so neither side needs the other's layout.

Smoke-test the tarball the way a customer meets it:

mkdir /tmp/t && cd /tmp/t && npm init -y
npm install /path/to/motiblog-mcp-<version>.tgz
node -e "console.log(Object.keys(require('@motiblog/mcp')))"
MOTIBLOG_HTTP_PORT=3098 node_modules/.bin/motiblog-mcp-http

Listing in the official MCP Registry

server.json in this directory is the listing, validated against schema 2025-12-11. The registry is the canonical directory since 2026 and downstream directories and newsletters ingest it, so one publish propagates — do this before filling in any individual directory form.

The namespace

server.json claims ai.motiblog/mcp — the reverse-DNS namespace for a domain we own, which is the identity a product should have rather than a personal GitHub handle. Claiming it means proving control of motiblog.ai.

The keypair is already generated and the proof is already deployed. The public half is served from the web app at /.well-known/mcp-registry-auth; the private half is at ~/.motiblog/mcp-registry-key (mode 600, never in the repo, same convention as the agent API key).

HTTP verification was chosen over the DNS TXT record deliberately: it lives in this repository, so the proof is reviewable, versioned and deploys with the app instead of depending on a registrar dashboard nobody remembers the login for. The registry accepts either, with the same key — if you ever prefer DNS, the record is:

motiblog.ai.  IN  TXT  "v=MCPv1; k=ed25519; p=<the same public key>"

Publishing

mcp-publisher is a Go binary from github.com/modelcontextprotocol/registry releases (v1.8.1 at time of writing) — it is not on npm.

cd apps/mcp
mcp-publisher login http \
  --domain motiblog.ai \
  --private-key "$(cat ~/.motiblog/mcp-registry-key)"
mcp-publisher publish

The web app must be deployed with the well-known route before the login will verify — the registry fetches https://motiblog.ai/.well-known/mcp-registry-auth and checks the signature against it. Confirm it is live first:

curl https://motiblog.ai/.well-known/mcp-registry-auth
# v=MCPv1; k=ed25519; p=...

If you rotate the key, the constant in the route and the DNS record (if one exists) must change together. A mismatch fails with a signature error that does not explain itself.

mcpName — the pairing the registry enforces

package.json carries "mcpName": "ai.motiblog/mcp", matching name in server.json. This is not optional decoration: the registry refuses to list a server that names an npm package unless that package points back at the listing, because the pairing is how it proves one owner controls both.

Omit it and publishing fails with:

NPM package '@motiblog/mcp' is missing required 'mcpName' field

Since the check reads the published package, a fix means republishing to npm first, then to the registry. Keep the two names in step.

Status

Live at ai.motiblog/mcp. Confirm with:

curl -s 'https://registry.modelcontextprotocol.io/v0.1/servers?search=motiblog'

Validate before publishing — the registry rejects on schema, and the limits are tighter than they look (description caps at 100 characters, which the first draft of ours blew past):

python3 - <<'PY'
import json, urllib.request, jsonschema
s = json.load(urllib.request.urlopen(json.load(open('server.json'))['$schema']))
jsonschema.validate(json.load(open('server.json')), s)
print('valid')
PY

Updating a listing needs a new version. Bump version in both package.json and server.json whenever the copy or the package changes, or the publish is a no-op.

Entry points

FileShapeUse
src/lib.tspure exports, no side effectswhat main points at; what the API imports
src/index.tsexecutes on importstdio server (pnpm start)
src/http.tsexecutable + import-safe server factorystandalone HTTP server (pnpm start:http)

Import the library surface in production code. http.ts is import-safe for transport tests; index.ts remains an executable-only entry point.

The agent loop

list_projects / create_project → suggest_topics → approve_content_plan → generate_article
      → list_review_queue → get_article (factCheckReport, seoScore)
      → update_article (fix flagged claims / supply_product_fact)
      → approve_publication → publish_to_integration …or… export_blog

get_pipeline_logs diagnoses failures; list_refresh_suggestions finds decaying published posts worth refreshing; start_pipeline runs the fully autonomous crawl→plan→generate loop when you want hands-off operation.

Tools

Discovery & system

ToolNotes
list_projectsProjects permitted by the credential + approval/quota settings
create_projectCreate in an available account slot; restricted OAuth grants automatically receive access
get_projectFull pipeline config, positioning inputs
update_projectChange approval/autonomy, language/model, positioning, links and banner settings
start_pipelineAutonomous crawl→plan→generate run
get_pipeline_statusRun state + steps
retry_pipelineRetry a failed run

Topics & planning

ToolNotes
suggest_topicsAdd topic(s) to content plan (DRAFT entries)
list_content_plansPlan queue with statuses
approve_content_planClear a plan entry for generation
regenerate_content_planAI-rewrite of a plan entry
reschedule_content_planMove a plan on the calendar
create_content_planAdd a fully agent-authored plan entry (no AI rewrite)
update_content_planEdit a plan's title/keyword/summary/type/date, pause via DRAFT
bulk_reschedule_content_plansReorder up to 100 plans in one transactional call
delete_content_planSoft-delete ungenerated backlog (restorable)
restore_content_planUndo a soft delete, status and date intact
list_deleted_content_plansAudit what was deleted, find ids to restore
generate_articleFull pipeline from APPROVED plan
generate_programmatic_articlesUp to 500 rows through a title template
get_calendarScheduled entries between two ISO dates

Review loop (the governance core)

ToolNotes
list_review_queueArticles by lifecycle status (default REVIEW)
get_articleFull markdown + factCheckReport, topicGate, seoScore, optional per-phase logs
get_article_feedbackAggregate reader ratings for one article
update_articleEdit content/title/meta/status — how agents answer fact-check flags
approve_publicationGoverned approval gate; publish_now=true publishes immediately
schedule_publicationSet/clear scheduled publish time

Regeneration ops

ToolNotes
regenerate_articleFull rerun (destructive)
regenerate_chapterOne section only (safe)
get_pipeline_logsPhase telemetry: status, tokens, USD cost
list_refresh_suggestionsGSC-driven decay candidates across permitted projects
dismiss_refresh_suggestionRemove an addressed suggestion from the queue
rewrite_textInline editor rewrite actions without mutating the article

Publishing targets

ToolNotes
list_integrationsWEBHOOK/WORDPRESS/GHOST/WEBFLOW/SHOPIFY/DEVTO/SANITY/CUSTOM_API
get_integrationTarget state with credentials redacted
create_integrationCreate any supported provider target
update_integration / delete_integrationEdit, enable/disable, or remove targets
create_webhook_integrationPush target for your own infra; deliveries signed X-Signature: sha256=HMAC(secret)
test_integrationping or full connection test
publish_to_integrationAPPROVED article → PUBLISHING → PUBLISHED
retry_publishRetry failed attempt
mark_integration_syncedRecord a verified manual sync in publish history
list_publish_logsSuccess/failure history with provider errors

Agent-supplied knowledge

ToolNotes
add_keyword / list_keywords / update_keyword / delete_keywordFull keyword lifecycle
list_competitors / add_competitor / delete_competitorCompetitive set management
list_pillar_clustersPillar/member coverage and publication progress
supply_product_fact / list_product_facts / get_product_fact / delete_product_factGround truth enforced by strict fact-checking — supply BEFORE generation
Tool groupNotes
get_brand_voice / get_brand_voice_preview / refresh_brand_voiceInspect and re-extract writing voice
list_authors / get_author / create_author / update_author / delete_authorFull author profiles
get_content_source / update_content_source / detect_content_linksSitemap/blog-root/manual internal-link source
list_content_pages / delete_content_page / get_content_statsContent-intelligence inventory
start_site_crawl / get_crawl_status / list_crawled_pages / get_crawled_page / get_site_analysisWebsite crawling and analysis

Banners and images

Tool groupNotes
upload_article_banner / delete_article_bannerReplace or remove an article banner; pipeline banner generation stays internal
upload_imageImport a public HTTPS image into MotiBlog storage
get_banner_preview / suggest_banner_promptInspect and configure the banner style without exposing raw AI image generation

Google Search Console

Tool groupNotes
get_search_console_status / connect_search_console / list_search_console_properties / select_search_console_property / disconnect_search_consoleConnection lifecycle; Google consent remains a human browser step
sync_search_console / get_search_console_date_range / get_search_console_performanceSync and overview analytics
list_search_console_queries / list_search_console_pages / get_article_position_historyDetailed search performance
list_search_console_insights / dismiss_search_console_insight / get_search_console_action_queueAgent action loop

Account and notifications

Tool groupNotes
get_account_profile / update_account_profileSafe identity fields only; no billing, password, email, key, or OAuth-grant changes
get_account_entitlementRead-only plan/allowance state (no payment identifiers; cannot start a purchase)
list_notifications / get_unread_notification_count / mark_notification_read / mark_all_notifications_readNotification inbox

Export (deploy it yourself)

ToolNotes
export_blogWrites {out_dir}/{slug}/index.md (+ YAML frontmatter incl. motiblogArticleId) and manifest.json. Portable GFM: single H1, <img>→markdown images, iframes→watch links. Drop into Astro/Next/Jekyll/Hugo/plain git and deploy anywhere.

Security model

  • The API key authenticates as the project owner on every existing route — ownership checks are unchanged; there is no privilege escalation.
  • Admin routes refuse API keys (AdminGuard rejects machine callers), even if the owner is an admin.
  • Publication always passes the typed approval gate with provenance — MCP adds no bypass.
  • Payment, password/email changes, API-key and OAuth-grant management, and project/account deletion remain human-only.
  • Keys are per-project and rotatable; rotate immediately if exposed.
  • Rate limiting: global ThrottlerGuard applies unchanged.

Development

pnpm --filter @motiblog/mcp test   # jest unit tests (client envelope handling, export format)
pnpm --filter @motiblog/mcp lint   # tsc --noEmit
pnpm --filter @motiblog/mcp dev    # tsx watch (restart your MCP client after edits)

Keywords

mcp

FAQs

Package last updated on 28 Aug 2026

Related posts