@postdom/mcp
Version:
Postdom MCP server for agent-led social publishing and performance measurement.
770 lines (761 loc) • 26.9 kB
JavaScript
// src/index.ts
import { randomUUID } from "crypto";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
// ../core/src/approval.ts
import { z as z2 } from "zod";
// ../core/src/platform.ts
import { z } from "zod";
var corePlatformSchema = z.enum(["tiktok", "instagram", "youtube"]);
var policyVisibilityOptions = {
tiktok: [
"PUBLIC_TO_EVERYONE",
"MUTUAL_FOLLOW_FRIENDS",
"FOLLOWER_OF_CREATOR",
"SELF_ONLY"
],
instagram: ["account_default"],
youtube: ["public", "private", "unlisted"]
};
var policyVisibilitySchema = z.enum([
...policyVisibilityOptions.tiktok,
...policyVisibilityOptions.instagram,
...policyVisibilityOptions.youtube
]);
var PLATFORM_LIMITS_VERIFIED_AT = "2026-08-21T00:00:00.000Z";
var PLATFORM_LIMITS_EVIDENCE = "ARCHITECTURE.md#4-data-model";
function verifiedLimit(value) {
return {
value,
verified_at: PLATFORM_LIMITS_VERIFIED_AT,
evidence: PLATFORM_LIMITS_EVIDENCE
};
}
var platformLimits = {
global: {
caption_max_chars: verifiedLimit(4e3),
media_max_bytes: verifiedLimit(500 * 1024 * 1024),
video_max_seconds: verifiedLimit(10 * 60),
video_mime_types: verifiedLimit(["video/mp4", "video/quicktime"])
},
tiktok: {
video_min_seconds: verifiedLimit(3),
video_max_seconds: verifiedLimit(10 * 60)
},
instagram: {
caption_max_chars: verifiedLimit(2200),
video_max_seconds: verifiedLimit(90)
},
youtube: {
title_max_chars: verifiedLimit(100),
video_max_seconds: verifiedLimit(3 * 60),
aspect_ratio: verifiedLimit("9:16")
}
};
var tiktokSettingsSchema = z.object({
privacy_level: z.enum([
"PUBLIC_TO_EVERYONE",
"MUTUAL_FOLLOW_FRIENDS",
"FOLLOWER_OF_CREATOR",
"SELF_ONLY"
]),
allow_comment: z.boolean(),
allow_duet: z.boolean(),
allow_stitch: z.boolean(),
content_preview_confirmed: z.literal(true),
express_consent_given: z.literal(true),
video_made_with_ai: z.boolean().default(true)
});
var instagramSettingsSchema = z.object({
contentType: z.literal("reel").default("reel"),
isAiGenerated: z.boolean().default(true)
});
var youtubeSettingsSchema = z.object({
title: z.string().min(1).max(platformLimits.youtube.title_max_chars.value),
visibility: z.enum(["public", "private", "unlisted"]).default("private"),
madeForKids: z.boolean(),
containsSyntheticMedia: z.boolean().default(true)
});
var platformTargetSchema = z.discriminatedUnion("platform", [
z.object({
account_id: z.string().min(1),
platform: z.literal("tiktok"),
settings: tiktokSettingsSchema
}),
z.object({
account_id: z.string().min(1),
platform: z.literal("instagram"),
settings: instagramSettingsSchema
}),
z.object({
account_id: z.string().min(1),
platform: z.literal("youtube"),
settings: youtubeSettingsSchema
})
]);
var publishRequestSchema = z.object({
post_id: z.uuid(),
caption: z.string().max(platformLimits.global.caption_max_chars.value),
media_url: z.url(),
targets: z.array(platformTargetSchema).min(1),
idempotency_key: z.string().min(8).max(255)
});
// ../core/src/approval.ts
var agentContextSchema = z2.object({
identity: z2.string().trim().min(1).max(120),
intent: z2.string().trim().min(1).max(500)
});
var approvalFeedbackCategorySchema = z2.enum([
"caption",
"media",
"targeting",
"timing",
"policy",
"other"
]);
var approvalFeedbackSchema = z2.object({
category: approvalFeedbackCategorySchema,
text: z2.string().trim().min(1).max(1e3)
});
var approvalDecisionSchema = z2.enum(["changes_requested", "rejected"]);
var approvalDestinationSchema = z2.object({
account_id: z2.string().min(1),
platform: corePlatformSchema,
handle: z2.string().nullable(),
settings: z2.record(z2.string(), z2.unknown())
});
var planStatusSchema = z2.enum([
"requires_approval",
"approved",
"changes_requested",
"rejected",
"expired",
"cancelled"
]);
var planAuthorizationStatusSchema = z2.enum(["applied", "not_applied"]);
var planAuthorizationReasonSchema = z2.enum([
"agent_paused",
"publishing_disabled",
"plan_not_found",
"plan_not_approved",
"plan_not_started",
"plan_expired",
"plan_cancelled",
"target_not_in_plan",
"target_not_l2",
"target_disconnected",
"policy_not_configured",
"policy_violation",
"max_posts_exhausted"
]);
// ../core/src/media.ts
import { z as z3 } from "zod";
var MEDIA_UPLOAD_URL_TTL_SECONDS = 5 * 60;
var MEDIA_UPLOAD_MAX_BYTES_PER_HOUR = platformLimits.global.media_max_bytes.value * 4;
var mediaHandleSchema = z3.string().regex(
/^pd_media_[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
"media_handle must be a Postdom media handle"
);
var mediaUploadRequestSchema = z3.object({
content_type: z3.enum(platformLimits.global.video_mime_types.value),
size_bytes: z3.number().int().positive().max(platformLimits.global.media_max_bytes.value),
platforms: z3.array(corePlatformSchema).min(1).max(3)
}).superRefine((value, context) => {
if (new Set(value.platforms).size !== value.platforms.length) {
context.addIssue({
code: "custom",
path: ["platforms"],
message: "Target platforms must be unique"
});
}
});
var mediaUploadResponseSchema = z3.object({
media_handle: mediaHandleSchema,
upload_url: z3.url(),
method: z3.literal("PUT"),
headers: z3.object({
"Content-Type": z3.string(),
"Content-Length": z3.string().regex(/^\d+$/)
}),
expires_at: z3.iso.datetime()
});
var mediaUploadStatusSchema = z3.enum(["pending", "stored", "failed"]);
var mediaStatusResponseSchema = z3.object({
media_handle: mediaHandleSchema,
status: mediaUploadStatusSchema,
content_type: z3.enum(platformLimits.global.video_mime_types.value),
size_bytes: z3.number().int().positive(),
media_url: z3.url().nullable(),
failure_reason: z3.string().nullable()
});
var publishMediaSourceSchema = z3.object({
video_url: z3.url().optional(),
media_handle: mediaHandleSchema.optional()
}).superRefine((value, context) => {
if (Number(Boolean(value.video_url)) + Number(Boolean(value.media_handle)) !== 1) {
context.addIssue({
code: "custom",
path: ["media_handle"],
message: "Provide exactly one of media_handle or video_url"
});
}
});
var postMediaSourceSchema = z3.object({
media_url: z3.url().optional(),
media_handle: mediaHandleSchema.optional()
}).superRefine((value, context) => {
if (Number(Boolean(value.media_url)) + Number(Boolean(value.media_handle)) !== 1) {
context.addIssue({
code: "custom",
path: ["media_handle"],
message: "Provide exactly one of media_handle or media_url"
});
}
});
var mediaOperations = {
createUpload: {
method: "POST",
path: "/media/uploads",
request: mediaUploadRequestSchema,
response: mediaUploadResponseSchema
},
getUpload: {
method: "GET",
path: "/media/{media_handle}",
params: z3.object({ media_handle: mediaHandleSchema }),
response: mediaStatusResponseSchema
}
};
// ../core/src/performance.ts
import { z as z4 } from "zod";
var metricAvailabilitySchema = z4.enum([
"available",
"delayed(2-3d)",
"estimable",
"never",
"unverified"
]);
var performanceMetricSchema = z4.enum([
"views",
"likes",
"comments",
"shares",
"saves",
"watch_time_s",
"avg_watch_pct",
"completion_pct",
"follower_delta"
]);
var BASELINE_VERIFIED_AT = "2026-08-21T00:00:00.000Z";
var METRIC_EVIDENCE = "docs/verification/metric-availability.md";
function verified(state, verifiedAt = BASELINE_VERIFIED_AT) {
return { state, verified_at: verifiedAt, evidence: METRIC_EVIDENCE };
}
function unverified() {
return { state: "unverified", verified_at: null, evidence: METRIC_EVIDENCE };
}
var metricAvailabilityMetadata = {
tiktok: {
views: verified("available"),
likes: verified("available"),
comments: verified("available"),
shares: verified("available"),
saves: unverified(),
watch_time_s: verified("never"),
avg_watch_pct: verified("never"),
completion_pct: verified("never"),
follower_delta: verified("estimable")
},
instagram: {
views: verified("available"),
likes: verified("available"),
comments: verified("available"),
shares: verified("available"),
saves: unverified(),
watch_time_s: verified("available"),
avg_watch_pct: verified("estimable"),
completion_pct: verified("estimable"),
follower_delta: verified("estimable")
},
youtube: {
views: verified("available"),
likes: verified("available"),
comments: verified("available"),
shares: verified("available"),
saves: verified("never"),
watch_time_s: verified("available"),
avg_watch_pct: verified("delayed(2-3d)"),
completion_pct: verified("delayed(2-3d)"),
follower_delta: verified("estimable")
}
};
var metricAvailability = Object.fromEntries(
Object.entries(metricAvailabilityMetadata).map(([platform, metrics]) => [
platform,
Object.fromEntries(
Object.entries(metrics).map(([metric, metadata]) => [metric, metadata.state])
)
])
);
var nullableMetric = z4.number().nonnegative().nullable();
var postPerformanceSchema = z4.object({
views: z4.number().nonnegative(),
likes: z4.number().nonnegative(),
comments: z4.number().nonnegative(),
shares: z4.number().nonnegative(),
saves: z4.number().nonnegative().nullable(),
watch_time_s: nullableMetric,
avg_watch_pct: nullableMetric,
completion_pct: nullableMetric,
follower_delta: z4.number().nullable(),
captured_at: z4.iso.datetime(),
platform: corePlatformSchema,
source: z4.enum(["webhook", "poll"]),
change_since_last: z4.record(z4.string(), z4.number().nullable()).optional()
});
// ../core/src/webhooks.ts
import { z as z5 } from "zod";
var outboundWebhookEventTypeSchema = z5.enum([
"post.published",
"post.failed",
"performance.updated"
]);
var outboundWebhookEndpointCreateSchema = z5.object({
url: z5.url(),
events: z5.array(outboundWebhookEventTypeSchema).min(1).max(3)
}).superRefine((value, context) => {
if (new Set(value.events).size !== value.events.length) {
context.addIssue({
code: "custom",
path: ["events"],
message: "Webhook event types must be unique"
});
}
});
var outboundWebhookEndpointUpdateSchema = z5.object({
url: z5.url().optional(),
events: z5.array(outboundWebhookEventTypeSchema).min(1).max(3).optional(),
enabled: z5.boolean().optional()
}).refine((value) => Object.keys(value).length > 0, {
message: "At least one webhook endpoint field is required"
}).superRefine((value, context) => {
if (value.events && new Set(value.events).size !== value.events.length) {
context.addIssue({
code: "custom",
path: ["events"],
message: "Webhook event types must be unique"
});
}
});
var outboundWebhookEndpointSchema = z5.object({
id: z5.uuid(),
url: z5.url(),
events: z5.array(outboundWebhookEventTypeSchema),
enabled: z5.boolean(),
created_at: z5.iso.datetime(),
updated_at: z5.iso.datetime()
});
var outboundWebhookEndpointCreatedSchema = outboundWebhookEndpointSchema.extend({
signing_secret: z5.string().startsWith("whsec_")
});
var outboundWebhookDeliveryStateSchema = z5.enum([
"pending",
"sending",
"retry",
"delivered",
"exhausted",
"cancelled"
]);
var outboundWebhookDeliverySchema = z5.object({
id: z5.uuid(),
event_id: z5.uuid(),
event_type: outboundWebhookEventTypeSchema,
endpoint_id: z5.uuid(),
state: outboundWebhookDeliveryStateSchema,
attempts: z5.number().int().nonnegative(),
next_attempt_at: z5.iso.datetime(),
delivered_at: z5.iso.datetime().nullable(),
last_http_status: z5.number().int().nullable(),
last_error: z5.string().nullable(),
created_at: z5.iso.datetime()
});
// src/index.ts
import { z as z6 } from "zod";
var POSTDOM_MCP_VERSION = "0.3.0";
var performanceMetricSchema2 = z6.enum([
"views",
"likes",
"comments",
"shares",
"saves",
"watch_time_s",
"avg_watch_pct",
"completion_pct",
"follower_delta"
]);
var POSTDOM_MCP_TOOL_NAMES = [
"get_workspace_status",
"get_brief",
"get_digest",
"list_accounts",
"connect_account",
"upload_media",
"get_media",
"publish_video",
"submit_plan",
"get_plan",
"get_publish",
"get_performance",
"get_best_posts"
];
var PostdomClient = class {
constructor(options) {
this.options = options;
this.baseUrl = options.baseUrl ?? "https://api.postdom.com/v1";
this.fetch = options.fetch ?? globalThis.fetch;
}
options;
baseUrl;
fetch;
async request(path, init = {}) {
const headers = new Headers(init.headers);
headers.set("Authorization", `Bearer ${this.options.apiKey}`);
headers.set("Accept", "application/json");
if (init.body) headers.set("Content-Type", "application/json");
const response = await this.fetch(`${this.baseUrl}${path}`, { ...init, headers });
const payload = await response.json();
if (!response.ok) {
const problem = typeof payload === "object" && payload !== null ? payload : {};
const code = typeof problem.code === "string" ? ` ${problem.code}` : "";
const detail = typeof problem.detail === "string" ? `: ${problem.detail}` : "";
throw new Error(`Postdom API request failed with HTTP ${response.status}${code}${detail}`);
}
return payload;
}
async listAccounts() {
const response = await this.request("/accounts");
return response.accounts;
}
getWorkspaceStatus() {
return this.request("/workspace/status");
}
connectAccount(platform) {
return this.request("/accounts/connect", {
method: "POST",
body: JSON.stringify({
platform,
redirect_url: "https://app.postdom.com/accounts"
})
});
}
async createMediaUpload(input) {
const request = mediaUploadRequestSchema.parse(input);
const response = await this.request(mediaOperations.createUpload.path, {
method: mediaOperations.createUpload.method,
body: JSON.stringify(request)
});
return mediaUploadResponseSchema.parse(response);
}
async getMedia(mediaHandle) {
const { media_handle } = mediaOperations.getUpload.params.parse({
media_handle: mediaHandle
});
const response = await this.request(
mediaOperations.getUpload.path.replace(
"{media_handle}",
encodeURIComponent(media_handle)
)
);
return mediaStatusResponseSchema.parse(response);
}
async publishVideo(input) {
const mediaSource = publishMediaSourceSchema.parse({
video_url: input.videoUrl,
media_handle: input.mediaHandle
});
const accounts = await this.listAccounts();
const byId = new Map(accounts.map((account) => [account.providerAccountId, account]));
const targets = input.accountIds.map((accountId) => {
const account = byId.get(accountId);
if (!account) throw new Error(`Postdom account ${accountId} is not connected`);
return defaultTarget(account.platform, accountId, input.caption);
});
return this.request("/posts", {
method: "POST",
headers: {
"Idempotency-Key": input.idempotencyKey ?? randomUUID(),
"X-Postdom-Source": "mcp"
},
body: JSON.stringify({
caption: input.caption,
...mediaSource.video_url ? { media_url: mediaSource.video_url } : {},
...mediaSource.media_handle ? { media_handle: mediaSource.media_handle } : {},
publish_at: input.publishAt,
plan_id: input.planId,
targets,
agent_context: {
identity: this.options.agentIdentity ?? input.agentIdentity ?? "AI agent via MCP",
intent: input.intent
}
})
});
}
async submitPlan(input) {
const accounts = await this.listAccounts();
const byId = new Map(accounts.map((account) => [account.providerAccountId, account]));
const targets = input.accountIds.map((accountId) => {
const account = byId.get(accountId);
if (!account) throw new Error(`Postdom account ${accountId} is not connected`);
return { account_id: accountId, platform: account.platform };
});
return this.request("/plans", {
method: "POST",
headers: {
"Idempotency-Key": input.idempotencyKey ?? randomUUID(),
"X-Postdom-Source": "mcp"
},
body: JSON.stringify({
title: input.title,
objective: input.objective,
starts_at: input.startsAt,
ends_at: input.endsAt,
max_posts: input.maxPosts,
brief_version: input.briefVersion,
targets,
agent_context: {
identity: this.options.agentIdentity ?? input.agentIdentity ?? "AI agent via MCP",
intent: input.intent
}
})
});
}
getPlan(planId) {
return this.request(`/plans/${encodeURIComponent(planId)}`);
}
getBrief() {
return this.request("/brief");
}
getDigest() {
return this.request("/digest");
}
getPublish(postId) {
return this.request(`/posts/${encodeURIComponent(postId)}`);
}
getPostPerformance(postId) {
return this.request(`/posts/${encodeURIComponent(postId)}/performance`);
}
getAccountPerformance(accountId, window) {
return this.request(
`/accounts/${encodeURIComponent(accountId)}/performance?window=${window}`
);
}
getBestPosts(accountId, metric, window) {
return this.request(
`/accounts/${encodeURIComponent(accountId)}/best-posts?metric=${encodeURIComponent(metric)}&window=${window}`
);
}
};
function defaultTarget(platform, accountId, caption) {
if (platform === "tiktok") {
return {
account_id: accountId,
platform,
settings: {
privacy_level: "SELF_ONLY",
allow_comment: false,
allow_duet: false,
allow_stitch: false,
content_preview_confirmed: true,
express_consent_given: true,
video_made_with_ai: true
}
};
}
if (platform === "instagram") {
return {
account_id: accountId,
platform,
settings: { contentType: "reel", isAiGenerated: true }
};
}
if (platform === "youtube") {
return {
account_id: accountId,
platform,
settings: {
title: caption.split("\n")[0]?.slice(0, 100) || "Postdom video",
visibility: "private",
madeForKids: false,
containsSyntheticMedia: true
}
};
}
throw new Error(`publish_video does not support ${platform} in M2`);
}
function toolResult(value) {
return {
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
structuredContent: value
};
}
function createPostdomMcpServer(client) {
const server = new McpServer({ name: "postdom", version: POSTDOM_MCP_VERSION });
server.registerTool("get_workspace_status", {
title: "Get workspace status",
description: "Start here. Read connected-account health, autonomy and policy state, brief version, activity counts, and the connection gate.",
inputSchema: {},
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}, async () => toolResult(await client.getWorkspaceStatus()));
server.registerTool("get_brief", {
title: "Get workspace brief",
description: "Read the current workspace-owned brand guidance before planning or writing content.",
inputSchema: {},
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}, async () => toolResult(await client.getBrief()));
server.registerTool("get_digest", {
title: "Get latest learning digest",
description: "Read the latest completed weekly workspace digest before planning. It describes observed outcomes, populations, cadence endings, and coverage gaps; it does not recommend actions.",
inputSchema: {},
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}, async () => toolResult(await client.getDigest()));
server.registerTool("list_accounts", {
title: "List connected accounts",
description: "List the destination accounts available inside this workspace.",
inputSchema: {},
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}, async () => toolResult({ accounts: await client.listAccounts() }));
server.registerTool("connect_account", {
title: "Connect social account",
description: "Create a platform OAuth URL. Hand the returned URL to the human; never ask for or handle their social password.",
inputSchema: { platform: z6.enum(["tiktok", "instagram", "youtube"]) },
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true }
}, async ({ platform }) => toolResult(await client.connectAccount(platform)));
server.registerTool("upload_media", {
title: "Upload media",
description: "Create a short-lived, workspace-scoped PUT URL for finished video bytes. Upload the exact declared bytes directly to the returned URL, then call get_media until the handle is stored.",
inputSchema: mediaUploadRequestSchema.shape,
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true }
}, async (input) => toolResult(await client.createMediaUpload(input)));
server.registerTool("get_media", {
title: "Get media",
description: "Verify one workspace media handle after its direct PUT. Continue only when status is stored; preserve pending or failed exactly.",
inputSchema: { media_handle: mediaHandleSchema },
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}, async ({ media_handle }) => toolResult(await client.getMedia(media_handle)));
server.registerTool("publish_video", {
title: "Publish video",
description: "Create a private, AI-disclosed short-form video publish, optionally scheduled in UTC. Posts flow automatically inside an account's policy or await review on review-mode accounts.",
inputSchema: {
account_ids: z6.array(z6.string()).min(1),
video_url: z6.url().optional(),
media_handle: mediaHandleSchema.optional(),
caption: z6.string().max(4e3),
intent: z6.string().trim().min(1).max(500),
agent_identity: z6.string().trim().min(1).max(120).optional(),
publish_at: z6.string().datetime().optional(),
plan_id: z6.string().uuid().optional(),
idempotency_key: z6.string().min(1).optional()
},
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: true }
}, async ({ account_ids, video_url, media_handle, caption, intent, agent_identity, publish_at, plan_id, idempotency_key }) => {
publishMediaSourceSchema.parse({ video_url, media_handle });
return toolResult(await client.publishVideo({
accountIds: account_ids,
videoUrl: video_url,
mediaHandle: media_handle,
caption,
intent,
agentIdentity: agent_identity,
publishAt: publish_at,
planId: plan_id,
idempotencyKey: idempotency_key
}));
});
server.registerTool("submit_plan", {
title: "Submit publication plan",
description: "Submit a time-bounded L2 publication plan for one human approval.",
inputSchema: {
account_ids: z6.array(z6.string()).min(1),
title: z6.string().trim().min(1).max(120),
objective: z6.string().trim().min(1).max(1e3),
starts_at: z6.string().datetime(),
ends_at: z6.string().datetime(),
max_posts: z6.number().int().min(1).max(20),
brief_version: z6.number().int().positive().optional(),
intent: z6.string().trim().min(1).max(500),
agent_identity: z6.string().trim().min(1).max(120).optional(),
idempotency_key: z6.string().min(1).optional()
},
annotations: { readOnlyHint: false, destructiveHint: false, openWorldHint: false }
}, async ({ account_ids, title, objective, starts_at, ends_at, max_posts, brief_version, intent, agent_identity, idempotency_key }) => toolResult(
await client.submitPlan({
accountIds: account_ids,
title,
objective,
startsAt: starts_at,
endsAt: ends_at,
maxPosts: max_posts,
briefVersion: brief_version,
intent,
agentIdentity: agent_identity,
idempotencyKey: idempotency_key
})
));
server.registerTool("get_plan", {
title: "Get publication plan",
description: "Read plan status and structured feedback from the human reviewer.",
inputSchema: { plan_id: z6.string().uuid() },
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}, async ({ plan_id }) => toolResult(await client.getPlan(plan_id)));
server.registerTool("get_publish", {
title: "Get publish",
description: "Read publish state and any structured approval feedback returned by the human reviewer.",
inputSchema: { post_id: z6.string().min(1) },
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}, async ({ post_id }) => toolResult(await client.getPublish(post_id)));
server.registerTool("get_performance", {
title: "Get performance",
description: "Read normalized performance snapshots for one post or connected account.",
inputSchema: {
post_id: z6.string().optional(),
account_id: z6.string().optional(),
window: z6.enum(["7d", "30d"]).default("7d")
},
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}, async ({ post_id, account_id, window }) => {
if (Boolean(post_id) === Boolean(account_id)) {
throw new Error("Provide exactly one of post_id or account_id");
}
const result = post_id ? await client.getPostPerformance(post_id) : await client.getAccountPerformance(account_id, window);
return toolResult(result);
});
server.registerTool("get_best_posts", {
title: "Get best posts",
description: "Rank one connected account's posts by an evidence-backed metric over the requested window. Null observations are excluded with reasons, never ranked as zero.",
inputSchema: {
account_id: z6.string().min(1),
metric: performanceMetricSchema2,
window: z6.enum(["7d", "30d"]).default("7d")
},
annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false }
}, async ({ account_id, metric, window }) => toolResult(
await client.getBestPosts(account_id, metric, window)
));
return server;
}
async function handlePostdomMcpHttpRequest(request, options) {
const transport = new WebStandardStreamableHTTPServerTransport({
sessionIdGenerator: void 0,
enableJsonResponse: true
});
const server = createPostdomMcpServer(new PostdomClient(options));
await server.connect(transport);
return transport.handleRequest(request);
}
export {
POSTDOM_MCP_VERSION,
POSTDOM_MCP_TOOL_NAMES,
PostdomClient,
createPostdomMcpServer,
handlePostdomMcpHttpRequest
};