Create LinkedIn Post
linkedin_create_postCreate LinkedIn posts on behalf of members or organizations with text, images, or article links. Specify author URN, post content, and visibility.
Instructions
Create a new post on LinkedIn on behalf of a member or organization.
Supports text-only posts, image posts (after uploading via linkedin_upload_image), and article/link posts. Post length is capped at 3000 characters.
Requires scope: w_member_social (for member posts) or w_organization_social (for org posts)
Args:
author_urn (string): URN of the post author. Format: 'urn:li:person:{id}' for members. Get your ID from linkedin_get_profile's 'sub' field.
text (string): Post text (1–3000 characters, required)
visibility ('PUBLIC' | 'CONNECTIONS' | 'LOGGED_IN'): Who can see the post (default: 'PUBLIC')
image_asset_urn (string, optional): Asset URN from linkedin_upload_image
article_url (string, optional): URL to share as a link post
article_title (string, optional): Link preview title (used with article_url)
article_description (string, optional): Link preview description (used with article_url)
Returns: The URN of the newly created post (e.g., 'urn:li:share:7123456789')
Examples:
Text post: { author_urn: "urn:li:person:abc", text: "Hello LinkedIn!" }
Image post: { author_urn: "urn:li:person:abc", text: "Check this out", image_asset_urn: "urn:li:digitalmediaAsset:..." }
Article share: { author_urn: "urn:li:person:abc", text: "Great read", article_url: "https://..." }
Error Handling:
403 if w_member_social scope is missing
400 if text exceeds 3000 chars or author URN is invalid
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| author_urn | Yes | URN of the author. Use 'urn:li:person:{id}' for member posts. Get your person ID from linkedin_get_profile (the 'sub' field). | |
| text | Yes | Post text content (max 3000 characters) | |
| visibility | No | Post visibility: PUBLIC (anyone), CONNECTIONS (1st-degree connections), LOGGED_IN (LinkedIn members) | PUBLIC |
| image_asset_urn | No | Asset URN of an uploaded image to attach (from linkedin_upload_image). Optional. Omit for text-only posts. | |
| article_url | No | URL of an article to share as a link post. Optional. | |
| article_title | No | Title for the article link preview. Used only when article_url is provided. | |
| article_description | No | Description for the article link preview. Used only when article_url is provided. |
Implementation Reference
- src/tools/posts.ts:170-260 (registration)Registration of the linkedin_create_post tool on the MCP server, with title, description, input schema, and the handler callback.
export function registerPostTools(server: McpServer): void { // Create post server.registerTool( "linkedin_create_post", { title: "Create LinkedIn Post", description: `Create a new post on LinkedIn on behalf of a member or organization. Supports text-only posts, image posts (after uploading via linkedin_upload_image), and article/link posts. Post length is capped at 3000 characters. Requires scope: w_member_social (for member posts) or w_organization_social (for org posts) Args: - author_urn (string): URN of the post author. Format: 'urn:li:person:{id}' for members. Get your ID from linkedin_get_profile's 'sub' field. - text (string): Post text (1–3000 characters, required) - visibility ('PUBLIC' | 'CONNECTIONS' | 'LOGGED_IN'): Who can see the post (default: 'PUBLIC') - image_asset_urn (string, optional): Asset URN from linkedin_upload_image - article_url (string, optional): URL to share as a link post - article_title (string, optional): Link preview title (used with article_url) - article_description (string, optional): Link preview description (used with article_url) Returns: The URN of the newly created post (e.g., 'urn:li:share:7123456789') Examples: - Text post: { author_urn: "urn:li:person:abc", text: "Hello LinkedIn!" } - Image post: { author_urn: "urn:li:person:abc", text: "Check this out", image_asset_urn: "urn:li:digitalmediaAsset:..." } - Article share: { author_urn: "urn:li:person:abc", text: "Great read", article_url: "https://..." } Error Handling: - 403 if w_member_social scope is missing - 400 if text exceeds 3000 chars or author URN is invalid`, inputSchema: CreatePostInputSchema, annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true, }, }, async (params: CreatePostInput) => { try { const body: Record<string, unknown> = { author: params.author_urn, commentary: escapeLittleText(params.text), visibility: params.visibility, lifecycleState: "PUBLISHED", distribution: { feedDistribution: "MAIN_FEED", targetEntities: [], thirdPartyDistributionChannels: [], }, }; if (params.image_asset_urn) { body.content = { media: { id: params.image_asset_urn, }, }; } else if (params.article_url) { body.content = { article: { source: params.article_url, ...(params.article_title ? { title: params.article_title } : {}), ...(params.article_description ? { description: params.article_description } : {}), }, }; } const response = await restPost<{ id: string }>("/posts", body); const postUrn = response.id ?? "unknown"; return { content: [ { type: "text", text: `Post created successfully.\n\n**Post URN:** ${postUrn}`, }, ], structuredContent: { post_urn: postUrn }, }; } catch (error) { return { content: [{ type: "text", text: handleApiError(error) }] }; } } ); - src/tools/posts.ts:15-59 (schema)Zod input validation schema for linkedin_create_post, defining author_urn, text, visibility, image_asset_urn, article_url, article_title, and article_description.
const CreatePostInputSchema = z .object({ author_urn: z .string() .describe( "URN of the author. Use 'urn:li:person:{id}' for member posts. " + "Get your person ID from linkedin_get_profile (the 'sub' field)." ), text: z .string() .min(1) .max(3000) .describe("Post text content (max 3000 characters)"), visibility: z .enum(["PUBLIC", "CONNECTIONS", "LOGGED_IN"]) .default("PUBLIC") .describe( "Post visibility: PUBLIC (anyone), CONNECTIONS (1st-degree connections), LOGGED_IN (LinkedIn members)" ), image_asset_urn: z .string() .optional() .describe( "Asset URN of an uploaded image to attach (from linkedin_upload_image). " + "Optional. Omit for text-only posts." ), article_url: z .string() .url() .optional() .describe("URL of an article to share as a link post. Optional."), article_title: z .string() .max(400) .optional() .describe("Title for the article link preview. Used only when article_url is provided."), article_description: z .string() .max(400) .optional() .describe( "Description for the article link preview. Used only when article_url is provided." ), }) .strict(); - src/tools/posts.ts:212-259 (handler)Handler function that builds the request body (with commentary, visibility, content for images/articles), calls restPost to LinkedIn API /rest/posts, and returns the created post URN.
async (params: CreatePostInput) => { try { const body: Record<string, unknown> = { author: params.author_urn, commentary: escapeLittleText(params.text), visibility: params.visibility, lifecycleState: "PUBLISHED", distribution: { feedDistribution: "MAIN_FEED", targetEntities: [], thirdPartyDistributionChannels: [], }, }; if (params.image_asset_urn) { body.content = { media: { id: params.image_asset_urn, }, }; } else if (params.article_url) { body.content = { article: { source: params.article_url, ...(params.article_title ? { title: params.article_title } : {}), ...(params.article_description ? { description: params.article_description } : {}), }, }; } const response = await restPost<{ id: string }>("/posts", body); const postUrn = response.id ?? "unknown"; return { content: [ { type: "text", text: `Post created successfully.\n\n**Post URN:** ${postUrn}`, }, ], structuredContent: { post_urn: postUrn }, }; } catch (error) { return { content: [{ type: "text", text: handleApiError(error) }] }; } } - src/tools/posts.ts:149-151 (helper)Helper function that escapes special characters in post text to prevent LinkedIn's Little Text parser from truncating the post.
function escapeLittleText(text: string): string { return text.replace(/([()<>\\*_{}\[\]~])/g, "\\$1"); } - src/tools/posts.ts:134-136 (helper)Helper function to URL-encode URN values for use in API paths.
function encodeUrn(urn: string): string { return encodeURIComponent(urn); }