Skip to content
Dashboard

Claude & MCP

Brainpercent runs a hosted MCP server. Connect it once and Claude can check your balance, pick a project, turn a URL into branded multi-platform posts, and publish them, without you writing any integration code.

Install in Claude Code

One command. Replace bp_your_key with a key from your developer settings.

Terminal
claude mcp add --transport http brainpercent https://www.brainpercent.app/api/mcp \
  --header "Authorization: Bearer bp_your_key"

Confirm it connected:

Terminal
claude mcp list
# brainpercent: https://www.brainpercent.app/api/mcp (HTTP) - Connected

Scope. claude mcp add writes to the current project by default. Add --scope user to make it available in every project on your machine.

Claude Desktop and other MCP clients

The server speaks Streamable HTTP with a JSON-RPC 2.0 body, so any client that supports remote MCP servers can use the same URL. For clients that take a JSON config file:

MCP client config
{
  "mcpServers": {
    "brainpercent": {
      "type": "http",
      "url": "https://www.brainpercent.app/api/mcp",
      "headers": {
        "Authorization": "Bearer bp_your_key"
      }
    }
  }
}

If your client reserves the Authorization header for its own auth, send the key as X-Brainpercent-Key instead. Both are accepted.

What the server exposes

Six tools. One of them spends credits; the other five are free. Only demo_post_from_url works without a key.

ToolArgumentsCostReturns
check_creditsnone0Wallet balance, generation allowance, plan
list_projectssearch, limit0Your projects with the ids generation accepts
demo_post_from_urlurl (required), platform0One post as text plus a rendered card. Not saved to your account.
generate_postssource_url + platforms (required), source_type, angle, title, project_id1 / platformA content_id to poll. Branded output lands in your dashboard.
get_contentcontent_id (required)0Status plus the caption, first comment and image per platform
publishcontent_id (required), platforms, scheduled_at0Queues the post to your connected accounts, or schedules it

Ask Claude for the list yourself, or check the wire format directly:

Terminal
curl -sX POST https://www.brainpercent.app/api/mcp \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
200tools/list
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "tools": [
      {
        "name": "check_credits",
        "inputSchema": {
          "type": "object",
          "properties": {}
        }
      },
      {
        "name": "list_projects",
        "inputSchema": {
          "type": "object",
          "properties": {
            "search": {
              "type": "string"
            },
            "limit": {
              "type": "integer"
            }
          }
        }
      },
      {
        "name": "demo_post_from_url",
        "inputSchema": {
          "type": "object",
          "properties": {
            "url": {
              "type": "string"
            },
            "platform": {
              "type": "string",
              "enum": [
                "linkedin",
                "instagram",
                "facebook",
                "twitter",
                "threads"
              ]
            }
          },
          "required": [
            "url"
          ]
        }
      },
      {
        "name": "generate_posts",
        "inputSchema": {
          "type": "object",
          "properties": {
            "source_url": {
              "type": "string"
            },
            "platforms": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "instagram",
                  "facebook",
                  "linkedin",
                  "twitter",
                  "threads",
                  "tiktok",
                  "bluesky",
                  "pinterest",
                  "google_business",
                  "telegram"
                ]
              }
            },
            "source_type": {
              "type": "string",
              "enum": [
                "website",
                "blog",
                "youtube"
              ]
            },
            "angle": {
              "type": "string"
            },
            "title": {
              "type": "string"
            },
            "project_id": {
              "type": "string"
            }
          },
          "required": [
            "source_url",
            "platforms"
          ]
        }
      },
      {
        "name": "get_content",
        "inputSchema": {
          "type": "object",
          "properties": {
            "content_id": {
              "type": "string"
            }
          },
          "required": [
            "content_id"
          ]
        }
      },
      {
        "name": "publish",
        "inputSchema": {
          "type": "object",
          "properties": {
            "content_id": {
              "type": "string"
            },
            "platforms": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "scheduled_at": {
              "type": "string"
            }
          },
          "required": [
            "content_id"
          ]
        }
      }
    ]
  }
}

A worked example per tool

In Claude you just ask in English and it picks the tool. The raw JSON-RPC below is what goes over the wire, so you can drive the same server from any client. Every call carries your key in the Authorization header; nothing takes a user id, because the server derives the account from the key.

1. check_credits

Ask Claude: "How many Brainpercent credits do I have?"

check_credits
curl -sX POST https://www.brainpercent.app/api/mcp \
  -H "Authorization: Bearer bp_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": 1, "method": "tools/call",
    "params": { "name": "check_credits", "arguments": {} }
  }'

2. list_projects

