@linkedapi/mcp 0.3.11

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.
Files changed (212) hide show
  1. package/.dockerignore +11 -0
  2. package/.github/workflows/merge-main.yaml +136 -0
  3. package/.github/workflows/pr-main.yaml +42 -0
  4. package/.husky/pre-commit +5 -0
  5. package/.prettierrc.js +21 -0
  6. package/.vscode/launch.json +19 -0
  7. package/Dockerfile +39 -0
  8. package/LICENSE +21 -0
  9. package/README.md +17 -0
  10. package/dist/index.d.ts +3 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +189 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/linked-api-server.d.ts +13 -0
  15. package/dist/linked-api-server.d.ts.map +1 -0
  16. package/dist/linked-api-server.js +127 -0
  17. package/dist/linked-api-server.js.map +1 -0
  18. package/dist/linked-api-tools.d.ts +8 -0
  19. package/dist/linked-api-tools.d.ts.map +1 -0
  20. package/dist/linked-api-tools.js +72 -0
  21. package/dist/linked-api-tools.js.map +1 -0
  22. package/dist/prompts/index.d.ts +10 -0
  23. package/dist/prompts/index.d.ts.map +1 -0
  24. package/dist/prompts/index.js +190 -0
  25. package/dist/prompts/index.js.map +1 -0
  26. package/dist/tools/check-connection-status.d.ts +13 -0
  27. package/dist/tools/check-connection-status.d.ts.map +1 -0
  28. package/dist/tools/check-connection-status.js +31 -0
  29. package/dist/tools/check-connection-status.js.map +1 -0
  30. package/dist/tools/comment-on-post.d.ts +15 -0
  31. package/dist/tools/comment-on-post.d.ts.map +1 -0
  32. package/dist/tools/comment-on-post.js +41 -0
  33. package/dist/tools/comment-on-post.js.map +1 -0
  34. package/dist/tools/create-post.d.ts +23 -0
  35. package/dist/tools/create-post.d.ts.map +1 -0
  36. package/dist/tools/create-post.js +67 -0
  37. package/dist/tools/create-post.js.map +1 -0
  38. package/dist/tools/execute-custom-workflow.d.ts +13 -0
  39. package/dist/tools/execute-custom-workflow.d.ts.map +1 -0
  40. package/dist/tools/execute-custom-workflow.js +26 -0
  41. package/dist/tools/execute-custom-workflow.js.map +1 -0
  42. package/dist/tools/fetch-company.d.ts +34 -0
  43. package/dist/tools/fetch-company.d.ts.map +1 -0
  44. package/dist/tools/fetch-company.js +139 -0
  45. package/dist/tools/fetch-company.js.map +1 -0
  46. package/dist/tools/fetch-person.d.ts +32 -0
  47. package/dist/tools/fetch-person.d.ts.map +1 -0
  48. package/dist/tools/fetch-person.js +129 -0
  49. package/dist/tools/fetch-person.js.map +1 -0
  50. package/dist/tools/fetch-post.d.ts +26 -0
  51. package/dist/tools/fetch-post.d.ts.map +1 -0
  52. package/dist/tools/fetch-post.js +82 -0
  53. package/dist/tools/fetch-post.js.map +1 -0
  54. package/dist/tools/get-api-usage-stats.d.ts +19 -0
  55. package/dist/tools/get-api-usage-stats.d.ts.map +1 -0
  56. package/dist/tools/get-api-usage-stats.js +40 -0
  57. package/dist/tools/get-api-usage-stats.js.map +1 -0
  58. package/dist/tools/get-conversation.d.ts +26 -0
  59. package/dist/tools/get-conversation.d.ts.map +1 -0
  60. package/dist/tools/get-conversation.js +59 -0
  61. package/dist/tools/get-conversation.js.map +1 -0
  62. package/dist/tools/get-workflow-result.d.ts +50 -0
  63. package/dist/tools/get-workflow-result.d.ts.map +1 -0
  64. package/dist/tools/get-workflow-result.js +46 -0
  65. package/dist/tools/get-workflow-result.js.map +1 -0
  66. package/dist/tools/nv-fetch-company.d.ts +30 -0
  67. package/dist/tools/nv-fetch-company.d.ts.map +1 -0
  68. package/dist/tools/nv-fetch-company.js +124 -0
  69. package/dist/tools/nv-fetch-company.js.map +1 -0
  70. package/dist/tools/nv-fetch-person.d.ts +13 -0
  71. package/dist/tools/nv-fetch-person.d.ts.map +1 -0
  72. package/dist/tools/nv-fetch-person.js +31 -0
  73. package/dist/tools/nv-fetch-person.js.map +1 -0
  74. package/dist/tools/nv-get-conversation.d.ts +26 -0
  75. package/dist/tools/nv-get-conversation.d.ts.map +1 -0
  76. package/dist/tools/nv-get-conversation.js +59 -0
  77. package/dist/tools/nv-get-conversation.js.map +1 -0
  78. package/dist/tools/nv-search-companies.d.ts +32 -0
  79. package/dist/tools/nv-search-companies.d.ts.map +1 -0
  80. package/dist/tools/nv-search-companies.js +106 -0
  81. package/dist/tools/nv-search-companies.js.map +1 -0
  82. package/dist/tools/nv-search-people.d.ts +25 -0
  83. package/dist/tools/nv-search-people.d.ts.map +1 -0
  84. package/dist/tools/nv-search-people.js +99 -0
  85. package/dist/tools/nv-search-people.js.map +1 -0
  86. package/dist/tools/nv-send-message.d.ts +15 -0
  87. package/dist/tools/nv-send-message.d.ts.map +1 -0
  88. package/dist/tools/nv-send-message.js +41 -0
  89. package/dist/tools/nv-send-message.js.map +1 -0
  90. package/dist/tools/nv-sync-conversation.d.ts +13 -0
  91. package/dist/tools/nv-sync-conversation.d.ts.map +1 -0
  92. package/dist/tools/nv-sync-conversation.js +31 -0
  93. package/dist/tools/nv-sync-conversation.js.map +1 -0
  94. package/dist/tools/react-to-post.d.ts +22 -0
  95. package/dist/tools/react-to-post.d.ts.map +1 -0
  96. package/dist/tools/react-to-post.js +42 -0
  97. package/dist/tools/react-to-post.js.map +1 -0
  98. package/dist/tools/remove-connection.d.ts +13 -0
  99. package/dist/tools/remove-connection.d.ts.map +1 -0
  100. package/dist/tools/remove-connection.js +31 -0
  101. package/dist/tools/remove-connection.js.map +1 -0
  102. package/dist/tools/retrieve-connections.d.ts +24 -0
  103. package/dist/tools/retrieve-connections.d.ts.map +1 -0
  104. package/dist/tools/retrieve-connections.js +90 -0
  105. package/dist/tools/retrieve-connections.js.map +1 -0
  106. package/dist/tools/retrieve-pending-requests.d.ts +10 -0
  107. package/dist/tools/retrieve-pending-requests.d.ts.map +1 -0
  108. package/dist/tools/retrieve-pending-requests.js +23 -0
  109. package/dist/tools/retrieve-pending-requests.js.map +1 -0
  110. package/dist/tools/retrieve-performance.d.ts +10 -0
  111. package/dist/tools/retrieve-performance.d.ts.map +1 -0
  112. package/dist/tools/retrieve-performance.js +23 -0
  113. package/dist/tools/retrieve-performance.js.map +1 -0
  114. package/dist/tools/retrieve-ssi.d.ts +10 -0
  115. package/dist/tools/retrieve-ssi.d.ts.map +1 -0
  116. package/dist/tools/retrieve-ssi.js +23 -0
  117. package/dist/tools/retrieve-ssi.js.map +1 -0
  118. package/dist/tools/search-companies.d.ts +28 -0
  119. package/dist/tools/search-companies.d.ts.map +1 -0
  120. package/dist/tools/search-companies.js +86 -0
  121. package/dist/tools/search-companies.js.map +1 -0
  122. package/dist/tools/search-people.d.ts +24 -0
  123. package/dist/tools/search-people.d.ts.map +1 -0
  124. package/dist/tools/search-people.js +90 -0
  125. package/dist/tools/search-people.js.map +1 -0
  126. package/dist/tools/send-connection-request.d.ts +15 -0
  127. package/dist/tools/send-connection-request.d.ts.map +1 -0
  128. package/dist/tools/send-connection-request.js +41 -0
  129. package/dist/tools/send-connection-request.js.map +1 -0
  130. package/dist/tools/send-message.d.ts +14 -0
  131. package/dist/tools/send-message.d.ts.map +1 -0
  132. package/dist/tools/send-message.js +36 -0
  133. package/dist/tools/send-message.js.map +1 -0
  134. package/dist/tools/sync-conversation.d.ts +13 -0
  135. package/dist/tools/sync-conversation.d.ts.map +1 -0
  136. package/dist/tools/sync-conversation.js +31 -0
  137. package/dist/tools/sync-conversation.js.map +1 -0
  138. package/dist/tools/withdraw-connection-request.d.ts +14 -0
  139. package/dist/tools/withdraw-connection-request.d.ts.map +1 -0
  140. package/dist/tools/withdraw-connection-request.js +36 -0
  141. package/dist/tools/withdraw-connection-request.js.map +1 -0
  142. package/dist/utils/define-request-timeout.d.ts +2 -0
  143. package/dist/utils/define-request-timeout.d.ts.map +1 -0
  144. package/dist/utils/define-request-timeout.js +20 -0
  145. package/dist/utils/define-request-timeout.js.map +1 -0
  146. package/dist/utils/execute-with-progress.d.ts +8 -0
  147. package/dist/utils/execute-with-progress.d.ts.map +1 -0
  148. package/dist/utils/execute-with-progress.js +75 -0
  149. package/dist/utils/execute-with-progress.js.map +1 -0
  150. package/dist/utils/handle-linked-api-error.d.ts +3 -0
  151. package/dist/utils/handle-linked-api-error.d.ts.map +1 -0
  152. package/dist/utils/handle-linked-api-error.js +29 -0
  153. package/dist/utils/handle-linked-api-error.js.map +1 -0
  154. package/dist/utils/json-http-transport.d.ts +26 -0
  155. package/dist/utils/json-http-transport.d.ts.map +1 -0
  156. package/dist/utils/json-http-transport.js +265 -0
  157. package/dist/utils/json-http-transport.js.map +1 -0
  158. package/dist/utils/linked-api-tool.d.ts +28 -0
  159. package/dist/utils/linked-api-tool.d.ts.map +1 -0
  160. package/dist/utils/linked-api-tool.js +25 -0
  161. package/dist/utils/linked-api-tool.js.map +1 -0
  162. package/dist/utils/logger.d.ts +8 -0
  163. package/dist/utils/logger.d.ts.map +1 -0
  164. package/dist/utils/logger.js +81 -0
  165. package/dist/utils/logger.js.map +1 -0
  166. package/dist/utils/types.d.ts +16 -0
  167. package/dist/utils/types.d.ts.map +1 -0
  168. package/dist/utils/types.js +3 -0
  169. package/dist/utils/types.js.map +1 -0
  170. package/eslint.config.js +118 -0
  171. package/package.json +62 -0
  172. package/src/index.ts +212 -0
  173. package/src/linked-api-server.ts +159 -0
  174. package/src/linked-api-tools.ts +72 -0
  175. package/src/prompts/index.ts +190 -0
  176. package/src/tools/check-connection-status.ts +39 -0
  177. package/src/tools/comment-on-post.ts +42 -0
  178. package/src/tools/create-post.ts +71 -0
  179. package/src/tools/execute-custom-workflow.ts +28 -0
  180. package/src/tools/fetch-company.ts +154 -0
  181. package/src/tools/fetch-person.ts +145 -0
  182. package/src/tools/fetch-post.ts +90 -0
  183. package/src/tools/get-api-usage-stats.ts +48 -0
  184. package/src/tools/get-conversation.ts +76 -0
  185. package/src/tools/get-workflow-result.ts +62 -0
  186. package/src/tools/nv-fetch-company.ts +136 -0
  187. package/src/tools/nv-fetch-person.ts +31 -0
  188. package/src/tools/nv-get-conversation.ts +76 -0
  189. package/src/tools/nv-search-companies.ts +114 -0
  190. package/src/tools/nv-search-people.ts +107 -0
  191. package/src/tools/nv-send-message.ts +42 -0
  192. package/src/tools/nv-sync-conversation.ts +32 -0
  193. package/src/tools/react-to-post.ts +44 -0
  194. package/src/tools/remove-connection.ts +32 -0
  195. package/src/tools/retrieve-connections.ts +105 -0
  196. package/src/tools/retrieve-pending-requests.ts +23 -0
  197. package/src/tools/retrieve-performance.ts +23 -0
  198. package/src/tools/retrieve-ssi.ts +23 -0
  199. package/src/tools/search-companies.ts +93 -0
  200. package/src/tools/search-people.ts +97 -0
  201. package/src/tools/send-connection-request.ts +46 -0
  202. package/src/tools/send-message.ts +36 -0
  203. package/src/tools/sync-conversation.ts +32 -0
  204. package/src/tools/withdraw-connection-request.ts +41 -0
  205. package/src/utils/define-request-timeout.ts +17 -0
  206. package/src/utils/execute-with-progress.ts +93 -0
  207. package/src/utils/handle-linked-api-error.ts +27 -0
  208. package/src/utils/json-http-transport.ts +325 -0
  209. package/src/utils/linked-api-tool.ts +58 -0
  210. package/src/utils/logger.ts +84 -0
  211. package/src/utils/types.ts +17 -0
  212. package/tsconfig.json +36 -0
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@linkedapi/mcp",
3
+ "version": "0.3.11",
4
+ "description": "MCP server that lets AI assistants control LinkedIn accounts and retrieve real-time data.",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "linkedapi-mcp": "dist/index.js"
8
+ },
9
+ "type": "commonjs",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsx src/index.ts",
13
+ "dev:http": "tsx src/index.ts --http",
14
+ "dev:stdio": "tsx src/index.ts",
15
+ "start": "node dist/index.js",
16
+ "watch": "tsx watch src/index.ts",
17
+ "clean": "rm -rf dist",
18
+ "typecheck": "tsc --noEmit",
19
+ "lint": "eslint src --fix",
20
+ "format": "prettier --write \"src/**/*.ts\"",
21
+ "prepublishOnly": "npm run clean && npm run build",
22
+ "prepare": "husky"
23
+ },
24
+ "keywords": [
25
+ "mcp",
26
+ "linkedapi",
27
+ "linkedin",
28
+ "model-context-protocol"
29
+ ],
30
+ "author": "Linked API",
31
+ "license": "MIT",
32
+ "dependencies": {
33
+ "@modelcontextprotocol/sdk": "^1.17.4",
34
+ "@linkedapi/node": "^1.2.15",
35
+ "zod": "^4.1.1"
36
+ },
37
+ "devDependencies": {
38
+ "@eslint/compat": "^1.2.8",
39
+ "@eslint/eslintrc": "^3.3.1",
40
+ "@eslint/js": "^9.24.0",
41
+ "@trivago/prettier-plugin-sort-imports": "^5.2.2",
42
+ "@types/jest": "^29.5.12",
43
+ "@types/node": "^20.12.12",
44
+ "@typescript-eslint/eslint-plugin": "^8.29.1",
45
+ "@typescript-eslint/parser": "^8.29.1",
46
+ "eslint": "^9.24.0",
47
+ "eslint-config-prettier": "^10.1.2",
48
+ "eslint-import-resolver-typescript": "^4.4.4",
49
+ "eslint-plugin-import": "^2.31.0",
50
+ "eslint-plugin-n": "^17.17.0",
51
+ "eslint-plugin-promise": "^7.2.1",
52
+ "eslint-plugin-unused-imports": "^4.1.4",
53
+ "husky": "^9.1.7",
54
+ "jest": "^29.7.0",
55
+ "prettier": "^3.5.3",
56
+ "ts-jest": "^29.1.2",
57
+ "ts-node": "^10.9.2",
58
+ "tsx": "^4.7.1",
59
+ "typescript": "^5.8.3",
60
+ "typescript-eslint": "^8.38.0"
61
+ }
62
+ }
package/src/index.ts ADDED
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env node
2
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import {
5
+ CallToolRequestSchema,
6
+ GetPromptRequestSchema,
7
+ ListPromptsRequestSchema,
8
+ ListToolsRequestSchema,
9
+ } from '@modelcontextprotocol/sdk/types.js';
10
+ import http from 'node:http';
11
+
12
+ import { LinkedApiMCPServer } from './linked-api-server';
13
+ import { availablePrompts, getPromptContent, systemPrompt } from './prompts';
14
+ import { JsonHTTPServerTransport } from './utils/json-http-transport';
15
+ import { logger } from './utils/logger';
16
+ import { LinkedApiProgressNotification } from './utils/types';
17
+
18
+ function deriveClientFromUserAgent(userAgent: string): string {
19
+ const ua = userAgent.toLowerCase();
20
+ if (ua.includes('cursor')) return 'cursor';
21
+ if (ua.includes('windsurf')) return 'windsurf';
22
+ if (ua.includes('vscode') || ua.includes('visual studio code')) return 'vscode';
23
+ if (ua.includes('chatgpt') || ua.includes('openai')) return 'chatgpt';
24
+ if (ua.includes('curl')) return 'curl';
25
+ if (ua.includes('postman')) return 'postman';
26
+ if (
27
+ ua.includes('mozilla') ||
28
+ ua.includes('chrome') ||
29
+ ua.includes('safari') ||
30
+ ua.includes('firefox')
31
+ )
32
+ return 'browser';
33
+ return userAgent;
34
+ }
35
+
36
+ function getArgValue(flag: string): string | undefined {
37
+ const index = process.argv.indexOf(flag);
38
+ if (index === -1) return undefined;
39
+ const value = process.argv[index + 1];
40
+ if (!value || value.startsWith('--')) return undefined;
41
+ return value;
42
+ }
43
+
44
+ function hasFlag(flag: string): boolean {
45
+ return process.argv.includes(flag);
46
+ }
47
+
48
+ async function main() {
49
+ const server = new Server(
50
+ {
51
+ name: 'linkedapi-mcp',
52
+ version: '1.0.0',
53
+ description: 'MCP Server for Linked API (https://linkedapi.io)',
54
+ },
55
+ {
56
+ capabilities: {
57
+ tools: {},
58
+ prompts: {},
59
+ },
60
+ instructions: systemPrompt,
61
+ },
62
+ );
63
+
64
+ const progressCallback = (_notification: LinkedApiProgressNotification) => {};
65
+ const linkedApiServer = new LinkedApiMCPServer(progressCallback);
66
+
67
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
68
+ const tools = linkedApiServer.getTools();
69
+ return {
70
+ tools,
71
+ };
72
+ });
73
+
74
+ server.setRequestHandler(ListPromptsRequestSchema, async () => {
75
+ return {
76
+ prompts: availablePrompts,
77
+ };
78
+ });
79
+
80
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
81
+ const { name } = request.params;
82
+
83
+ try {
84
+ const content = name === 'performance_guidelines' ? systemPrompt : getPromptContent(name);
85
+
86
+ return {
87
+ description: `Linked API MCP: ${name.replace('_', ' ')}`,
88
+ messages: [
89
+ {
90
+ role: 'user',
91
+ content: {
92
+ type: 'text',
93
+ text: content,
94
+ },
95
+ },
96
+ ],
97
+ };
98
+ } catch {
99
+ throw new Error(`Unknown prompt: ${name}`);
100
+ }
101
+ });
102
+
103
+ server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
104
+ const requestInfoAny = (
105
+ extra as unknown as { requestInfo?: { method?: string; transport?: string } }
106
+ )?.requestInfo;
107
+ const method = requestInfoAny?.method ?? 'N/A';
108
+ const transportType = (requestInfoAny?.transport as 'http' | 'sse' | undefined) ?? 'N/A';
109
+ logger.info(
110
+ {
111
+ method,
112
+ transport: transportType,
113
+ },
114
+ 'Tool request received',
115
+ );
116
+
117
+ try {
118
+ const localLinkedApiToken = process.env.LINKED_API_TOKEN;
119
+ const localIdentificationToken = process.env.IDENTIFICATION_TOKEN;
120
+ const headers = extra?.requestInfo?.headers ?? {};
121
+ const linkedApiToken = (headers['linked-api-token'] ?? localLinkedApiToken ?? '') as string;
122
+ const identificationToken = (headers['identification-token'] ??
123
+ localIdentificationToken ??
124
+ '') as string;
125
+ let mcpClient = (headers['client'] ?? '') as string;
126
+ if (!mcpClient) {
127
+ const userAgentHeader = headers['user-agent'];
128
+ if (typeof userAgentHeader === 'string' && userAgentHeader.trim().length > 0) {
129
+ mcpClient = deriveClientFromUserAgent(userAgentHeader);
130
+ }
131
+ }
132
+
133
+ const result = await linkedApiServer.executeWithTokens(request.params, {
134
+ linkedApiToken,
135
+ identificationToken,
136
+ mcpClient,
137
+ });
138
+ return result;
139
+ } catch (error) {
140
+ logger.error(
141
+ {
142
+ toolName: request.params.name,
143
+ error: error instanceof Error ? error.message : String(error),
144
+ },
145
+ 'Critical tool execution error',
146
+ );
147
+ return {
148
+ content: [
149
+ {
150
+ type: 'text',
151
+ text: 'Unknown error. Please try again.',
152
+ },
153
+ ],
154
+ };
155
+ }
156
+ });
157
+
158
+ if (hasFlag('--http') || hasFlag('--transport=http')) {
159
+ const port = Number(process.env.PORT ?? getArgValue('--port') ?? 3000);
160
+ const host = process.env.HOST ?? getArgValue('--host') ?? '0.0.0.0';
161
+ const transport = new JsonHTTPServerTransport();
162
+
163
+ await server.connect(transport);
164
+
165
+ const httpServer = http.createServer(async (req, res) => {
166
+ try {
167
+ if (!req.url) {
168
+ res.statusCode = 400;
169
+ res.end('Bad Request');
170
+ return;
171
+ }
172
+ const url = new URL(req.url, `http://${req.headers.host ?? 'localhost'}`);
173
+ // Set query parameters to headers if they are not set
174
+ const linkedApiTokenQP = url.searchParams.get('linked-api-token');
175
+ const identificationTokenQP = url.searchParams.get('identification-token');
176
+ const mcpClient = url.searchParams.get('client');
177
+ if (!req.headers['linked-api-token'] && linkedApiTokenQP) {
178
+ req.headers['linked-api-token'] = linkedApiTokenQP;
179
+ }
180
+ if (!req.headers['identification-token'] && identificationTokenQP) {
181
+ req.headers['identification-token'] = identificationTokenQP;
182
+ }
183
+ if (!req.headers['client'] && mcpClient) {
184
+ req.headers['client'] = mcpClient;
185
+ }
186
+ await transport.handleRequest(req, res);
187
+ } catch (error) {
188
+ logger.error(
189
+ {
190
+ error: error instanceof Error ? error.message : String(error),
191
+ },
192
+ 'HTTP request handling failed',
193
+ );
194
+ res.statusCode = 500;
195
+ res.end('Internal Server Error');
196
+ }
197
+ });
198
+
199
+ httpServer.listen(port, host, () => {
200
+ logger.info({ host }, `HTTP transport listening on port ${port}`);
201
+ });
202
+ } else {
203
+ const transport = new StdioServerTransport();
204
+ await server.connect(transport);
205
+ logger.info('stdio transport connected');
206
+ }
207
+ }
208
+
209
+ main().catch((error) => {
210
+ logger.error(error, 'Fatal error');
211
+ process.exit(1);
212
+ });
@@ -0,0 +1,159 @@
1
+ import { LinkedApi, LinkedApiError, TLinkedApiConfig } from '@linkedapi/node';
2
+ import { buildLinkedApiHttpClient } from '@linkedapi/node/dist/core';
3
+ import { Tool } from '@modelcontextprotocol/sdk/types.js';
4
+
5
+ import { LinkedApiTools } from './linked-api-tools';
6
+ import { defineRequestTimeoutInSeconds } from './utils/define-request-timeout';
7
+ import { handleLinkedApiError } from './utils/handle-linked-api-error';
8
+ import { logger } from './utils/logger';
9
+ import {
10
+ CallToolResult,
11
+ ExtendedCallToolRequest,
12
+ LinkedApiProgressNotification,
13
+ } from './utils/types';
14
+
15
+ export class LinkedApiMCPServer {
16
+ private tools: LinkedApiTools;
17
+
18
+ constructor(progressCallback: (notification: LinkedApiProgressNotification) => void) {
19
+ this.tools = new LinkedApiTools(progressCallback);
20
+ }
21
+
22
+ public getTools(): Tool[] {
23
+ return this.tools.tools.map((tool) => tool.getTool());
24
+ }
25
+
26
+ public async executeWithTokens(
27
+ request: ExtendedCallToolRequest['params'],
28
+ { linkedApiToken, identificationToken, mcpClient }: TLinkedApiConfig & { mcpClient: string },
29
+ ): Promise<CallToolResult> {
30
+ const workflowTimeout = defineRequestTimeoutInSeconds(mcpClient) * 1000;
31
+ logger.info(
32
+ {
33
+ toolName: request.name,
34
+ arguments: request.arguments,
35
+ mcpClient,
36
+ workflowTimeout,
37
+ },
38
+ 'Tool execution started',
39
+ );
40
+ const linkedapi = new LinkedApi(
41
+ buildLinkedApiHttpClient(
42
+ {
43
+ linkedApiToken: linkedApiToken,
44
+ identificationToken: identificationToken,
45
+ },
46
+ 'mcp',
47
+ ),
48
+ );
49
+
50
+ const { name: toolName, arguments: args, _meta } = request;
51
+ const progressToken = _meta?.progressToken;
52
+
53
+ const startTime = Date.now();
54
+ try {
55
+ const tool = this.tools.toolByName(toolName);
56
+ if (!tool) {
57
+ throw new Error(`Unknown tool: ${toolName}`);
58
+ }
59
+ const params = tool.validate(args);
60
+ const { data, errors } = await tool.execute({
61
+ linkedapi,
62
+ args: params,
63
+ workflowTimeout,
64
+ progressToken,
65
+ });
66
+ const endTime = Date.now();
67
+ const duration = `${((endTime - startTime) / 1000).toFixed(2)} seconds`;
68
+ if (errors.length > 0 && !data) {
69
+ logger.error(
70
+ {
71
+ toolName,
72
+ duration,
73
+ errors,
74
+ },
75
+ 'Tool execution failed',
76
+ );
77
+ return {
78
+ content: [
79
+ {
80
+ type: 'text' as const,
81
+ text: errors.map((e) => e.message).join('\n'),
82
+ },
83
+ ],
84
+ };
85
+ }
86
+ logger.info(
87
+ {
88
+ toolName,
89
+ duration,
90
+ data,
91
+ },
92
+ 'Tool execution successful',
93
+ );
94
+ if (data) {
95
+ return {
96
+ content: [
97
+ {
98
+ type: 'text' as const,
99
+ text: JSON.stringify(data, null, 2),
100
+ },
101
+ ],
102
+ };
103
+ }
104
+ return {
105
+ content: [
106
+ {
107
+ type: 'text' as const,
108
+ text: 'Completed',
109
+ },
110
+ ],
111
+ };
112
+ } catch (error) {
113
+ const duration = this.calculateDuration(startTime);
114
+ if (error instanceof LinkedApiError) {
115
+ const body = handleLinkedApiError(error);
116
+ logger.error(
117
+ {
118
+ toolName,
119
+ duration,
120
+ body,
121
+ },
122
+ 'Tool execution failed with Linked API error',
123
+ );
124
+ return {
125
+ content: [
126
+ {
127
+ type: 'text' as const,
128
+ text: JSON.stringify(body, null, 2),
129
+ },
130
+ ],
131
+ };
132
+ }
133
+ const errorMessage = error instanceof Error ? error.message : String(error);
134
+ logger.error(
135
+ {
136
+ toolName,
137
+ duration,
138
+ error: errorMessage,
139
+ stack: error instanceof Error ? error.stack : undefined,
140
+ },
141
+ 'Tool execution failed with unknown error',
142
+ );
143
+
144
+ return {
145
+ content: [
146
+ {
147
+ type: 'text' as const,
148
+ text: `Error executing ${toolName}: ${errorMessage}`,
149
+ },
150
+ ],
151
+ };
152
+ }
153
+ }
154
+
155
+ private calculateDuration(startTime: number): string {
156
+ const endTime = Date.now();
157
+ return `${((endTime - startTime) / 1000).toFixed(2)} seconds`;
158
+ }
159
+ }
@@ -0,0 +1,72 @@
1
+ import { CheckConnectionStatusTool } from './tools/check-connection-status.js';
2
+ import { CommentOnPostTool } from './tools/comment-on-post.js';
3
+ import { CreatePostTool } from './tools/create-post.js';
4
+ import { ExecuteCustomWorkflowTool } from './tools/execute-custom-workflow.js';
5
+ import { FetchCompanyTool } from './tools/fetch-company.js';
6
+ import { FetchPersonTool } from './tools/fetch-person.js';
7
+ import { FetchPostTool } from './tools/fetch-post.js';
8
+ import { GetApiUsageTool } from './tools/get-api-usage-stats.js';
9
+ import { GetConversationTool } from './tools/get-conversation.js';
10
+ import { GetWorkflowResultTool } from './tools/get-workflow-result.js';
11
+ import { NvFetchCompanyTool } from './tools/nv-fetch-company.js';
12
+ import { NvFetchPersonTool } from './tools/nv-fetch-person.js';
13
+ import { NvGetConversationTool } from './tools/nv-get-conversation.js';
14
+ import { NvSearchCompaniesTool } from './tools/nv-search-companies.js';
15
+ import { NvSearchPeopleTool } from './tools/nv-search-people.js';
16
+ import { NvSendMessageTool } from './tools/nv-send-message.js';
17
+ import { ReactToPostTool } from './tools/react-to-post.js';
18
+ import { RemoveConnectionTool } from './tools/remove-connection.js';
19
+ import { RetrieveConnectionsTool } from './tools/retrieve-connections.js';
20
+ import { RetrievePendingRequestsTool } from './tools/retrieve-pending-requests.js';
21
+ import { RetrievePerformanceTool } from './tools/retrieve-performance.js';
22
+ import { RetrieveSSITool } from './tools/retrieve-ssi.js';
23
+ import { SearchCompaniesTool } from './tools/search-companies.js';
24
+ import { SearchPeopleTool } from './tools/search-people.js';
25
+ import { SendConnectionRequestTool } from './tools/send-connection-request.js';
26
+ import { SendMessageTool } from './tools/send-message.js';
27
+ import { WithdrawConnectionRequestTool } from './tools/withdraw-connection-request.js';
28
+ import { LinkedApiTool } from './utils/linked-api-tool.js';
29
+ import { LinkedApiProgressNotification } from './utils/types.js';
30
+
31
+ export class LinkedApiTools {
32
+ public readonly tools: ReadonlyArray<LinkedApiTool<unknown, unknown>>;
33
+
34
+ constructor(progressCallback: (progress: LinkedApiProgressNotification) => void) {
35
+ this.tools = [
36
+ // Standard tools
37
+ new SendMessageTool(progressCallback),
38
+ new GetConversationTool(progressCallback),
39
+ new CheckConnectionStatusTool(progressCallback),
40
+ new RetrieveConnectionsTool(progressCallback),
41
+ new SendConnectionRequestTool(progressCallback),
42
+ new WithdrawConnectionRequestTool(progressCallback),
43
+ new RetrievePendingRequestsTool(progressCallback),
44
+ new RemoveConnectionTool(progressCallback),
45
+ new SearchCompaniesTool(progressCallback),
46
+ new SearchPeopleTool(progressCallback),
47
+ new FetchCompanyTool(progressCallback),
48
+ new FetchPersonTool(progressCallback),
49
+ new FetchPostTool(progressCallback),
50
+ new ReactToPostTool(progressCallback),
51
+ new CommentOnPostTool(progressCallback),
52
+ new CreatePostTool(progressCallback),
53
+ new RetrieveSSITool(progressCallback),
54
+ new RetrievePerformanceTool(progressCallback),
55
+ // Sales Navigator tools
56
+ new NvSendMessageTool(progressCallback),
57
+ new NvGetConversationTool(progressCallback),
58
+ new NvSearchCompaniesTool(progressCallback),
59
+ new NvSearchPeopleTool(progressCallback),
60
+ new NvFetchCompanyTool(progressCallback),
61
+ new NvFetchPersonTool(progressCallback),
62
+ // Other tools
63
+ new ExecuteCustomWorkflowTool(progressCallback),
64
+ new GetWorkflowResultTool(progressCallback),
65
+ new GetApiUsageTool(progressCallback),
66
+ ];
67
+ }
68
+
69
+ public toolByName(name: string): LinkedApiTool<unknown, unknown> | undefined {
70
+ return this.tools.find((tool) => tool.name === name);
71
+ }
72
+ }
@@ -0,0 +1,190 @@
1
+ export const systemPrompt = `You are a Linked API MCP server that provides access to LinkedIn data and actions.
2
+
3
+ CRITICAL PERFORMANCE RULE: Only set optional parameters (retrieveExperience, retrieveEmployees, retrievePosts, etc.) to true if the user explicitly requests that specific data. Each additional parameter significantly increases execution time.
4
+
5
+ DEFAULT BEHAVIOR: Always use basic requests first. For example:
6
+ - fetch_person(personUrl) - gets name, title, company (fast)
7
+ - Only add retrieveExperience: true if user asks for work history
8
+ - Only add retrievePosts: true if user asks for posts/content
9
+
10
+ DON'T enable multiple optional flags unless the user specifically requests all that data.
11
+
12
+ AUTHENTICATION ERROR HANDLING
13
+
14
+ WHEN YOU GET AN AUTHENTICATION ERROR:
15
+ The MCP client must provide valid authentication tokens to use Linked API services.
16
+
17
+ REQUIRED TOKENS (have to be provided in headers in each request):
18
+ - linked-api-token: Your Linked API access token from linkedapi.io
19
+ - identification-token: Your identification token from linkedapi.io
20
+
21
+ TOKEN SETUP:
22
+ 1. Visit https://linkedapi.io to get tokens
23
+ 2. Set headers with every request
24
+ 3. Keep tokens secure and never share them in logs or error messages
25
+
26
+ BACKGROUND WORKFLOW HANDLING
27
+
28
+ LINKEDIN WORKFLOWS RUN IN THE BACKGROUND:
29
+ - Linked API operations often take 3-5 minutes or longer
30
+ - To keep the MCP client responsive, workflows continue in the background
31
+ - This is INTENTIONAL DESIGN for better user experience
32
+ - You receive periodic updates about workflow progress
33
+ - Simply continue listening for updates using get_workflow_result
34
+
35
+ WHEN A WORKFLOW IS RUNNING IN BACKGROUND:
36
+ 1. You'll receive a status message with: workflowId and operationName
37
+ 2. Use get_workflow_result(workflowId, operationName) to continue listening for updates
38
+ 3. Keep listening until you get the final results
39
+ 4. This is normal workflow operation - the task is actively running
40
+
41
+ EXAMPLE BACKGROUND WORKFLOW MESSAGE:
42
+ {
43
+ "message": "Workflow is running in the background. Continue listening for updates.",
44
+ "workflowId": "account-12345-abcde",
45
+ "operationName": "searchCompanies"
46
+ }
47
+
48
+ RESPONSE: "Great! The search is running in the background. Let me continue listening for updates."
49
+
50
+ ALWAYS EXPLAIN TO USERS:
51
+ - The workflow is actively running in the background
52
+ - You're continuing to listen for updates
53
+ - This ensures responsive interaction while processing
54
+ - This is how Linked API provides optimal user experience
55
+
56
+ CLIENT ACTION WHEN YOU SEE A BACKGROUND WORKFLOW MESSAGE:
57
+ - Immediately call the get_workflow_result tool with the provided workflowId and operationName
58
+ - Keep calling get_workflow_result until the final result is returned
59
+ - Do NOT retry the original tool; the workflow is already running
60
+ - Treat this as normal, successful behavior (not an error)
61
+
62
+ REQUIRED PARAMETERS FOR get_workflow_result:
63
+ - workflowId: The exact value from the background message
64
+ - operationName: The exact value from the background message (required for correct type restoration)
65
+
66
+ GENERIC MCP CALL EXAMPLE:
67
+ {
68
+ "name": "get_workflow_result",
69
+ "arguments": {
70
+ "workflowId": "account-12345-abcde",
71
+ "operationName": "searchCompanies"
72
+ }
73
+ }
74
+
75
+ Remember: Background workflows = Active processing with responsive updates!`;
76
+
77
+ export const parameterUsageGuidelines = `Parameter Usage Guidelines:
78
+
79
+ FAST:
80
+ - Basic person/company info (no optional flags)
81
+ - Simple searches
82
+ - Send messages, connection requests
83
+
84
+ SLOW:
85
+ - retrievePosts, retrieveComments, retrieveReactions
86
+ - retrieveEmployees, retrieveDMs
87
+ - Complex searches
88
+
89
+ VERY SLOW:
90
+ - Multiple optional flags together
91
+ - Large employee lists
92
+ - Extensive post history
93
+
94
+ Only enable what the user explicitly requests.`;
95
+
96
+ export const backgroundWorkflowPrompt = `LINKED API BACKGROUND WORKFLOWS GUIDE
97
+
98
+ IMPORTANT: Background workflows are NORMAL and OPTIMAL behavior!
99
+
100
+ WHY WORKFLOWS RUN IN BACKGROUND:
101
+ - Linked API operations are naturally slow (5+ minutes)
102
+ - MCP client stays responsive by running workflows in background
103
+ - This is INTENTIONAL DESIGN for better user experience
104
+ - Your operation continues processing while you get updates
105
+
106
+ WHAT TO DO WHEN WORKFLOW RUNS IN BACKGROUND:
107
+ 1. Look for workflowId and operationName in the status message
108
+ 2. Immediately call get_workflow_result(workflowId, operationName) to continue listening
109
+ 3. Repeat get_workflow_result until you get final results
110
+ 4. Do NOT re-run the original tool; the workflow is already executing
111
+ 5. Treat this as normal, successful operation (not an error)
112
+
113
+ EXAMPLE BACKGROUND WORKFLOW MESSAGE:
114
+ {
115
+ "message": "Workflow is running in the background. Continue listening for updates.",
116
+ "workflowId": "account-12345-abcde",
117
+ "operationName": "searchCompanies"
118
+ }
119
+
120
+ CORRECT RESPONSE:
121
+ "Perfect! The search is running in the background. Let me continue listening by calling get_workflow_result with the provided parameters."
122
+
123
+ GENERIC MCP CALL TO CONTINUE LISTENING:
124
+ {
125
+ "name": "get_workflow_result",
126
+ "arguments": {
127
+ "workflowId": "account-12345-abcde",
128
+ "operationName": "searchCompanies"
129
+ }
130
+ }
131
+
132
+ INCORRECT RESPONSE:
133
+ "Sorry, there was an error. The search failed."
134
+
135
+ REMEMBER:
136
+ ✅ Background workflows = Active processing (operation is running)
137
+ ❌ Background workflows ≠ Errors (operation is not broken)
138
+ 🔄 Always continue listening for workflow updates
139
+ ⏱️ Linked API uses background processing for optimal UX
140
+
141
+ COMMON MISTAKES TO AVOID:
142
+ - Retrying the original tool instead of calling get_workflow_result
143
+ - Omitting operationName (it is required)
144
+ - Treating the background message as a failure instead of an active process`;
145
+
146
+ export const authenticationPrompt = `AUTHENTICATION REQUIREMENTS:
147
+ - MCP clients must provide valid authentication tokens to use Linked API services
148
+ - linked-api-token: Main Linked API access token from linkedapi.io
149
+ - identification-token: Account identification token from linkedapi.io
150
+
151
+ TOKEN SETUP:
152
+ 1. Visit https://linkedapi.io to get tokens
153
+ 2. Set headers with every request in MCP client configuration
154
+ 3. Keep tokens secure and never share them in logs or error messages
155
+
156
+ More information: https://linkedapi.io/mcp/installation/`;
157
+
158
+ export const availablePrompts = [
159
+ {
160
+ name: 'performance_guidelines',
161
+ description: 'Get performance optimization guidelines for Linked API MCP tools',
162
+ },
163
+ {
164
+ name: 'parameter_usage',
165
+ description: 'Learn when to use optional parameters in Linked API requests',
166
+ },
167
+ {
168
+ name: 'background_workflows',
169
+ description: 'Learn how Linked API background workflows provide optimal UX',
170
+ },
171
+ {
172
+ name: 'authentication_requirements',
173
+ description: 'Learn how to authenticate with Linked API MCP',
174
+ },
175
+ ];
176
+
177
+ export function getPromptContent(name: string): string {
178
+ switch (name) {
179
+ case 'performance_guidelines':
180
+ return systemPrompt;
181
+ case 'parameter_usage':
182
+ return parameterUsageGuidelines;
183
+ case 'background_workflows':
184
+ return backgroundWorkflowPrompt;
185
+ case 'authentication_requirements':
186
+ return authenticationPrompt;
187
+ default:
188
+ throw new Error(`Unknown prompt: ${name}`);
189
+ }
190
+ }