Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@linkedapi/mcp",
"version": "2.3.12",
"version": "2.3.13",
"description": "MCP server that lets AI assistants control LinkedIn accounts and retrieve real-time data.",
"main": "dist/index.js",
"bin": {
Expand Down Expand Up @@ -30,7 +30,7 @@
"author": "Linked API",
"license": "MIT",
"dependencies": {
"@linkedapi/node": "^2.3.12",
"@linkedapi/node": "^2.3.13",
"@modelcontextprotocol/sdk": "^1.17.4",
"zod": "^4.1.1"
},
Expand All @@ -56,4 +56,4 @@
"typescript": "^5.8.3",
"typescript-eslint": "^8.38.0"
}
}
}
2 changes: 2 additions & 0 deletions src/linked-api-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { AdminSetSeatsTool } from './tools/admin-set-seats.js';
import { CheckConnectionStatusTool } from './tools/check-connection-status.js';
import { CommentOnPostTool } from './tools/comment-on-post.js';
import { CreatePostTool } from './tools/create-post.js';
import { CreateRepostTool } from './tools/create-repost.js';
import { ExecuteCustomWorkflowTool } from './tools/execute-custom-workflow.js';
import { FetchCompanyTool } from './tools/fetch-company.js';
import { FetchJobTool } from './tools/fetch-job.js';
Expand Down Expand Up @@ -114,6 +115,7 @@ export class LinkedApiTools {
new ReactToCommentTool(),
new ReplyToCommentTool(),
new CreatePostTool(),
new CreateRepostTool(),
new RetrieveFeedTool(),
new RetrieveProfileViewersTool(),
new RetrieveSSITool(),
Expand Down
12 changes: 9 additions & 3 deletions src/tools/comment-on-post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export class CommentOnPostTool extends OperationTool<TCommentOnPostParams, TComm
public override readonly name = 'comment_on_post';
public override readonly operationName = OPERATION_NAME.commentOnPost;
protected override readonly schema = z.object({
postUrl: z.string(),
postUrl: z.string().optional(),
postUrn: z.string().optional(),
text: z.string().min(1),
companyUrl: z.string().optional(),
});
Expand All @@ -24,7 +25,12 @@ export class CommentOnPostTool extends OperationTool<TCommentOnPostParams, TComm
postUrl: {
type: 'string',
description:
"The LinkedIn post URL to comment on (e.g., 'https://www.linkedin.com/posts/username_activity-id')",
"The LinkedIn post URL to comment on (e.g., 'https://www.linkedin.com/posts/username_activity-id') Provide this or postUrn.",
},
postUrn: {
type: 'string',
description:
"URN of the post to comment on, as an alternative to postUrl. (e.g., 'urn:li:activity:1234567890123456789')",
},
text: {
type: 'string',
Expand All @@ -36,7 +42,7 @@ export class CommentOnPostTool extends OperationTool<TCommentOnPostParams, TComm
"LinkedIn company page URL. If specified, the comment will be added on behalf of the company. (e.g., 'https://www.linkedin.com/company/acme-corp')",
},
},
required: ['postUrl', 'text'],
required: ['text'],
},
};
}
Expand Down
51 changes: 49 additions & 2 deletions src/tools/create-post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,17 @@ export class CreatePostTool extends OperationTool<TCreatePostParams, unknown> {
public override readonly operationName = OPERATION_NAME.createPost;
protected override readonly schema = z.object({
text: z.string().min(1).max(3000),
mentions: z
.array(
z.object({
key: z.string(),
name: z.string(),
urn: z.string().optional(),
personHashedUrl: z.string().optional(),
companyHashedUrl: z.string().optional(),
}),
)
.optional(),
companyUrl: z.string().optional(),
attachments: z
.array(
Expand All @@ -26,13 +37,49 @@ export class CreatePostTool extends OperationTool<TCreatePostParams, unknown> {
return {
name: this.name,
description:
'Creates a new LinkedIn post with optional media attachments (st.createPost action). If this workflow is still running, do not retry this tool; retrying can create duplicate posts.',
'Creates a new LinkedIn post with optional mentions and media attachments (st.createPost action). Returns the URL and URN of the new post. If this workflow is still running, do not retry this tool; retrying can create duplicate posts.',
inputSchema: {
type: 'object',
properties: {
text: {
type: 'string',
description: 'Post content, must be up to 3000 characters.',
description:
'Post content, must be up to 3000 characters. Write "@[key]" where a mention should appear and describe that key in mentions.',
},
mentions: {
type: 'array',
description:
'People and companies to mention in the text. Each item binds a "@[key]" placeholder to one entity.',
items: {
type: 'object',
properties: {
key: {
type: 'string',
description:
'Placeholder name used in the text as "@[key]". 1-30 characters: letters, digits, underscore or hyphen.',
},
name: {
type: 'string',
description:
'Name to find the person or company by. Always required: LinkedIn resolves a mention through its own name suggestions.',
},
urn: {
type: 'string',
description:
"URN of the entity (e.g., 'urn:li:member:123456789' or 'urn:li:organization:1234567'). Decides which of the offered namesakes is taken.",
},
personHashedUrl: {
type: 'string',
description:
'Hashed LinkedIn profile URL of the person, as an alternative to urn.',
},
companyHashedUrl: {
type: 'string',
description: 'Hashed LinkedIn company page URL, as an alternative to urn.',
},
},
required: ['key', 'name'],
},
},
companyUrl: {
type: 'string',
Expand Down
90 changes: 90 additions & 0 deletions src/tools/create-repost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { OPERATION_NAME, TCreateRepostParams } from '@linkedapi/node';
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { z } from 'zod';

import { OperationTool } from '../utils/linked-api-tool.js';

export class CreateRepostTool extends OperationTool<TCreateRepostParams, unknown> {
public override readonly name = 'create_repost';
public override readonly operationName = OPERATION_NAME.createRepost;
protected override readonly schema = z.object({
postUrl: z.string().optional(),
postUrn: z.string().optional(),
text: z.string().max(3000).optional(),
mentions: z
.array(
z.object({
key: z.string(),
name: z.string(),
urn: z.string().optional(),
personHashedUrl: z.string().optional(),
companyHashedUrl: z.string().optional(),
}),
)
.optional(),
});

public override getTool(): Tool {
return {
name: this.name,
description:
'Reposts a LinkedIn post, either as is or with your own commentary (st.createRepost action). Returns the URL and URN of the repost itself, which is a post of your own account, not of the post that was reshared. Reposting the same post twice from the same account fails with alreadyReposted. If this workflow is still running, do not retry this tool; retrying can create duplicate reposts.',
inputSchema: {
type: 'object',
properties: {
postUrl: {
type: 'string',
description:
"LinkedIn URL of the post to repost. (e.g., 'https://www.linkedin.com/posts/username_activity-id') Provide this or postUrn.",
},
postUrn: {
type: 'string',
description:
"URN of the post to repost, as an alternative to postUrl. (e.g., 'urn:li:activity:1234567890123456789')",
},
text: {
type: 'string',
description:
'Your own commentary, up to 3000 characters. Without it the post is reposted as is. Write "@[key]" where a mention should appear and describe that key in mentions.',
},
mentions: {
type: 'array',
description:
'People and companies to mention in the commentary. Each item binds a "@[key]" placeholder to one entity.',
items: {
type: 'object',
properties: {
key: {
type: 'string',
description:
'Placeholder name used in the text as "@[key]". 1-30 characters: letters, digits, underscore or hyphen.',
},
name: {
type: 'string',
description:
'Name to find the person or company by. Always required: LinkedIn resolves a mention through its own name suggestions.',
},
urn: {
type: 'string',
description:
"URN of the entity (e.g., 'urn:li:member:123456789' or 'urn:li:organization:1234567'). Decides which of the offered namesakes is taken.",
},
personHashedUrl: {
type: 'string',
description:
'Hashed LinkedIn profile URL of the person, as an alternative to urn.',
},
companyHashedUrl: {
type: 'string',
description: 'Hashed LinkedIn company page URL, as an alternative to urn.',
},
},
required: ['key', 'name'],
},
},
},
required: [],
},
};
}
}
12 changes: 9 additions & 3 deletions src/tools/fetch-post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export class FetchPostTool extends OperationTool<TFetchPostParams, unknown> {
public override readonly name = 'fetch_post';
public override readonly operationName = OPERATION_NAME.fetchPost;
protected override readonly schema = z.object({
postUrl: z.string(),
postUrl: z.string().optional(),
postUrn: z.string().optional(),
retrieveComments: z.boolean().optional(),
retrieveReactions: z.boolean().optional(),
commentsRetrievalConfig: z
Expand Down Expand Up @@ -36,7 +37,12 @@ export class FetchPostTool extends OperationTool<TFetchPostParams, unknown> {
postUrl: {
type: 'string',
description:
"LinkedIn URL of the post. (e.g., 'https://www.linkedin.com/posts/username_activity-id')",
"LinkedIn URL of the post. (e.g., 'https://www.linkedin.com/posts/username_activity-id') Provide this or postUrn.",
},
postUrn: {
type: 'string',
description:
"URN of the post to fetch, as an alternative to postUrl. (e.g., 'urn:li:activity:1234567890123456789')",
},
retrieveComments: {
type: 'boolean',
Expand Down Expand Up @@ -83,7 +89,7 @@ export class FetchPostTool extends OperationTool<TFetchPostParams, unknown> {
},
},
},
required: ['postUrl'],
required: [],
},
};
}
Expand Down
12 changes: 9 additions & 3 deletions src/tools/react-to-post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export class ReactToPostTool extends OperationTool<TReactToPostParams, unknown>
public override readonly name = 'react_to_post';
public override readonly operationName = OPERATION_NAME.reactToPost;
protected override readonly schema = z.object({
postUrl: z.string(),
postUrl: z.string().optional(),
postUrn: z.string().optional(),
type: z.enum(['like', 'love', 'celebrate', 'support', 'funny', 'insightful']).or(z.string()),
companyUrl: z.string().optional(),
});
Expand All @@ -24,7 +25,12 @@ export class ReactToPostTool extends OperationTool<TReactToPostParams, unknown>
postUrl: {
type: 'string',
description:
"LinkedIn URL of the post to react. (e.g., 'https://www.linkedin.com/posts/username_activity-id')",
"LinkedIn URL of the post to react. (e.g., 'https://www.linkedin.com/posts/username_activity-id') Provide this or postUrn.",
},
postUrn: {
type: 'string',
description:
"URN of the post to react to, as an alternative to postUrl. (e.g., 'urn:li:activity:1234567890123456789')",
},
type: {
type: 'string',
Expand All @@ -37,7 +43,7 @@ export class ReactToPostTool extends OperationTool<TReactToPostParams, unknown>
"LinkedIn company page URL. If specified, the reaction will be added on behalf of the company. (e.g., 'https://www.linkedin.com/company/acme-corp')",
},
},
required: ['postUrl', 'type'],
required: ['type'],
},
};
}
Expand Down
Loading