Ask Claude: "Which Brainpercent projects do I have?" Each project carries its own brand voice, language and template, so the id you pass to generate_posts decides how the posts sound.

list_projects
curl -sX POST https://www.brainpercent.app/api/mcp \
  -H "Authorization: Bearer bp_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": 2, "method": "tools/call",
    "params": {
      "name": "list_projects",
      "arguments": { "search": "studio", "limit": 5 }
    }
  }'

3. demo_post_from_url

The one tool that needs no key. One post, not saved to any account, capped at one per day per IP.

demo_post_from_url
curl -sX POST https://www.brainpercent.app/api/mcp \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": 3, "method": "tools/call",
    "params": {
      "name": "demo_post_from_url",
      "arguments": {
        "url": "https://example.com/blog/launch",
        "platform": "linkedin"
      }
    }
  }'

4. generate_posts spends credits

Ask Claude: "Turn https://example.com/blog/launch into LinkedIn and Instagram posts for REI Studio". Three platforms costs 1.8 credits. The key needs the write permission.

generate_posts
curl -sX POST https://www.brainpercent.app/api/mcp \
  -H "Authorization: Bearer bp_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": 4, "method": "tools/call",
    "params": {
      "name": "generate_posts",
      "arguments": {
        "source_url": "https://example.com/blog/launch",
        "platforms": ["linkedin", "instagram", "twitter"],
        "angle": "lead with the pricing change",
        "project_id": "fae81600-ba99-4ef7-b2cf-d93566f16ef8"
      }
    }
  }'

5. get_content

Generation is asynchronous. Poll this until the status is ready or completed.

get_content
curl -sX POST https://www.brainpercent.app/api/mcp \
  -H "Authorization: Bearer bp_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": 5, "method": "tools/call",
    "params": {
      "name": "get_content",
      "arguments": { "content_id": "8629552b-51b5-47b0-acbc-3ab2c3f2029a" }
    }
  }'

6. publish

Ask Claude: "Publish it" or "Schedule it for Monday 9am". Publishing is free; you already paid at generation. You need at least one connected account and a key with write.

publish
curl -sX POST https://www.brainpercent.app/api/mcp \
  -H "Authorization: Bearer bp_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0", "id": 6, "method": "tools/call",
    "params": {
      "name": "publish",
      "arguments": {
        "content_id": "8629552b-51b5-47b0-acbc-3ab2c3f2029a",
        "platforms": ["linkedin"]
      }
    }
  }'

Limits and auth

  • Without a key: demo_post_from_url only, one generation per day per IP address. Every other tool answers with a link to create a key.
  • With a key: your account's normal limits apply. Read tools need the read permission; generate_posts and publish need write.
  • Everything is scoped to the key. No tool takes a user id. The account is derived from the key server-side, and a project_id or content_id you did not create simply reads as not found.
  • The server is stateless. There is no session to establish and nothing to keep alive.
  • Keys are shown once at creation. If you lose one, revoke it and create another at /chat/developers.

Building your own tools on the REST API

If you are calling the Anthropic API directly rather than using MCP, define the endpoints as tools. These schemas mirror what the OpenAPI spec declares, so they will not drift from the live routes.

Tool definitions
[
  {
    "name": "list_projects",
    "description": "List the user's projects. Every generation needs a project_id, so call this first.",
    "input_schema": {
      "type": "object",
      "properties": {
        "search": { "type": "string", "description": "Filter by business name" }
      }
    }
  },
  {
    "name": "generate_social_content",
    "description": "Turn a URL into platform-tailored posts. Asynchronous: returns a content_id to poll. Costs 0.6 credits per platform.",
    "input_schema": {
      "type": "object",
      "properties": {
        "source_url": { "type": "string", "description": "Public URL to summarize" },
        "platforms": {
          "type": "array",
          "items": {
            "type": "string",
            "enum": ["instagram", "facebook", "linkedin", "twitter", "threads", "tiktok", "bluesky", "pinterest", "google_business", "telegram"]
          }
        },
        "angle": { "type": "string", "description": "Optional direction hint for the prompt" },
        "project_id": { "type": "string", "description": "Defaults to the active project" }
      },
      "required": ["source_url", "platforms"]
    }
  },
  {
    "name": "get_social_content",
    "description": "Read one social content item. The captions are in 'platforms', keyed by platform name. Poll until status is 'ready' or 'completed'.",
    "input_schema": {
      "type": "object",
      "properties": {
        "content_id": { "type": "string" }
      },
      "required": ["content_id"]
    }
  },
  {
    "name": "generate_article",
    "description": "Generate an SEO article. Asynchronous, roughly 10-15 minutes. Costs 1.5 credits.",
    "input_schema": {
      "type": "object",
      "properties": {
        "topic": { "type": "string", "description": "Main keyword, 3-500 characters" },
        "project_id": { "type": "string", "description": "Required. Supplies brand and language context." },
        "keywords": { "type": "array", "items": { "type": "string" } },
        "tone": { "type": "string", "enum": ["professional", "casual", "academic", "conversational"] },
        "word_count": { "type": "integer", "minimum": 300, "maximum": 5000 }
      },
      "required": ["topic", "project_id"]
    }
  },
  {
    "name": "check_article_status",
    "description": "Poll article generation. Status 'cms' or 'needs_editing' means finished. There is no 'published' status.",
    "input_schema": {
      "type": "object",
      "properties": {
        "article_id": { "type": "string" }
      },
      "required": ["article_id"]
    }
  },
  {
    "name": "check_credits",
    "description": "Read the current credit balance and plan.",
    "input_schema": { "type": "object", "properties": {} }
  }
]

