@himanshu31shr/linkedin-mcp-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/schemas/analytics.d.ts +56 -0
- package/dist/schemas/analytics.d.ts.map +1 -0
- package/dist/schemas/analytics.js +41 -0
- package/dist/schemas/analytics.js.map +1 -0
- package/dist/schemas/media.d.ts +60 -0
- package/dist/schemas/media.d.ts.map +1 -0
- package/dist/schemas/media.js +31 -0
- package/dist/schemas/media.js.map +1 -0
- package/dist/schemas/organization.d.ts +74 -0
- package/dist/schemas/organization.d.ts.map +1 -0
- package/dist/schemas/organization.js +77 -0
- package/dist/schemas/organization.js.map +1 -0
- package/dist/schemas/posts.d.ts +51 -0
- package/dist/schemas/posts.d.ts.map +1 -0
- package/dist/schemas/posts.js +28 -0
- package/dist/schemas/posts.js.map +1 -0
- package/dist/schemas/profile.d.ts +30 -0
- package/dist/schemas/profile.d.ts.map +1 -0
- package/dist/schemas/profile.js +12 -0
- package/dist/schemas/profile.js.map +1 -0
- package/dist/schemas/social-actions.d.ts +78 -0
- package/dist/schemas/social-actions.d.ts.map +1 -0
- package/dist/schemas/social-actions.js +80 -0
- package/dist/schemas/social-actions.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +29 -0
- package/dist/server.js.map +1 -0
- package/dist/services/linkedin-client.d.ts +55 -0
- package/dist/services/linkedin-client.d.ts.map +1 -0
- package/dist/services/linkedin-client.js +148 -0
- package/dist/services/linkedin-client.js.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +7 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/media.d.ts +30 -0
- package/dist/tools/media.d.ts.map +1 -0
- package/dist/tools/media.js +198 -0
- package/dist/tools/media.js.map +1 -0
- package/dist/tools/org-analytics.d.ts +7 -0
- package/dist/tools/org-analytics.d.ts.map +1 -0
- package/dist/tools/org-analytics.js +92 -0
- package/dist/tools/org-analytics.js.map +1 -0
- package/dist/tools/organization.d.ts +7 -0
- package/dist/tools/organization.d.ts.map +1 -0
- package/dist/tools/organization.js +134 -0
- package/dist/tools/organization.js.map +1 -0
- package/dist/tools/posts.d.ts +30 -0
- package/dist/tools/posts.d.ts.map +1 -0
- package/dist/tools/posts.js +127 -0
- package/dist/tools/posts.js.map +1 -0
- package/dist/tools/profile.d.ts +22 -0
- package/dist/tools/profile.d.ts.map +1 -0
- package/dist/tools/profile.js +63 -0
- package/dist/tools/profile.js.map +1 -0
- package/dist/tools/social-actions.d.ts +7 -0
- package/dist/tools/social-actions.d.ts.map +1 -0
- package/dist/tools/social-actions.js +158 -0
- package/dist/tools/social-actions.js.map +1 -0
- package/dist/utils/errors.d.ts +35 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +59 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +13 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +19 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/urns.d.ts +43 -0
- package/dist/utils/urns.d.ts.map +1 -0
- package/dist/utils/urns.js +59 -0
- package/dist/utils/urns.js.map +1 -0
- package/package.json +63 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import type { TextContent } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { LinkedInClient } from '../services/linkedin-client.js';
|
|
4
|
+
/**
|
|
5
|
+
* Get the authenticated user's LinkedIn profile.
|
|
6
|
+
*/
|
|
7
|
+
export declare function getProfile(client: LinkedInClient): Promise<{
|
|
8
|
+
content: TextContent[];
|
|
9
|
+
isError?: true;
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* Get the authenticated user's email address.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getEmail(client: LinkedInClient): Promise<{
|
|
15
|
+
content: TextContent[];
|
|
16
|
+
isError?: true;
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Register all profile-related tools with the MCP server.
|
|
20
|
+
*/
|
|
21
|
+
export declare function registerProfileTools(server: McpServer, client: LinkedInClient): void;
|
|
22
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../../src/tools/profile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAQhE;;GAEG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC,CAsBrD;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,IAAI,CAAA;CAAE,CAAC,CAWrD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAwBpF"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { formatToolError, formatToolResult } from '../utils/errors.js';
|
|
2
|
+
import { createChildLogger } from '../utils/logger.js';
|
|
3
|
+
import { GetProfileInputSchema, GetEmailInputSchema } from '../schemas/profile.js';
|
|
4
|
+
const log = createChildLogger('profile-tools');
|
|
5
|
+
/**
|
|
6
|
+
* Get the authenticated user's LinkedIn profile.
|
|
7
|
+
*/
|
|
8
|
+
export async function getProfile(client) {
|
|
9
|
+
try {
|
|
10
|
+
log.info('CAVEMAN: Fetching user profile from /v2/userinfo');
|
|
11
|
+
const userInfo = await client.get('/userinfo', undefined, false);
|
|
12
|
+
log.info('CAVEMAN: Profile fetched successfully for sub=%s', userInfo.sub);
|
|
13
|
+
const profile = {
|
|
14
|
+
id: userInfo.sub,
|
|
15
|
+
name: userInfo.name,
|
|
16
|
+
firstName: userInfo.given_name,
|
|
17
|
+
lastName: userInfo.family_name,
|
|
18
|
+
email: userInfo.email,
|
|
19
|
+
emailVerified: userInfo.email_verified,
|
|
20
|
+
picture: userInfo.picture,
|
|
21
|
+
locale: userInfo.locale,
|
|
22
|
+
};
|
|
23
|
+
return formatToolResult(profile);
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
log.error('CAVEMAN: Failed to fetch profile: %s', error);
|
|
27
|
+
return formatToolError(error);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get the authenticated user's email address.
|
|
32
|
+
*/
|
|
33
|
+
export async function getEmail(client) {
|
|
34
|
+
try {
|
|
35
|
+
log.info('CAVEMAN: Fetching user email from /v2/userinfo');
|
|
36
|
+
const userInfo = await client.get('/userinfo', undefined, false);
|
|
37
|
+
log.info('CAVEMAN: Email fetched successfully');
|
|
38
|
+
return formatToolResult({ email: userInfo.email });
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
log.error('CAVEMAN: Failed to fetch email: %s', error);
|
|
42
|
+
return formatToolError(error);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Register all profile-related tools with the MCP server.
|
|
47
|
+
*/
|
|
48
|
+
export function registerProfileTools(server, client) {
|
|
49
|
+
server.registerTool('get_profile', {
|
|
50
|
+
description: "Get the authenticated user's LinkedIn profile including name, email, picture, and unique ID",
|
|
51
|
+
inputSchema: GetProfileInputSchema.shape,
|
|
52
|
+
}, async () => {
|
|
53
|
+
return getProfile(client);
|
|
54
|
+
});
|
|
55
|
+
server.registerTool('get_email', {
|
|
56
|
+
description: "Get the authenticated user's LinkedIn email address",
|
|
57
|
+
inputSchema: GetEmailInputSchema.shape,
|
|
58
|
+
}, async () => {
|
|
59
|
+
return getEmail(client);
|
|
60
|
+
});
|
|
61
|
+
log.info('CAVEMAN: Registered profile tools (get_profile, get_email)');
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/tools/profile.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,OAAO,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAEnF,MAAM,GAAG,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAC;AAE/C;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAsB;IAEtB,IAAI,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QAC7D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAmB,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QACnF,GAAG,CAAC,IAAI,CAAC,kDAAkD,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;QAE3E,MAAM,OAAO,GAAG;YACd,EAAE,EAAE,QAAQ,CAAC,GAAG;YAChB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,SAAS,EAAE,QAAQ,CAAC,UAAU;YAC9B,QAAQ,EAAE,QAAQ,CAAC,WAAW;YAC9B,KAAK,EAAE,QAAQ,CAAC,KAAK;YACrB,aAAa,EAAE,QAAQ,CAAC,cAAc;YACtC,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,MAAM,EAAE,QAAQ,CAAC,MAAM;SACxB,CAAC;QAEF,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,KAAK,CAAC,sCAAsC,EAAE,KAAK,CAAC,CAAC;QACzD,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,MAAsB;IAEtB,IAAI,CAAC;QACH,GAAG,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;QAC3D,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAmB,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;QACnF,GAAG,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QAEhD,OAAO,gBAAgB,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,KAAK,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAC;QACvD,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAiB,EAAE,MAAsB;IAC5E,MAAM,CAAC,YAAY,CACjB,aAAa,EACb;QACE,WAAW,EAAE,6FAA6F;QAC1G,WAAW,EAAE,qBAAqB,CAAC,KAAK;KACzC,EACD,KAAK,IAAI,EAAE;QACT,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;IAC5B,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,YAAY,CACjB,WAAW,EACX;QACE,WAAW,EAAE,qDAAqD;QAClE,WAAW,EAAE,mBAAmB,CAAC,KAAK;KACvC,EACD,KAAK,IAAI,EAAE;QACT,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC,CACF,CAAC;IAEF,GAAG,CAAC,IAAI,CAAC,4DAA4D,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { LinkedInClient } from '../services/linkedin-client.js';
|
|
3
|
+
/**
|
|
4
|
+
* Register all social action tools with the MCP server.
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerSocialActionTools(server: McpServer, client: LinkedInClient): void;
|
|
7
|
+
//# sourceMappingURL=social-actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"social-actions.d.ts","sourceRoot":"","sources":["../../src/tools/social-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAgDhE;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAkLzF"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { createChildLogger } from '../utils/logger.js';
|
|
2
|
+
import { formatToolError, formatToolResult } from '../utils/errors.js';
|
|
3
|
+
import { GetPostCommentsSchema, CreateCommentSchema, DeleteCommentSchema, AddReactionSchema, RemoveReactionSchema, } from '../schemas/social-actions.js';
|
|
4
|
+
const log = createChildLogger('social-actions-tools');
|
|
5
|
+
/**
|
|
6
|
+
* Get the authenticated user's person URN from /v2/userinfo.
|
|
7
|
+
*/
|
|
8
|
+
async function getPersonUrn(client) {
|
|
9
|
+
const userInfo = await client.get('/userinfo', undefined, false);
|
|
10
|
+
return `urn:li:person:${userInfo.sub}`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Parse a LinkedIn comment URN to extract the activity URN and comment ID.
|
|
14
|
+
* Comment URN format: urn:li:comment:(urn:li:activity:ACTIVITY_ID,COMMENT_ID)
|
|
15
|
+
* or: urn:li:comment:(activity:ACTIVITY_ID,COMMENT_ID)
|
|
16
|
+
*/
|
|
17
|
+
function parseCommentUrn(commentUrn) {
|
|
18
|
+
// Match urn:li:comment:(urn:li:activity:ACTIVITY_ID,COMMENT_ID)
|
|
19
|
+
const fullMatch = commentUrn.match(/^urn:li:comment:\((urn:li:activity:\d+),(\d+)\)$/);
|
|
20
|
+
if (fullMatch) {
|
|
21
|
+
return { activityUrn: fullMatch[1], commentId: fullMatch[2] };
|
|
22
|
+
}
|
|
23
|
+
// Match urn:li:comment:(urn:li:ugcPost:POST_ID,COMMENT_ID)
|
|
24
|
+
const ugcMatch = commentUrn.match(/^urn:li:comment:\((urn:li:ugcPost:\d+),(\d+)\)$/);
|
|
25
|
+
if (ugcMatch) {
|
|
26
|
+
return { activityUrn: ugcMatch[1], commentId: ugcMatch[2] };
|
|
27
|
+
}
|
|
28
|
+
throw new Error(`Invalid comment URN format: "${commentUrn}". Expected format: urn:li:comment:(urn:li:activity:ID,COMMENT_ID)`);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Register all social action tools with the MCP server.
|
|
32
|
+
*/
|
|
33
|
+
export function registerSocialActionTools(server, client) {
|
|
34
|
+
// ─── get_post_comments ──────────────────────────────────────────────────
|
|
35
|
+
server.registerTool('get_post_comments', {
|
|
36
|
+
description: 'Get comments on a LinkedIn post',
|
|
37
|
+
inputSchema: {
|
|
38
|
+
postUrn: GetPostCommentsSchema.shape.postUrn,
|
|
39
|
+
count: GetPostCommentsSchema.shape.count,
|
|
40
|
+
start: GetPostCommentsSchema.shape.start,
|
|
41
|
+
},
|
|
42
|
+
}, async (params) => {
|
|
43
|
+
try {
|
|
44
|
+
log.info('CAVEMAN: get_post_comments called for postUrn=%s', params.postUrn);
|
|
45
|
+
const encodedUrn = encodeURIComponent(params.postUrn);
|
|
46
|
+
const count = params.count ?? 10;
|
|
47
|
+
const start = params.start ?? 0;
|
|
48
|
+
const result = await client.get(`/socialActions/${encodedUrn}/comments`, { count: String(count), start: String(start) }, false);
|
|
49
|
+
log.info('CAVEMAN: get_post_comments succeeded');
|
|
50
|
+
return formatToolResult(result);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
log.error('CAVEMAN: get_post_comments failed: %s', error);
|
|
54
|
+
return formatToolError(error);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
// ─── create_comment ─────────────────────────────────────────────────────
|
|
58
|
+
server.registerTool('create_comment', {
|
|
59
|
+
description: 'Add a comment to a LinkedIn post',
|
|
60
|
+
inputSchema: {
|
|
61
|
+
postUrn: CreateCommentSchema.shape.postUrn,
|
|
62
|
+
text: CreateCommentSchema.shape.text,
|
|
63
|
+
authorUrn: CreateCommentSchema.shape.authorUrn,
|
|
64
|
+
},
|
|
65
|
+
}, async (params) => {
|
|
66
|
+
try {
|
|
67
|
+
log.info('CAVEMAN: create_comment called for postUrn=%s', params.postUrn);
|
|
68
|
+
const actorUrn = params.authorUrn ?? await getPersonUrn(client);
|
|
69
|
+
const encodedUrn = encodeURIComponent(params.postUrn);
|
|
70
|
+
const commentBody = {
|
|
71
|
+
actor: actorUrn,
|
|
72
|
+
message: {
|
|
73
|
+
text: params.text,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
log.debug('CAVEMAN: Creating comment with body=%o', commentBody);
|
|
77
|
+
const result = await client.post(`/socialActions/${encodedUrn}/comments`, commentBody, false);
|
|
78
|
+
log.info('CAVEMAN: create_comment succeeded');
|
|
79
|
+
return formatToolResult(result ?? { success: true, message: 'Comment created successfully' });
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
log.error('CAVEMAN: create_comment failed: %s', error);
|
|
83
|
+
return formatToolError(error);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
// ─── delete_comment ─────────────────────────────────────────────────────
|
|
87
|
+
server.registerTool('delete_comment', {
|
|
88
|
+
description: 'Delete a comment from a LinkedIn post',
|
|
89
|
+
inputSchema: {
|
|
90
|
+
commentUrn: DeleteCommentSchema.shape.commentUrn,
|
|
91
|
+
},
|
|
92
|
+
}, async (params) => {
|
|
93
|
+
try {
|
|
94
|
+
log.info('CAVEMAN: delete_comment called for commentUrn=%s', params.commentUrn);
|
|
95
|
+
const { activityUrn, commentId } = parseCommentUrn(params.commentUrn);
|
|
96
|
+
const encodedActivityUrn = encodeURIComponent(activityUrn);
|
|
97
|
+
log.debug('CAVEMAN: Deleting comment: activityUrn=%s, commentId=%s', activityUrn, commentId);
|
|
98
|
+
await client.delete(`/socialActions/${encodedActivityUrn}/comments/${commentId}`, false);
|
|
99
|
+
log.info('CAVEMAN: delete_comment succeeded');
|
|
100
|
+
return formatToolResult({ success: true, message: `Comment ${params.commentUrn} deleted successfully` });
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
log.error('CAVEMAN: delete_comment failed: %s', error);
|
|
104
|
+
return formatToolError(error);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// ─── add_reaction ───────────────────────────────────────────────────────
|
|
108
|
+
server.registerTool('add_reaction', {
|
|
109
|
+
description: 'Add a reaction (like, celebrate, etc.) to a LinkedIn post',
|
|
110
|
+
inputSchema: {
|
|
111
|
+
postUrn: AddReactionSchema.shape.postUrn,
|
|
112
|
+
reactionType: AddReactionSchema.shape.reactionType,
|
|
113
|
+
},
|
|
114
|
+
}, async (params) => {
|
|
115
|
+
try {
|
|
116
|
+
log.info('CAVEMAN: add_reaction called for postUrn=%s, type=%s', params.postUrn, params.reactionType);
|
|
117
|
+
const personUrn = await getPersonUrn(client);
|
|
118
|
+
const encodedUrn = encodeURIComponent(params.postUrn);
|
|
119
|
+
const reactionBody = {
|
|
120
|
+
actor: personUrn,
|
|
121
|
+
object: params.postUrn,
|
|
122
|
+
reactionType: params.reactionType,
|
|
123
|
+
};
|
|
124
|
+
log.debug('CAVEMAN: Adding reaction with body=%o', reactionBody);
|
|
125
|
+
const result = await client.post(`/socialActions/${encodedUrn}/likes`, reactionBody, false);
|
|
126
|
+
log.info('CAVEMAN: add_reaction succeeded');
|
|
127
|
+
return formatToolResult(result ?? { success: true, message: `Reaction ${params.reactionType} added successfully` });
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
log.error('CAVEMAN: add_reaction failed: %s', error);
|
|
131
|
+
return formatToolError(error);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
// ─── remove_reaction ────────────────────────────────────────────────────
|
|
135
|
+
server.registerTool('remove_reaction', {
|
|
136
|
+
description: 'Remove a reaction from a LinkedIn post',
|
|
137
|
+
inputSchema: {
|
|
138
|
+
postUrn: RemoveReactionSchema.shape.postUrn,
|
|
139
|
+
reactionType: RemoveReactionSchema.shape.reactionType,
|
|
140
|
+
},
|
|
141
|
+
}, async (params) => {
|
|
142
|
+
try {
|
|
143
|
+
log.info('CAVEMAN: remove_reaction called for postUrn=%s, type=%s', params.postUrn, params.reactionType);
|
|
144
|
+
const personUrn = await getPersonUrn(client);
|
|
145
|
+
const encodedPostUrn = encodeURIComponent(params.postUrn);
|
|
146
|
+
const encodedPersonUrn = encodeURIComponent(personUrn);
|
|
147
|
+
log.debug('CAVEMAN: Removing reaction from post=%s by person=%s', params.postUrn, personUrn);
|
|
148
|
+
await client.delete(`/socialActions/${encodedPostUrn}/likes/${encodedPersonUrn}`, false);
|
|
149
|
+
log.info('CAVEMAN: remove_reaction succeeded');
|
|
150
|
+
return formatToolResult({ success: true, message: `Reaction ${params.reactionType} removed successfully` });
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
log.error('CAVEMAN: remove_reaction failed: %s', error);
|
|
154
|
+
return formatToolError(error);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=social-actions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"social-actions.js","sourceRoot":"","sources":["../../src/tools/social-actions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACvE,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,8BAA8B,CAAC;AAEtC,MAAM,GAAG,GAAG,iBAAiB,CAAC,sBAAsB,CAAC,CAAC;AAEtD;;GAEG;AACH,KAAK,UAAU,YAAY,CAAC,MAAsB;IAChD,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,GAAG,CAAkB,WAAW,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAClF,OAAO,iBAAiB,QAAQ,CAAC,GAAG,EAAE,CAAC;AACzC,CAAC;AAED;;;;GAIG;AACH,SAAS,eAAe,CAAC,UAAkB;IACzC,gEAAgE;IAChE,MAAM,SAAS,GAAG,UAAU,CAAC,KAAK,CAChC,kDAAkD,CACnD,CAAC;IACF,IAAI,SAAS,EAAE,CAAC;QACd,OAAO,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAChE,CAAC;IAED,2DAA2D;IAC3D,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAC/B,iDAAiD,CAClD,CAAC;IACF,IAAI,QAAQ,EAAE,CAAC;QACb,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9D,CAAC;IAED,MAAM,IAAI,KAAK,CACb,gCAAgC,UAAU,oEAAoE,CAC/G,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,MAAiB,EAAE,MAAsB;IACjF,2EAA2E;IAC3E,MAAM,CAAC,YAAY,CACjB,mBAAmB,EACnB;QACE,WAAW,EAAE,iCAAiC;QAC9C,WAAW,EAAE;YACX,OAAO,EAAE,qBAAqB,CAAC,KAAK,CAAC,OAAO;YAC5C,KAAK,EAAE,qBAAqB,CAAC,KAAK,CAAC,KAAK;YACxC,KAAK,EAAE,qBAAqB,CAAC,KAAK,CAAC,KAAK;SACzC;KACF,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,kDAAkD,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YAE7E,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACtD,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC,CAAC;YAEhC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,CAC7B,kBAAkB,UAAU,WAAW,EACvC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,EAC9C,KAAK,CACN,CAAC;YAEF,GAAG,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;YACjD,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAClC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,KAAK,CAAC,uCAAuC,EAAE,KAAK,CAAC,CAAC;YAC1D,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,CACF,CAAC;IAEF,2EAA2E;IAC3E,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;QACE,WAAW,EAAE,kCAAkC;QAC/C,WAAW,EAAE;YACX,OAAO,EAAE,mBAAmB,CAAC,KAAK,CAAC,OAAO;YAC1C,IAAI,EAAE,mBAAmB,CAAC,KAAK,CAAC,IAAI;YACpC,SAAS,EAAE,mBAAmB,CAAC,KAAK,CAAC,SAAS;SAC/C;KACF,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,+CAA+C,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;YAE1E,MAAM,QAAQ,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;YAChE,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAEtD,MAAM,WAAW,GAAG;gBAClB,KAAK,EAAE,QAAQ;gBACf,OAAO,EAAE;oBACP,IAAI,EAAE,MAAM,CAAC,IAAI;iBAClB;aACF,CAAC;YAEF,GAAG,CAAC,KAAK,CAAC,wCAAwC,EAAE,WAAW,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAC9B,kBAAkB,UAAU,WAAW,EACvC,WAAW,EACX,KAAK,CACN,CAAC;YAEF,GAAG,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;YAC9C,OAAO,gBAAgB,CAAC,MAAM,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,8BAA8B,EAAE,CAAC,CAAC;QAChG,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,KAAK,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAC;YACvD,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,CACF,CAAC;IAEF,2EAA2E;IAC3E,MAAM,CAAC,YAAY,CACjB,gBAAgB,EAChB;QACE,WAAW,EAAE,uCAAuC;QACpD,WAAW,EAAE;YACX,UAAU,EAAE,mBAAmB,CAAC,KAAK,CAAC,UAAU;SACjD;KACF,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,kDAAkD,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;YAEhF,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YACtE,MAAM,kBAAkB,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;YAE3D,GAAG,CAAC,KAAK,CAAC,yDAAyD,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;YAC7F,MAAM,MAAM,CAAC,MAAM,CACjB,kBAAkB,kBAAkB,aAAa,SAAS,EAAE,EAC5D,KAAK,CACN,CAAC;YAEF,GAAG,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;YAC9C,OAAO,gBAAgB,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,WAAW,MAAM,CAAC,UAAU,uBAAuB,EAAE,CAAC,CAAC;QAC3G,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,KAAK,CAAC,oCAAoC,EAAE,KAAK,CAAC,CAAC;YACvD,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,CACF,CAAC;IAEF,2EAA2E;IAC3E,MAAM,CAAC,YAAY,CACjB,cAAc,EACd;QACE,WAAW,EAAE,2DAA2D;QACxE,WAAW,EAAE;YACX,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO;YACxC,YAAY,EAAE,iBAAiB,CAAC,KAAK,CAAC,YAAY;SACnD;KACF,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,sDAAsD,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;YAEtG,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;YAC7C,MAAM,UAAU,GAAG,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAEtD,MAAM,YAAY,GAAG;gBACnB,KAAK,EAAE,SAAS;gBAChB,MAAM,EAAE,MAAM,CAAC,OAAO;gBACtB,YAAY,EAAE,MAAM,CAAC,YAAY;aAClC,CAAC;YAEF,GAAG,CAAC,KAAK,CAAC,uCAAuC,EAAE,YAAY,CAAC,CAAC;YACjE,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAC9B,kBAAkB,UAAU,QAAQ,EACpC,YAAY,EACZ,KAAK,CACN,CAAC;YAEF,GAAG,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;YAC5C,OAAO,gBAAgB,CAAC,MAAM,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,MAAM,CAAC,YAAY,qBAAqB,EAAE,CAAC,CAAC;QACtH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,KAAK,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAC;YACrD,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,CACF,CAAC;IAEF,2EAA2E;IAC3E,MAAM,CAAC,YAAY,CACjB,iBAAiB,EACjB;QACE,WAAW,EAAE,wCAAwC;QACrD,WAAW,EAAE;YACX,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,OAAO;YAC3C,YAAY,EAAE,oBAAoB,CAAC,KAAK,CAAC,YAAY;SACtD;KACF,EACD,KAAK,EAAE,MAAM,EAAE,EAAE;QACf,IAAI,CAAC;YACH,GAAG,CAAC,IAAI,CAAC,yDAAyD,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;YAEzG,MAAM,SAAS,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;YAC7C,MAAM,cAAc,GAAG,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC1D,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;YAEvD,GAAG,CAAC,KAAK,CAAC,sDAAsD,EAAE,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;YAC7F,MAAM,MAAM,CAAC,MAAM,CACjB,kBAAkB,cAAc,UAAU,gBAAgB,EAAE,EAC5D,KAAK,CACN,CAAC;YAEF,GAAG,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;YAC/C,OAAO,gBAAgB,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,YAAY,MAAM,CAAC,YAAY,uBAAuB,EAAE,CAAC,CAAC;QAC9G,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,GAAG,CAAC,KAAK,CAAC,qCAAqC,EAAE,KAAK,CAAC,CAAC;YACxD,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;QAChC,CAAC;IACH,CAAC,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { TextContent } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Format an error into an MCP-compatible tool result with isError: true.
|
|
4
|
+
* This lets the LLM see the failure and self-correct, rather than crashing the protocol.
|
|
5
|
+
*/
|
|
6
|
+
export declare function formatToolError(error: unknown): {
|
|
7
|
+
content: TextContent[];
|
|
8
|
+
isError: true;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Format a successful tool result.
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatToolResult(data: unknown): {
|
|
14
|
+
content: TextContent[];
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Extract a human-readable error message from various error types.
|
|
18
|
+
*/
|
|
19
|
+
export declare function extractErrorMessage(error: unknown): string;
|
|
20
|
+
/**
|
|
21
|
+
* Custom error class for LinkedIn API errors.
|
|
22
|
+
* Preserves HTTP status code and any error details from the API response.
|
|
23
|
+
*/
|
|
24
|
+
export declare class LinkedInApiError extends Error {
|
|
25
|
+
readonly status: number;
|
|
26
|
+
readonly details?: Record<string, unknown> | undefined;
|
|
27
|
+
constructor(status: number, message: string, details?: Record<string, unknown> | undefined);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Error thrown when the access token is missing or invalid.
|
|
31
|
+
*/
|
|
32
|
+
export declare class AuthenticationError extends Error {
|
|
33
|
+
constructor(message?: string);
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAEtE;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,OAAO,EAAE,IAAI,CAAA;CAAE,CAMzF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,OAAO,EAAE,WAAW,EAAE,CAAA;CAAE,CAK1E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAW1D;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,MAAM,EAAE,MAAM;aAEd,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;gBAFjC,MAAM,EAAE,MAAM,EAC9B,OAAO,EAAE,MAAM,EACC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA;CAKpD;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,KAAK;gBAChC,OAAO,SAAyE;CAI7F"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Format an error into an MCP-compatible tool result with isError: true.
|
|
3
|
+
* This lets the LLM see the failure and self-correct, rather than crashing the protocol.
|
|
4
|
+
*/
|
|
5
|
+
export function formatToolError(error) {
|
|
6
|
+
const message = extractErrorMessage(error);
|
|
7
|
+
return {
|
|
8
|
+
content: [{ type: 'text', text: message }],
|
|
9
|
+
isError: true,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Format a successful tool result.
|
|
14
|
+
*/
|
|
15
|
+
export function formatToolResult(data) {
|
|
16
|
+
const text = typeof data === 'string' ? data : JSON.stringify(data, null, 2);
|
|
17
|
+
return {
|
|
18
|
+
content: [{ type: 'text', text }],
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Extract a human-readable error message from various error types.
|
|
23
|
+
*/
|
|
24
|
+
export function extractErrorMessage(error) {
|
|
25
|
+
if (error instanceof LinkedInApiError) {
|
|
26
|
+
return `LinkedIn API Error (${error.status}): ${error.message}${error.details ? `\nDetails: ${JSON.stringify(error.details)}` : ''}`;
|
|
27
|
+
}
|
|
28
|
+
if (error instanceof Error) {
|
|
29
|
+
return error.message;
|
|
30
|
+
}
|
|
31
|
+
if (typeof error === 'string') {
|
|
32
|
+
return error;
|
|
33
|
+
}
|
|
34
|
+
return 'An unknown error occurred';
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Custom error class for LinkedIn API errors.
|
|
38
|
+
* Preserves HTTP status code and any error details from the API response.
|
|
39
|
+
*/
|
|
40
|
+
export class LinkedInApiError extends Error {
|
|
41
|
+
status;
|
|
42
|
+
details;
|
|
43
|
+
constructor(status, message, details) {
|
|
44
|
+
super(message);
|
|
45
|
+
this.status = status;
|
|
46
|
+
this.details = details;
|
|
47
|
+
this.name = 'LinkedInApiError';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Error thrown when the access token is missing or invalid.
|
|
52
|
+
*/
|
|
53
|
+
export class AuthenticationError extends Error {
|
|
54
|
+
constructor(message = 'LINKEDIN_ACCESS_TOKEN environment variable is not set or is invalid.') {
|
|
55
|
+
super(message);
|
|
56
|
+
this.name = 'AuthenticationError';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,MAAM,OAAO,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAC3C,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAC1C,OAAO,EAAE,IAAI;KACd,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,IAAa;IAC5C,MAAM,IAAI,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7E,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;KAClC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAc;IAChD,IAAI,KAAK,YAAY,gBAAgB,EAAE,CAAC;QACtC,OAAO,uBAAuB,KAAK,CAAC,MAAM,MAAM,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;IACvI,CAAC;IACD,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,OAAO,CAAC;IACvB,CAAC;IACD,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,2BAA2B,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAEvB;IAEA;IAHlB,YACkB,MAAc,EAC9B,OAAe,EACC,OAAiC;QAEjD,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,WAAM,GAAN,MAAM,CAAQ;QAEd,YAAO,GAAP,OAAO,CAA0B;QAGjD,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,KAAK;IAC5C,YAAY,OAAO,GAAG,sEAAsE;QAC1F,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,qBAAqB,CAAC;IACpC,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
/**
|
|
3
|
+
* Application logger configured to write to stderr ONLY.
|
|
4
|
+
* This is critical: stdout is reserved for the JSON-RPC stream in MCP stdio transport.
|
|
5
|
+
* Any console.log() would corrupt the protocol — use this logger instead.
|
|
6
|
+
*/
|
|
7
|
+
export declare const logger: pino.Logger<never, boolean>;
|
|
8
|
+
/**
|
|
9
|
+
* Create a child logger with a specific module context.
|
|
10
|
+
* Usage: const log = createChildLogger('linkedin-client');
|
|
11
|
+
*/
|
|
12
|
+
export declare function createChildLogger(module: string): pino.Logger;
|
|
13
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,MAAM,6BAOlB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC,MAAM,CAE7D"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import pino from 'pino';
|
|
2
|
+
/**
|
|
3
|
+
* Application logger configured to write to stderr ONLY.
|
|
4
|
+
* This is critical: stdout is reserved for the JSON-RPC stream in MCP stdio transport.
|
|
5
|
+
* Any console.log() would corrupt the protocol — use this logger instead.
|
|
6
|
+
*/
|
|
7
|
+
export const logger = pino({
|
|
8
|
+
name: 'linkedin-mcp-server',
|
|
9
|
+
level: process.env.LOG_LEVEL ?? 'info',
|
|
10
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
11
|
+
}, pino.destination({ fd: 2 }));
|
|
12
|
+
/**
|
|
13
|
+
* Create a child logger with a specific module context.
|
|
14
|
+
* Usage: const log = createChildLogger('linkedin-client');
|
|
15
|
+
*/
|
|
16
|
+
export function createChildLogger(module) {
|
|
17
|
+
return logger.child({ module });
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;;GAIG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,CACxB;IACE,IAAI,EAAE,qBAAqB;IAC3B,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,MAAM;IACtC,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,OAAO;CACzC,EACD,IAAI,CAAC,WAAW,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,CAC5B,CAAC;AAEF;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,OAAO,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;AAClC,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LinkedIn URN (Uniform Resource Name) utilities.
|
|
3
|
+
*
|
|
4
|
+
* LinkedIn uses URNs to identify entities:
|
|
5
|
+
* urn:li:person:ABC123
|
|
6
|
+
* urn:li:organization:12345
|
|
7
|
+
* urn:li:share:67890
|
|
8
|
+
* urn:li:image:C4E22AQH...
|
|
9
|
+
* urn:li:ugcPost:12345
|
|
10
|
+
*/
|
|
11
|
+
/** Valid LinkedIn entity types that appear in URNs */
|
|
12
|
+
export type LinkedInEntityType = 'person' | 'organization' | 'share' | 'ugcPost' | 'image' | 'video' | 'document' | 'comment' | 'activity';
|
|
13
|
+
/** Parsed representation of a LinkedIn URN */
|
|
14
|
+
export interface ParsedUrn {
|
|
15
|
+
entityType: string;
|
|
16
|
+
entityId: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Build a LinkedIn URN string from entity type and ID.
|
|
20
|
+
* @example buildUrn('person', 'ABC123') → 'urn:li:person:ABC123'
|
|
21
|
+
*/
|
|
22
|
+
export declare function buildUrn(entityType: LinkedInEntityType, entityId: string): string;
|
|
23
|
+
/**
|
|
24
|
+
* Parse a LinkedIn URN string into its components.
|
|
25
|
+
* @example parseUrn('urn:li:person:ABC123') → { entityType: 'person', entityId: 'ABC123' }
|
|
26
|
+
* @throws {Error} If the URN format is invalid
|
|
27
|
+
*/
|
|
28
|
+
export declare function parseUrn(urn: string): ParsedUrn;
|
|
29
|
+
/**
|
|
30
|
+
* Validate that a string is a valid LinkedIn URN.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isValidUrn(urn: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Validate that a URN is of a specific entity type.
|
|
35
|
+
* @example isUrnOfType('urn:li:person:ABC123', 'person') → true
|
|
36
|
+
*/
|
|
37
|
+
export declare function isUrnOfType(urn: string, entityType: LinkedInEntityType): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Extract just the entity ID from a URN.
|
|
40
|
+
* @example extractEntityId('urn:li:person:ABC123') → 'ABC123'
|
|
41
|
+
*/
|
|
42
|
+
export declare function extractEntityId(urn: string): string;
|
|
43
|
+
//# sourceMappingURL=urns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urns.d.ts","sourceRoot":"","sources":["../../src/utils/urns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,sDAAsD;AACtD,MAAM,MAAM,kBAAkB,GAC1B,QAAQ,GACR,cAAc,GACd,OAAO,GACP,SAAS,GACT,OAAO,GACP,OAAO,GACP,UAAU,GACV,SAAS,GACT,UAAU,CAAC;AAEf,8CAA8C;AAC9C,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,UAAU,EAAE,kBAAkB,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAS/C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAOhF;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEnD"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LinkedIn URN (Uniform Resource Name) utilities.
|
|
3
|
+
*
|
|
4
|
+
* LinkedIn uses URNs to identify entities:
|
|
5
|
+
* urn:li:person:ABC123
|
|
6
|
+
* urn:li:organization:12345
|
|
7
|
+
* urn:li:share:67890
|
|
8
|
+
* urn:li:image:C4E22AQH...
|
|
9
|
+
* urn:li:ugcPost:12345
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Build a LinkedIn URN string from entity type and ID.
|
|
13
|
+
* @example buildUrn('person', 'ABC123') → 'urn:li:person:ABC123'
|
|
14
|
+
*/
|
|
15
|
+
export function buildUrn(entityType, entityId) {
|
|
16
|
+
return `urn:li:${entityType}:${entityId}`;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parse a LinkedIn URN string into its components.
|
|
20
|
+
* @example parseUrn('urn:li:person:ABC123') → { entityType: 'person', entityId: 'ABC123' }
|
|
21
|
+
* @throws {Error} If the URN format is invalid
|
|
22
|
+
*/
|
|
23
|
+
export function parseUrn(urn) {
|
|
24
|
+
const match = urn.match(/^urn:li:(\w+):(.+)$/);
|
|
25
|
+
if (!match) {
|
|
26
|
+
throw new Error(`Invalid LinkedIn URN format: "${urn}". Expected format: urn:li:{entityType}:{entityId}`);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
entityType: match[1],
|
|
30
|
+
entityId: match[2],
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Validate that a string is a valid LinkedIn URN.
|
|
35
|
+
*/
|
|
36
|
+
export function isValidUrn(urn) {
|
|
37
|
+
return /^urn:li:\w+:.+$/.test(urn);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Validate that a URN is of a specific entity type.
|
|
41
|
+
* @example isUrnOfType('urn:li:person:ABC123', 'person') → true
|
|
42
|
+
*/
|
|
43
|
+
export function isUrnOfType(urn, entityType) {
|
|
44
|
+
try {
|
|
45
|
+
const parsed = parseUrn(urn);
|
|
46
|
+
return parsed.entityType === entityType;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Extract just the entity ID from a URN.
|
|
54
|
+
* @example extractEntityId('urn:li:person:ABC123') → 'ABC123'
|
|
55
|
+
*/
|
|
56
|
+
export function extractEntityId(urn) {
|
|
57
|
+
return parseUrn(urn).entityId;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=urns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"urns.js","sourceRoot":"","sources":["../../src/utils/urns.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAoBH;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,UAA8B,EAAE,QAAgB;IACvE,OAAO,UAAU,UAAU,IAAI,QAAQ,EAAE,CAAC;AAC5C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,qBAAqB,CAAC,CAAC;IAC/C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CAAC,iCAAiC,GAAG,oDAAoD,CAAC,CAAC;IAC5G,CAAC;IACD,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC;QACpB,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;KACnB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,GAAW;IACpC,OAAO,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACrC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,GAAW,EAAE,UAA8B;IACrE,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;QAC7B,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,GAAW;IACzC,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;AAChC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@himanshu31shr/linkedin-mcp-server",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCP server for LinkedIn API integration — compatible with all major AI IDEs",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"linkedin-mcp-server": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"auth": "tsx scripts/auth.ts",
|
|
17
|
+
"dev": "tsx src/index.ts",
|
|
18
|
+
"start": "node dist/index.js",
|
|
19
|
+
"test": "vitest run",
|
|
20
|
+
"test:watch": "vitest",
|
|
21
|
+
"test:coverage": "vitest run --coverage",
|
|
22
|
+
"test:e2e": "vitest run --config vitest.e2e.config.ts",
|
|
23
|
+
"lint": "tsc --noEmit",
|
|
24
|
+
"inspect": "npx @modelcontextprotocol/inspector node dist/index.js",
|
|
25
|
+
"prepublishOnly": "npm run lint && npm test && npm run build"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"mcp",
|
|
29
|
+
"linkedin",
|
|
30
|
+
"model-context-protocol",
|
|
31
|
+
"ai",
|
|
32
|
+
"agent",
|
|
33
|
+
"oauth"
|
|
34
|
+
],
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"private": false,
|
|
37
|
+
"repository": {
|
|
38
|
+
"type": "git",
|
|
39
|
+
"url": "https://github.com/himanshu31shr/linkedin-mcp-server.git"
|
|
40
|
+
},
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/himanshu31shr/linkedin-mcp-server/issues"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/himanshu31shr/linkedin-mcp-server#readme",
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
47
|
+
"dotenv": "^17.4.2",
|
|
48
|
+
"open": "^11.0.0",
|
|
49
|
+
"pino": "^9.6.0",
|
|
50
|
+
"zod": "^3.24.4"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^22.15.0",
|
|
54
|
+
"@vitest/coverage-v8": "^3.1.4",
|
|
55
|
+
"msw": "^2.7.3",
|
|
56
|
+
"tsx": "^4.19.4",
|
|
57
|
+
"typescript": "^5.8.3",
|
|
58
|
+
"vitest": "^3.1.4"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=18.0.0"
|
|
62
|
+
}
|
|
63
|
+
}
|