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.
One HTTP endpoint, bearer auth, every SocialKit capability exposed as a tool. Drop the config into any MCP-aware client.
# Add the remote MCP server (recommended):
claude mcp add --transport http socialkit \
https://mcp.socialkit.sh \
--header "Authorization: Bearer $SOCIALKIT_KEY"
# Or add it to .mcp.json by hand:
# {
# "mcpServers": {
# "socialkit": {
# "type": "http",
# "url": "https://mcp.socialkit.sh",
# "headers": { "Authorization": "Bearer $SOCIALKIT_KEY" }
# }
# }
# }# ~/.cursor/mcp.json
{
"mcpServers": {
"socialkit": {
"type": "http",
"url": "https://mcp.socialkit.sh",
"headers": { "Authorization": "Bearer $SOCIALKIT_KEY" }
}
}
}# discovery is keyless: read the catalog before you sign up
curl https://mcp.socialkit.sh/tools/list
# tools: score_post, rewrite_post,
# validate_post, generate_post, plan_week,
# build_voice, create_brand,
# update_brand, delete_brand,
# create_voice, update_voice,
# delete_voice, create_post,
# update_post, delete_post,
# schedule_post, bulk_schedule,
# cancel_post, publish_post,
# refresh_metrics, list_posts,
# get_post, score_saved_post,
# get_post_metrics, get_post_scores,
# get_calibration, get_analytics,
# get_calendar, list_brands,
# list_voices, list_channels,
# get_accountscore_postGrade a post 0-100 with a per-dimension breakdown.rewrite_postVoice-preserving rewrite that returns before/after scores.validate_postPreflight a draft: structural errors + reach/voice warnings, no LLM call.generate_postGenerate 1-3 scored drafts from a brief, ranked best-first.plan_weekTurn a brief into a sequenced content plan.build_voiceDistill a reusable voice from sample posts; pass its id to generate/plan.create_brandCreate a brand to ground generation and posts; returns its id.update_brandPatch an existing brand; only the fields you pass change.create_voiceCreate a voice from explicit traits, do-nots, and exemplars; returns its id.update_voicePatch an existing voice; only the fields you pass change.create_postSave a draft so it can be scheduled or published; returns its id.update_postEdit a draft's text, brand, channel, or media; only the fields you pass change.delete_brandDelete a brand by id; future generation can no longer reference it.delete_voiceDelete a voice profile by id; future generation can no longer reference it.delete_postPermanently delete a post and any pending scheduled job; not mid-publish.schedule_postSchedule a saved draft to publish at a future time.bulk_scheduleCreate and schedule a whole planned week in one all-or-nothing call.cancel_postCancel a scheduled post so it won't publish.publish_postPublish a stored draft to its connected channel right now.refresh_metricsPoll the platform now and store a fresh engagement snapshot.list_postsList posts newest-first, filter by status, paginated.get_postFetch a single post by id with its current status and text.get_post_metricsRead a published post's engagement: latest snapshot plus history.get_post_scoresRead a post's recorded score history with full breakdowns.score_saved_postScore a stored post and record it, so it enters the score-vs-reality set.get_calibrationRead score-vs-reality: did predicted scores track actual engagement.get_analyticsAccount-wide engagement rollup, totals and per-platform.get_calendarSee scheduled and published posts within a time window.list_brandsDiscover brand ids to ground generation and posts.list_voicesDiscover saved voice ids to write in a distilled voice.list_channelsDiscover connected channel ids to bind posts to.get_accountConfirm which account and plan the API key is authenticated as.The server also ships prompts, the canonical workflows your client can surface directly. prompts/list returns them; prompts/get fills in your brief.
draft_and_gradeDraft from a brief, then score and rewrite until it clears a target.improve_postGrade an existing post and rewrite the weak dimensions; returns before/after.plan_and_draft_weekTurn a brief into a sequenced week, then draft and save each post.And resources, your account context as attachable, read-only JSON. resources/list enumerates them; resources/read returns the document so your model can ground a draft in your own brand and voice without a tool call.
socialkit://metaThe service contract: score version, supported platforms, media kinds, and dimension weights. Build a valid call without guessing.socialkit://accountIdentity, plan, and what this deployment can do (publish, metrics, channels).socialkit://brandsEvery brand: audience, themes, link policy. Ground a draft without discovery.socialkit://voicesSaved voices and their traits, so the writing matches a distilled voice.socialkit://channelsConnected channels and status, so the model knows what it can publish to.socialkit://calibrationScore-vs-reality dataset: which predicted scores actually translated to reach.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 }) };
}Your model writes from a brief. No scoring constraints.
/v1/score returns 0–100 with ranked signals.
/v1/rewrite fixes weak dimensions, keeps the voice, returns before and after.
Loop until score ≥ target or max attempts. Return both.
One model, two jobs, grounded both times. The edge isn't a secret ranker. It's what the grader is told before it grades.
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.
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.
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.
We can route grading to your own judge or your own ranker. Tell us what you're optimizing for.