AI agent tools
Expose a Social SDK client to AI agents as JSON Schema tools for the Vercel AI SDK or OpenAI, with dry-run writes by default.
@opencoredev/social-sdk/ai turns a configured client into agent tools. Each tool has a name, a description, a JSON Schema for its arguments, and a call that never throws. The same toolkit backs the MCP server.
import { createSocial } from "@opencoredev/social-sdk";
import { createSocialToolkit } from "@opencoredev/social-sdk/ai";
import { idempotencyStore } from "./stores"; // your durable IdempotencyStore
const social = createSocial({ backend, authorization: policy, idempotencyStore });
const toolkit = createSocialToolkit(social, {
policy, // the same AuthorizationPolicy, for plans, previews and native tools
authorization: { tenantId, principalId },
});
const result = await toolkit.call("social_list_accounts", {});
Tools
The registry reads each backend’s capability manifest and lists a tool only when at least one backend declares its operation available. A backend without messaging never shows social_send_message.
| Tool | Operation | Access |
|---|---|---|
social_capabilities, social_doctor |
none | local |
social_validate, social_plan_publish |
local preparation | local |
social_list_accounts, social_get_account |
accounts.read |
read |
social_list_posts, social_get_post, social_search_posts |
posts.list, posts.get, search.posts |
read |
social_get_delivery |
posts.getDelivery |
read |
social_post_metrics, social_account_metrics, social_analytics_report |
analytics.* |
read |
social_list_comments, social_list_conversations, social_list_messages, social_list_notifications |
comments.read, messages.read, notifications.read |
read |
social_publish, social_publish_sequence |
posts.publish |
write |
social_delete_post |
posts.removeFromPlatform, posts.deleteBackendRecord, posts.cancelScheduled |
write |
social_reply_comment, social_send_message |
comments.write, messages.write |
write |
Pass tools: [...] to expose a subset. Local tools make no provider request.
Writes are previews until confirmed
writes controls every write tool:
"confirm"(default): a write returnsstatus: "dry-run"with a preparation summary, the declared capability, and the policy decision. It sends only when the call includesconfirmWrite: true."preview": writes never send, even withconfirmWrite: true."allow": writes send without confirmation. Use it only when your application runs its own approval step.
Executing social_publish also requires request.idempotencyKey. The key prevents a duplicate only when the client has a durable idempotencyStore that every process shares, as in the setup above: a retry after a timeout then reads the saved claim instead of posting again. Without a store, every confirmed call dispatches again, key or not. The Neon guide shows a Postgres store. Previews report text length and media counts, not the text or URLs.
Treat scheduled, accepted, processing, and unknown outcomes as not published. Reconcile with social_get_delivery before retrying.
Authorization and limits
Every backend call goes through the client facade, so your AuthorizationPolicy, capability checks, and per-backend concurrency limits apply to agent calls exactly as they do to your own code. A context passed per call replaces the toolkit default:
await toolkit.call("social_get_account", { account }, { authorization: session, signal });
The toolkit adds its own safeguards:
maxPageSize(default 50) caps anylimitan agent requests. When the agent omitslimit, the toolkit asks for 20, ormaxPageSizeif it is smaller. Every built-in list operation accepts 20; direct TikTok allows no more.limits: { maxCalls, maxWrites }fails further calls withrate_limitedonce spent.- Failures resolve to
{ ok: false, error }with aSocialErrorcode. Unexpected adapter errors become a genericupstream_failurewithout their message.
Native actions
Native modules bypass the client facade. To expose one, wrap it with defineNativeTool. The toolkit requires policy, authorizes the account with operation native, applies write confirmation, and limits concurrent native calls per backend (nativeConcurrency, default 4).
import { defineNativeTool } from "@opencoredev/social-sdk/ai";
const spacesTool = defineNativeTool({
name: "x_search_spaces",
description: "Search live or scheduled X Spaces by keyword.",
backend: "default",
access: "read",
argsSchema: { type: "object", properties: { query: { type: "string" } }, required: ["query"] },
// Validate args, then call your native client and return JSON.
run: ({ account, args, signal }) => searchSpaces(account, args, signal),
});
createSocialToolkit(social, { policy, nativeTools: [spacesTool] });
The tool is listed as social_native_x_search_spaces. Accounts from other backends are rejected before run.
Vercel AI SDK
toAISDKTools returns a ToolSet. Each input schema is a Standard Schema with a JSON Schema converter, so no adapter package is needed.
import { generateText } from "ai";
import { toAISDKTools } from "@opencoredev/social-sdk/ai";
const { text } = await generateText({
model,
tools: toAISDKTools(toolkit, { authorization: session }),
prompt: "Draft a launch post for our X account and show me the preview.",
});
The model’s abort signal is forwarded to each call.
OpenAI function calling
import { runOpenAIToolCall, toOpenAIResponsesTools } from "@opencoredev/social-sdk/ai";
const response = await openai.responses.create({
model,
input,
tools: toOpenAIResponsesTools(toolkit),
});
const toolOutputs = [];
for (const item of response.output)
if (item.type === "function_call")
toolOutputs.push({
type: "function_call_output",
call_id: item.call_id,
output: await runOpenAIToolCall(toolkit, item, { authorization: session }),
});
toOpenAIChatTools returns the Chat Completions shape. Schemas are not strict-mode schemas, so the tools set strict: false.