AI · live

Built for agents.

SocialKit speaks MCP, returns structured JSON, and is designed to live inside a loop. Score, rewrite, regrade, ship. No prompt gymnastics, no JSON-mode bandaids.

MCP server

14 tools

One HTTP endpoint, bearer auth, every SocialKit capability exposed as a tool. Drop the config into any MCP-aware client.

# ~/.claude/config.json
{
  "mcpServers": {
    "socialkit": {
      "url": "https://mcp.socialkit.sh",
      "auth": { "type": "bearer", "token": "$SOCIALKIT_KEY" }
    }
  }
}
# ~/.cursor/mcp.json
{
  "mcpServers": {
    "socialkit": {
      "type": "http",
      "url": "https://mcp.socialkit.sh",
      "headers": { "Authorization": "Bearer $SOCIALKIT_KEY" }
    }
  }
}
curl https://mcp.socialkit.sh/tools/list \
  -H "Authorization: Bearer $SOCIALKIT_KEY"

# tools: score_post, rewrite_post,
#        generate_post, plan_week,
#        build_voice, create_post,
#        schedule_post, cancel_post,
#        list_posts, get_post,
#        get_calendar, list_brands,
#        list_voices, list_channels

Agents

draft → grade → rewrite

The pattern that actually works: let your model draft freely, then use SocialKit as the judge and the rewriter. Three attempts is usually enough to land 80+.

import Anthropic from "@anthropic-ai/sdk";

const claude = new Anthropic();

// SocialKit is plain JSON over HTTPS. No SDK to install.
async function sk(path: string, body: unknown) {
  const res = await fetch(`https://api.socialkit.sh/v1/${path}`, {
    method: "POST",
    headers: {
      "content-type": "application/json",
      authorization: `Bearer ${process.env.SOCIALKIT_KEY}`,
    },
    body: JSON.stringify(body),
  });
  if (!res.ok) throw new Error(`socialkit ${path}: ${res.status}`);
  return res.json();
}

async function draftAndGrade(brief: string) {
  let draft = await claude.messages.create({
    model: "claude-sonnet-4-6",
    max_tokens: 800,
    messages: [{ role: "user", content: `Write a LinkedIn post: ${brief}` }],
  }).then(r => r.content[0].text);

  for (let attempt = 0; attempt < 3; attempt++) {
    const grade = await sk("score", { post: draft });
    if (grade.overall >= 80) return { draft, grade };

    const { rewrite, after } = await sk("rewrite", { post: draft });
    draft = rewrite;
    if (after.overall >= 80) return { draft, grade: after };
  }
  return { draft, grade: await sk("score", { post: draft }) };
}
1

Draft

Your model writes from a brief. No scoring constraints.

2

Grade

/v1/score returns 0–100 with ranked signals.

3

Rewrite

/v1/rewrite fixes weak dimensions, keeps the voice, returns before and after.

4

Ship

Loop until score ≥ target or max attempts. Return both.

Models

why this stack

One model, two jobs, grounded both times. The edge isn't a secret ranker. It's what the grader is told before it grades.

Sonnet 4.6judge

The grader is Claude Sonnet run as a judge, with forced tool use so every score comes back as typed JSON, not prose. It does not free-associate. It scores against a written evidence base of how the feed actually ranks.

  • Scores hook, format fit, dwell and save signals, voice.
  • Runs via Cloudflare AI Gateway, cached 30 days per input.
  • Same evidence base our own writers post against weekly.
Sonnet 4.6writer

Claude Sonnet also writes the drafts and the rewrites. It's the best public model at voice mimicry and tight first-person prose. Different prompt, different priors.

  • Prompt-tuned per voice profile.
  • Routed through the same AI Gateway.
  • Re-grade after every rewrite, always.

Where this goes next: a calibrated ranker trained on real outcomes, scoring dwell and reshare likelihood directly. That's on the roadmap, not in the box yet. We'd rather tell you what runs today than ship a number we can't stand behind.

Want a different model in the loop?

We can route grading to your own judge or your own ranker. Tell us what you're optimizing for.

hello@socialkit.sh