Tool execution handler

executeTool
const BP_BASE = 'https://www.brainpercent.app/api/v1';
const headers = {
  'Authorization': `Bearer ${process.env.BRAINPERCENT_API_KEY}`,
  'Content-Type': 'application/json',
};

// Note the www. The apex domain 307-redirects, and a redirected POST
// silently drops its body in most HTTP clients.

async function executeTool(name, input) {
  switch (name) {
    case 'list_projects': {
      const qs = input.search ? `?search=${encodeURIComponent(input.search)}` : '';
      const r = await fetch(`${BP_BASE}/projects${qs}`, { headers });
      return r.json();
    }

    case 'generate_social_content': {
      const r = await fetch(`${BP_BASE}/social/generate`, {
        method: 'POST',
        headers,
        body: JSON.stringify(input),
      });
      return r.json(); // 202 { data: { content_id, poll_url, credits_remaining } }
    }

    case 'get_social_content': {
      const r = await fetch(`${BP_BASE}/social/content/${input.content_id}`, { headers });
      return r.json();
    }

    case 'generate_article': {
      const r = await fetch(`${BP_BASE}/articles/generate`, {
        method: 'POST',
        headers,
        body: JSON.stringify(input),
      });
      return r.json(); // 202 { data: { article_id, poll_url } }
    }

    case 'check_article_status': {
      const r = await fetch(`${BP_BASE}/articles/${input.article_id}/status`, { headers });
      return r.json();
    }

    case 'check_credits': {
      const r = await fetch(`${BP_BASE}/user/credits`, { headers });
      return r.json();
    }

    default:
      return { error: `Unknown tool: ${name}` };
  }
}

Full conversation loop

Anthropic SDK
import Anthropic from '@anthropic-ai/sdk';

const anthropic = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

async function run(userMessage) {
  const messages = [{ role: 'user', content: userMessage }];

  while (true) {
    const response = await anthropic.messages.create({
      model: 'claude-sonnet-5',
      max_tokens: 2048,
      tools: BRAINPERCENT_TOOLS,
      messages,
    });

    messages.push({ role: 'assistant', content: response.content });

    if (response.stop_reason !== 'tool_use') {
      return response.content.find((b) => b.type === 'text')?.text ?? '';
    }

    const results = [];
    for (const block of response.content) {
      if (block.type !== 'tool_use') continue;
      const output = await executeTool(block.name, block.input);
      results.push({
        type: 'tool_result',
        tool_use_id: block.id,
        content: JSON.stringify(output),
      });
    }

    messages.push({ role: 'user', content: results });
  }
}

Prompt examples

Costs are in credits. One credit is one dollar of your single account balance.

PromptTools usedCost
"Turn this blog post into LinkedIn and Instagram posts"list_projects, generate_social_content, get_social_content1.2
"Write an article about AI trends"generate_article, check_article_status1.5
"How many credits do I have left?"check_credits0

Things that will bite you

  • Use www.brainpercent.app. The apex domain 307-redirects and most HTTP clients drop the POST body on redirect.
  • Generation is asynchronous. Both generate endpoints return 202 with a poll_url. Nothing is ready at the moment the call returns.
  • A finished article has status cms, not published.
  • Social captions live in the platforms object, keyed by platform. There is no content field.
  • Give Claude check_credits so it can check the balance before spending.
  • Over MCP, generate_posts is the only tool that costs anything, at 0.6 credits per platform, charged the moment the job is queued and refunded automatically if the job fails to start. Asking for ten platforms costs 6 credits.
  • demo_post_from_url is a preview. It does not save anything to your account, so you cannot publish its output. Use generate_posts for anything you intend to post.