@linkedapi/mcp 1.0.0 → 1.0.1
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/README.md +9 -0
- package/dist/index.js +42 -9
- package/dist/index.js.map +1 -1
- package/dist/linked-api-server.d.ts +7 -4
- package/dist/linked-api-server.d.ts.map +1 -1
- package/dist/linked-api-server.js +46 -14
- package/dist/linked-api-server.js.map +1 -1
- package/dist/linked-api-tools.d.ts +18 -4
- package/dist/linked-api-tools.d.ts.map +1 -1
- package/dist/linked-api-tools.js +28 -28
- package/dist/linked-api-tools.js.map +1 -1
- package/dist/tools/comment-on-post.d.ts.map +1 -1
- package/dist/tools/comment-on-post.js +1 -1
- package/dist/tools/comment-on-post.js.map +1 -1
- package/dist/tools/create-post.js +1 -1
- package/dist/tools/create-post.js.map +1 -1
- package/dist/tools/execute-custom-workflow.d.ts.map +1 -1
- package/dist/tools/execute-custom-workflow.js +1 -1
- package/dist/tools/execute-custom-workflow.js.map +1 -1
- package/dist/tools/get-conversation.d.ts +3 -1
- package/dist/tools/get-conversation.d.ts.map +1 -1
- package/dist/tools/get-conversation.js +20 -3
- package/dist/tools/get-conversation.js.map +1 -1
- package/dist/tools/get-workflow-result.d.ts +3 -1
- package/dist/tools/get-workflow-result.d.ts.map +1 -1
- package/dist/tools/get-workflow-result.js +7 -4
- package/dist/tools/get-workflow-result.js.map +1 -1
- package/dist/tools/nv-get-conversation.d.ts +3 -1
- package/dist/tools/nv-get-conversation.d.ts.map +1 -1
- package/dist/tools/nv-get-conversation.js +20 -3
- package/dist/tools/nv-get-conversation.js.map +1 -1
- package/dist/tools/nv-send-message.js +1 -1
- package/dist/tools/nv-send-message.js.map +1 -1
- package/dist/tools/react-to-post.js +1 -1
- package/dist/tools/react-to-post.js.map +1 -1
- package/dist/tools/remove-connection.js +1 -1
- package/dist/tools/remove-connection.js.map +1 -1
- package/dist/tools/send-connection-request.js +1 -1
- package/dist/tools/send-connection-request.js.map +1 -1
- package/dist/tools/send-message.d.ts.map +1 -1
- package/dist/tools/send-message.js +1 -1
- package/dist/tools/send-message.js.map +1 -1
- package/dist/tools/withdraw-connection-request.js +1 -1
- package/dist/tools/withdraw-connection-request.js.map +1 -1
- package/dist/utils/define-request-timeout.d.ts.map +1 -1
- package/dist/utils/define-request-timeout.js +16 -4
- package/dist/utils/define-request-timeout.js.map +1 -1
- package/dist/utils/execute-with-progress.d.ts +7 -2
- package/dist/utils/execute-with-progress.d.ts.map +1 -1
- package/dist/utils/execute-with-progress.js +19 -9
- package/dist/utils/execute-with-progress.js.map +1 -1
- package/dist/utils/handle-linked-api-error.d.ts +1 -1
- package/dist/utils/handle-linked-api-error.d.ts.map +1 -1
- package/dist/utils/handle-linked-api-error.js +10 -0
- package/dist/utils/handle-linked-api-error.js.map +1 -1
- package/dist/utils/json-http-transport.d.ts.map +1 -1
- package/dist/utils/json-http-transport.js +4 -3
- package/dist/utils/json-http-transport.js.map +1 -1
- package/dist/utils/linked-api-tool.d.ts +10 -14
- package/dist/utils/linked-api-tool.d.ts.map +1 -1
- package/dist/utils/linked-api-tool.js +5 -6
- package/dist/utils/linked-api-tool.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +46 -9
- package/src/linked-api-server.ts +64 -16
- package/src/linked-api-tools.ts +52 -31
- package/src/tools/comment-on-post.ts +2 -1
- package/src/tools/create-post.ts +1 -1
- package/src/tools/execute-custom-workflow.ts +2 -1
- package/src/tools/get-conversation.ts +27 -3
- package/src/tools/get-workflow-result.ts +10 -3
- package/src/tools/nv-get-conversation.ts +27 -3
- package/src/tools/nv-send-message.ts +1 -1
- package/src/tools/react-to-post.ts +1 -1
- package/src/tools/remove-connection.ts +1 -1
- package/src/tools/send-connection-request.ts +1 -1
- package/src/tools/send-message.ts +2 -1
- package/src/tools/withdraw-connection-request.ts +1 -1
- package/src/utils/define-request-timeout.ts +16 -4
- package/src/utils/execute-with-progress.ts +37 -19
- package/src/utils/handle-linked-api-error.ts +12 -1
- package/src/utils/json-http-transport.ts +4 -3
- package/src/utils/linked-api-tool.ts +16 -18
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -17,6 +17,9 @@ import { LinkedApiProgressNotification } from './utils/types';
|
|
|
17
17
|
|
|
18
18
|
function deriveClientFromUserAgent(userAgent: string): string {
|
|
19
19
|
const ua = userAgent.toLowerCase();
|
|
20
|
+
if (ua.includes('codex')) return 'codex';
|
|
21
|
+
if (ua.includes('claude-code') || ua.includes('claude code')) return 'claude-code';
|
|
22
|
+
if (ua.includes('claude') || ua.includes('anthropic')) return 'claude';
|
|
20
23
|
if (ua.includes('cursor')) return 'cursor';
|
|
21
24
|
if (ua.includes('windsurf')) return 'windsurf';
|
|
22
25
|
if (ua.includes('vscode') || ua.includes('visual studio code')) return 'vscode';
|
|
@@ -33,6 +36,14 @@ function deriveClientFromUserAgent(userAgent: string): string {
|
|
|
33
36
|
return userAgent;
|
|
34
37
|
}
|
|
35
38
|
|
|
39
|
+
function normalizeHeaderValue(value: string | Array<string> | undefined): string | undefined {
|
|
40
|
+
if (Array.isArray(value)) {
|
|
41
|
+
return value.find((item) => item.trim().length > 0)?.trim();
|
|
42
|
+
}
|
|
43
|
+
const normalizedValue = value?.trim();
|
|
44
|
+
return normalizedValue && normalizedValue.length > 0 ? normalizedValue : undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
function getArgValue(flag: string): string | undefined {
|
|
37
48
|
const index = process.argv.indexOf(flag);
|
|
38
49
|
if (index === -1) return undefined;
|
|
@@ -61,8 +72,7 @@ async function main() {
|
|
|
61
72
|
},
|
|
62
73
|
);
|
|
63
74
|
|
|
64
|
-
const
|
|
65
|
-
const linkedApiServer = new LinkedApiMCPServer(progressCallback);
|
|
75
|
+
const linkedApiServer = new LinkedApiMCPServer();
|
|
66
76
|
|
|
67
77
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
68
78
|
const tools = linkedApiServer.getTools();
|
|
@@ -118,22 +128,49 @@ async function main() {
|
|
|
118
128
|
const localLinkedApiToken = process.env.LINKED_API_TOKEN;
|
|
119
129
|
const localIdentificationToken = process.env.IDENTIFICATION_TOKEN;
|
|
120
130
|
const headers = extra?.requestInfo?.headers ?? {};
|
|
121
|
-
const linkedApiToken =
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
'')
|
|
125
|
-
let mcpClient = (headers['client'] ?? ''
|
|
131
|
+
const linkedApiToken =
|
|
132
|
+
normalizeHeaderValue(headers['linked-api-token']) ?? localLinkedApiToken ?? '';
|
|
133
|
+
const identificationToken =
|
|
134
|
+
normalizeHeaderValue(headers['identification-token']) ?? localIdentificationToken ?? '';
|
|
135
|
+
let mcpClient = normalizeHeaderValue(headers['client']) ?? '';
|
|
126
136
|
if (!mcpClient) {
|
|
127
|
-
const userAgentHeader = headers['user-agent'];
|
|
128
|
-
if (
|
|
137
|
+
const userAgentHeader = normalizeHeaderValue(headers['user-agent']);
|
|
138
|
+
if (userAgentHeader) {
|
|
129
139
|
mcpClient = deriveClientFromUserAgent(userAgentHeader);
|
|
130
140
|
}
|
|
131
141
|
}
|
|
142
|
+
const progressCallback = (notification: LinkedApiProgressNotification): void => {
|
|
143
|
+
const { progressToken, progress, total, message } = notification;
|
|
144
|
+
if (progressToken === undefined) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
void extra
|
|
149
|
+
.sendNotification({
|
|
150
|
+
method: 'notifications/progress',
|
|
151
|
+
params: {
|
|
152
|
+
progressToken,
|
|
153
|
+
progress,
|
|
154
|
+
total,
|
|
155
|
+
message,
|
|
156
|
+
},
|
|
157
|
+
})
|
|
158
|
+
.catch((error: unknown) => {
|
|
159
|
+
logger.warn(
|
|
160
|
+
{
|
|
161
|
+
toolName: request.params.name,
|
|
162
|
+
error: error instanceof Error ? error.message : String(error),
|
|
163
|
+
},
|
|
164
|
+
'Failed to send MCP progress notification',
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
};
|
|
132
168
|
|
|
133
169
|
const result = await linkedApiServer.executeWithTokens(request.params, {
|
|
134
170
|
linkedApiToken,
|
|
135
171
|
identificationToken,
|
|
136
172
|
mcpClient,
|
|
173
|
+
progressCallback,
|
|
137
174
|
});
|
|
138
175
|
return result;
|
|
139
176
|
} catch (error) {
|
package/src/linked-api-server.ts
CHANGED
|
@@ -12,22 +12,47 @@ import {
|
|
|
12
12
|
LinkedApiProgressNotification,
|
|
13
13
|
} from './utils/types';
|
|
14
14
|
|
|
15
|
+
const BACKGROUND_WORKFLOW_DESCRIPTION =
|
|
16
|
+
`Linked API actions are queued into a cloud-browser workflow and may take several minutes. This is normal LinkedIn automation behavior, not a failed request. If the response contains workflowId and operationName, do not retry this tool; call get_workflow_result with those exact values until the final result is returned.` as const;
|
|
17
|
+
const NON_WORKFLOW_TOOL_NAMES = new Set<string>(['get_workflow_result', 'get_api_usage'] as const);
|
|
18
|
+
const NOOP_PROGRESS_CALLBACK = (_notification: LinkedApiProgressNotification): void => {};
|
|
19
|
+
|
|
20
|
+
interface TExecuteWithTokensOptions extends TLinkedApiConfig {
|
|
21
|
+
mcpClient: string;
|
|
22
|
+
progressCallback?: (notification: LinkedApiProgressNotification) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
15
25
|
export class LinkedApiMCPServer {
|
|
16
26
|
private tools: LinkedApiTools;
|
|
17
27
|
|
|
18
|
-
constructor(
|
|
19
|
-
this.tools = new LinkedApiTools(
|
|
28
|
+
constructor() {
|
|
29
|
+
this.tools = new LinkedApiTools();
|
|
20
30
|
}
|
|
21
31
|
|
|
22
32
|
public getTools(): Tool[] {
|
|
23
|
-
const linkedApiTools = this.tools.tools.map((tool) =>
|
|
33
|
+
const linkedApiTools = this.tools.tools.map((tool) => {
|
|
34
|
+
const definition = tool.getTool();
|
|
35
|
+
if (NON_WORKFLOW_TOOL_NAMES.has(definition.name)) {
|
|
36
|
+
return definition;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
...definition,
|
|
41
|
+
description: `${definition.description}\n\n${BACKGROUND_WORKFLOW_DESCRIPTION}`,
|
|
42
|
+
};
|
|
43
|
+
});
|
|
24
44
|
const adminTools = this.tools.adminTools.map((tool) => tool.getTool());
|
|
25
45
|
return [...linkedApiTools, ...adminTools];
|
|
26
46
|
}
|
|
27
47
|
|
|
28
48
|
public async executeWithTokens(
|
|
29
49
|
request: ExtendedCallToolRequest['params'],
|
|
30
|
-
{
|
|
50
|
+
{
|
|
51
|
+
linkedApiToken,
|
|
52
|
+
identificationToken,
|
|
53
|
+
mcpClient,
|
|
54
|
+
progressCallback = NOOP_PROGRESS_CALLBACK,
|
|
55
|
+
}: TExecuteWithTokensOptions,
|
|
31
56
|
): Promise<CallToolResult> {
|
|
32
57
|
const workflowTimeout = defineRequestTimeoutInSeconds(mcpClient) * 1000;
|
|
33
58
|
logger.info(
|
|
@@ -46,22 +71,41 @@ export class LinkedApiMCPServer {
|
|
|
46
71
|
try {
|
|
47
72
|
const adminTool = this.tools.adminToolByName(toolName);
|
|
48
73
|
if (adminTool) {
|
|
49
|
-
const admin = new LinkedApiAdmin({
|
|
50
|
-
|
|
74
|
+
const admin = new LinkedApiAdmin({
|
|
75
|
+
linkedApiToken,
|
|
76
|
+
client: 'mcp',
|
|
77
|
+
});
|
|
51
78
|
const params = adminTool.validate(args);
|
|
52
|
-
const result = await adminTool.execute({
|
|
53
|
-
|
|
79
|
+
const result = await adminTool.execute({
|
|
80
|
+
admin,
|
|
81
|
+
args: params,
|
|
82
|
+
});
|
|
54
83
|
const duration = this.calculateDuration(startTime);
|
|
55
|
-
logger.info(
|
|
56
|
-
|
|
57
|
-
|
|
84
|
+
logger.info(
|
|
85
|
+
{
|
|
86
|
+
toolName,
|
|
87
|
+
duration,
|
|
88
|
+
data: result,
|
|
89
|
+
},
|
|
90
|
+
'Tool execution successful',
|
|
91
|
+
);
|
|
58
92
|
if (result === undefined) {
|
|
59
|
-
return {
|
|
60
|
-
|
|
93
|
+
return {
|
|
94
|
+
content: [
|
|
95
|
+
{
|
|
96
|
+
type: 'text' as const,
|
|
97
|
+
text: 'Completed',
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
61
101
|
}
|
|
62
102
|
return {
|
|
63
|
-
content: [
|
|
64
|
-
|
|
103
|
+
content: [
|
|
104
|
+
{
|
|
105
|
+
type: 'text' as const,
|
|
106
|
+
text: JSON.stringify(result, null, 2),
|
|
107
|
+
},
|
|
108
|
+
],
|
|
65
109
|
};
|
|
66
110
|
}
|
|
67
111
|
|
|
@@ -82,9 +126,10 @@ text: JSON.stringify(result, null, 2) }],
|
|
|
82
126
|
const params = tool.validate(args);
|
|
83
127
|
const { data, errors } = await tool.execute({
|
|
84
128
|
linkedapi,
|
|
85
|
-
args: params,
|
|
129
|
+
args: params as never,
|
|
86
130
|
workflowTimeout,
|
|
87
131
|
progressToken,
|
|
132
|
+
progressCallback,
|
|
88
133
|
});
|
|
89
134
|
const endTime = Date.now();
|
|
90
135
|
const duration = `${((endTime - startTime) / 1000).toFixed(2)} seconds`;
|
|
@@ -98,6 +143,7 @@ text: JSON.stringify(result, null, 2) }],
|
|
|
98
143
|
'Tool execution failed',
|
|
99
144
|
);
|
|
100
145
|
return {
|
|
146
|
+
isError: true,
|
|
101
147
|
content: [
|
|
102
148
|
{
|
|
103
149
|
type: 'text' as const,
|
|
@@ -151,6 +197,8 @@ text: JSON.stringify(result, null, 2) }],
|
|
|
151
197
|
text: JSON.stringify(body, null, 2),
|
|
152
198
|
},
|
|
153
199
|
],
|
|
200
|
+
structuredContent: body,
|
|
201
|
+
isError: error.type !== 'workflowTimeout',
|
|
154
202
|
};
|
|
155
203
|
}
|
|
156
204
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
package/src/linked-api-tools.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import LinkedApi, { TMappedResponse } from '@linkedapi/node';
|
|
2
|
+
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
|
|
1
4
|
import { AdminConnectAccountTool } from './tools/admin-connect-account.js';
|
|
2
5
|
import { AdminDisconnectAccountTool } from './tools/admin-disconnect-account.js';
|
|
3
6
|
import { AdminGetAccountsTool } from './tools/admin-get-accounts.js';
|
|
@@ -36,45 +39,63 @@ import { SendConnectionRequestTool } from './tools/send-connection-request.js';
|
|
|
36
39
|
import { SendMessageTool } from './tools/send-message.js';
|
|
37
40
|
import { WithdrawConnectionRequestTool } from './tools/withdraw-connection-request.js';
|
|
38
41
|
import { AdminTool } from './utils/admin-tool.js';
|
|
39
|
-
import { LinkedApiTool } from './utils/linked-api-tool.js';
|
|
40
42
|
import { LinkedApiProgressNotification } from './utils/types.js';
|
|
41
43
|
|
|
44
|
+
interface TRegisteredLinkedApiTool {
|
|
45
|
+
readonly name: string;
|
|
46
|
+
getTool(): Tool;
|
|
47
|
+
validate(args: unknown): unknown;
|
|
48
|
+
execute({
|
|
49
|
+
linkedapi,
|
|
50
|
+
args,
|
|
51
|
+
workflowTimeout,
|
|
52
|
+
progressToken,
|
|
53
|
+
progressCallback,
|
|
54
|
+
}: {
|
|
55
|
+
linkedapi: LinkedApi;
|
|
56
|
+
args: never;
|
|
57
|
+
workflowTimeout: number;
|
|
58
|
+
progressToken?: string | number;
|
|
59
|
+
progressCallback: (progress: LinkedApiProgressNotification) => void;
|
|
60
|
+
}): Promise<TMappedResponse<unknown>>;
|
|
61
|
+
}
|
|
62
|
+
|
|
42
63
|
export class LinkedApiTools {
|
|
43
|
-
public readonly tools: ReadonlyArray<
|
|
64
|
+
public readonly tools: ReadonlyArray<TRegisteredLinkedApiTool>;
|
|
44
65
|
public readonly adminTools: ReadonlyArray<AdminTool<unknown, unknown>>;
|
|
45
66
|
|
|
46
|
-
constructor(
|
|
67
|
+
constructor() {
|
|
47
68
|
this.tools = [
|
|
48
69
|
// Standard tools
|
|
49
|
-
new SendMessageTool(
|
|
50
|
-
new GetConversationTool(
|
|
51
|
-
new CheckConnectionStatusTool(
|
|
52
|
-
new RetrieveConnectionsTool(
|
|
53
|
-
new SendConnectionRequestTool(
|
|
54
|
-
new WithdrawConnectionRequestTool(
|
|
55
|
-
new RetrievePendingRequestsTool(
|
|
56
|
-
new RemoveConnectionTool(
|
|
57
|
-
new SearchCompaniesTool(
|
|
58
|
-
new SearchPeopleTool(
|
|
59
|
-
new FetchCompanyTool(
|
|
60
|
-
new FetchPersonTool(
|
|
61
|
-
new FetchPostTool(
|
|
62
|
-
new ReactToPostTool(
|
|
63
|
-
new CommentOnPostTool(
|
|
64
|
-
new CreatePostTool(
|
|
65
|
-
new RetrieveSSITool(
|
|
66
|
-
new RetrievePerformanceTool(
|
|
70
|
+
new SendMessageTool(),
|
|
71
|
+
new GetConversationTool(),
|
|
72
|
+
new CheckConnectionStatusTool(),
|
|
73
|
+
new RetrieveConnectionsTool(),
|
|
74
|
+
new SendConnectionRequestTool(),
|
|
75
|
+
new WithdrawConnectionRequestTool(),
|
|
76
|
+
new RetrievePendingRequestsTool(),
|
|
77
|
+
new RemoveConnectionTool(),
|
|
78
|
+
new SearchCompaniesTool(),
|
|
79
|
+
new SearchPeopleTool(),
|
|
80
|
+
new FetchCompanyTool(),
|
|
81
|
+
new FetchPersonTool(),
|
|
82
|
+
new FetchPostTool(),
|
|
83
|
+
new ReactToPostTool(),
|
|
84
|
+
new CommentOnPostTool(),
|
|
85
|
+
new CreatePostTool(),
|
|
86
|
+
new RetrieveSSITool(),
|
|
87
|
+
new RetrievePerformanceTool(),
|
|
67
88
|
// Sales Navigator tools
|
|
68
|
-
new NvSendMessageTool(
|
|
69
|
-
new NvGetConversationTool(
|
|
70
|
-
new NvSearchCompaniesTool(
|
|
71
|
-
new NvSearchPeopleTool(
|
|
72
|
-
new NvFetchCompanyTool(
|
|
73
|
-
new NvFetchPersonTool(
|
|
89
|
+
new NvSendMessageTool(),
|
|
90
|
+
new NvGetConversationTool(),
|
|
91
|
+
new NvSearchCompaniesTool(),
|
|
92
|
+
new NvSearchPeopleTool(),
|
|
93
|
+
new NvFetchCompanyTool(),
|
|
94
|
+
new NvFetchPersonTool(),
|
|
74
95
|
// Other tools
|
|
75
|
-
new ExecuteCustomWorkflowTool(
|
|
76
|
-
new GetWorkflowResultTool(
|
|
77
|
-
new GetApiUsageTool(
|
|
96
|
+
new ExecuteCustomWorkflowTool(),
|
|
97
|
+
new GetWorkflowResultTool(),
|
|
98
|
+
new GetApiUsageTool(),
|
|
78
99
|
];
|
|
79
100
|
|
|
80
101
|
this.adminTools = [
|
|
@@ -91,7 +112,7 @@ export class LinkedApiTools {
|
|
|
91
112
|
];
|
|
92
113
|
}
|
|
93
114
|
|
|
94
|
-
public toolByName(name: string):
|
|
115
|
+
public toolByName(name: string): TRegisteredLinkedApiTool | undefined {
|
|
95
116
|
return this.tools.find((tool) => tool.name === name);
|
|
96
117
|
}
|
|
97
118
|
|
|
@@ -16,7 +16,8 @@ export class CommentOnPostTool extends OperationTool<TCommentOnPostParams, unkno
|
|
|
16
16
|
public override getTool(): Tool {
|
|
17
17
|
return {
|
|
18
18
|
name: this.name,
|
|
19
|
-
description:
|
|
19
|
+
description:
|
|
20
|
+
'Allows you to leave a comment on a post (st.commentOnPost action). If this workflow is still running, do not retry this tool; retrying can post duplicate comments.',
|
|
20
21
|
inputSchema: {
|
|
21
22
|
type: 'object',
|
|
22
23
|
properties: {
|
package/src/tools/create-post.ts
CHANGED
|
@@ -26,7 +26,7 @@ export class CreatePostTool extends OperationTool<TCreatePostParams, unknown> {
|
|
|
26
26
|
return {
|
|
27
27
|
name: this.name,
|
|
28
28
|
description:
|
|
29
|
-
'Creates a new LinkedIn post with optional media attachments (st.createPost action).',
|
|
29
|
+
'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.',
|
|
30
30
|
inputSchema: {
|
|
31
31
|
type: 'object',
|
|
32
32
|
properties: {
|
|
@@ -17,7 +17,8 @@ export class ExecuteCustomWorkflowTool extends OperationTool<
|
|
|
17
17
|
public override getTool(): Tool {
|
|
18
18
|
return {
|
|
19
19
|
name: this.name,
|
|
20
|
-
description:
|
|
20
|
+
description:
|
|
21
|
+
'Execute a custom workflow definition. If this workflow is still running, do not retry this tool; retrying can duplicate any write actions inside the custom workflow.',
|
|
21
22
|
inputSchema: {
|
|
22
23
|
type: 'object',
|
|
23
24
|
properties: { definition: { type: 'object' } },
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import LinkedApi, {
|
|
1
|
+
import LinkedApi, {
|
|
2
|
+
LinkedApiWorkflowTimeoutError,
|
|
3
|
+
TConversationPollResult,
|
|
4
|
+
TMappedResponse,
|
|
5
|
+
} from '@linkedapi/node';
|
|
2
6
|
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
7
|
import z from 'zod';
|
|
4
8
|
|
|
9
|
+
import { executeWithProgress } from '../utils/execute-with-progress.js';
|
|
5
10
|
import { LinkedApiTool } from '../utils/linked-api-tool.js';
|
|
11
|
+
import { LinkedApiProgressNotification } from '../utils/types.js';
|
|
6
12
|
|
|
7
13
|
export class GetConversationTool extends LinkedApiTool<
|
|
8
14
|
{ personUrl: string; since?: string },
|
|
@@ -17,18 +23,36 @@ export class GetConversationTool extends LinkedApiTool<
|
|
|
17
23
|
public override async execute({
|
|
18
24
|
linkedapi,
|
|
19
25
|
args: { personUrl, since },
|
|
26
|
+
workflowTimeout,
|
|
27
|
+
progressToken,
|
|
28
|
+
progressCallback,
|
|
20
29
|
}: {
|
|
21
30
|
linkedapi: LinkedApi;
|
|
22
31
|
args: { personUrl: string; since?: string };
|
|
23
32
|
workflowTimeout: number;
|
|
24
33
|
progressToken?: string | number;
|
|
34
|
+
progressCallback: (progress: LinkedApiProgressNotification) => void;
|
|
25
35
|
}): Promise<TMappedResponse<TConversationPollResult>> {
|
|
26
36
|
const conversations = await this.getConversation(linkedapi, personUrl, since);
|
|
27
37
|
if (conversations.errors.length === 0) {
|
|
28
38
|
return conversations;
|
|
29
39
|
}
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
try {
|
|
41
|
+
await executeWithProgress({
|
|
42
|
+
progressCallback,
|
|
43
|
+
operation: linkedapi.syncConversation,
|
|
44
|
+
workflowTimeout,
|
|
45
|
+
params: { personUrl },
|
|
46
|
+
progressToken,
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error instanceof LinkedApiWorkflowTimeoutError) {
|
|
50
|
+
error.message = `${error.message}
|
|
51
|
+
|
|
52
|
+
After get_workflow_result completes this sync workflow, call get_conversation again with the same personUrl to read the synchronized messages.`;
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
32
56
|
return await this.getConversation(linkedapi, personUrl, since);
|
|
33
57
|
}
|
|
34
58
|
|
|
@@ -4,6 +4,7 @@ import z from 'zod';
|
|
|
4
4
|
|
|
5
5
|
import { executeWithProgress } from '../utils/execute-with-progress.js';
|
|
6
6
|
import { LinkedApiTool } from '../utils/linked-api-tool.js';
|
|
7
|
+
import { LinkedApiProgressNotification } from '../utils/types.js';
|
|
7
8
|
|
|
8
9
|
interface IGetWorkflowResultParams {
|
|
9
10
|
workflowId: string;
|
|
@@ -22,16 +23,21 @@ export class GetWorkflowResultTool extends LinkedApiTool<IGetWorkflowResultParam
|
|
|
22
23
|
args: { workflowId, operationName },
|
|
23
24
|
workflowTimeout,
|
|
24
25
|
progressToken,
|
|
26
|
+
progressCallback,
|
|
25
27
|
}: {
|
|
26
28
|
linkedapi: LinkedApi;
|
|
27
29
|
args: IGetWorkflowResultParams;
|
|
28
30
|
workflowTimeout: number;
|
|
29
31
|
progressToken?: string | number;
|
|
32
|
+
progressCallback: (progress: LinkedApiProgressNotification) => void;
|
|
30
33
|
}): Promise<TMappedResponse<unknown>> {
|
|
31
34
|
const operation = linkedapi.operations.find(
|
|
32
35
|
(operation) => operation.operationName === operationName,
|
|
33
36
|
)!;
|
|
34
|
-
return await executeWithProgress(
|
|
37
|
+
return await executeWithProgress({
|
|
38
|
+
progressCallback,
|
|
39
|
+
operation,
|
|
40
|
+
workflowTimeout,
|
|
35
41
|
workflowId,
|
|
36
42
|
progressToken,
|
|
37
43
|
});
|
|
@@ -47,12 +53,13 @@ export class GetWorkflowResultTool extends LinkedApiTool<IGetWorkflowResultParam
|
|
|
47
53
|
properties: {
|
|
48
54
|
workflowId: {
|
|
49
55
|
type: 'string',
|
|
50
|
-
description:
|
|
56
|
+
description:
|
|
57
|
+
'Required. The workflow ID provided in the background workflow status message.',
|
|
51
58
|
},
|
|
52
59
|
operationName: {
|
|
53
60
|
type: 'string',
|
|
54
61
|
description:
|
|
55
|
-
'
|
|
62
|
+
'Required. The operationName provided in the background workflow status message. Use the exact value so the result can be restored correctly.',
|
|
56
63
|
},
|
|
57
64
|
},
|
|
58
65
|
required: ['workflowId', 'operationName'],
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import LinkedApi, {
|
|
1
|
+
import LinkedApi, {
|
|
2
|
+
LinkedApiWorkflowTimeoutError,
|
|
3
|
+
TConversationPollResult,
|
|
4
|
+
TMappedResponse,
|
|
5
|
+
} from '@linkedapi/node';
|
|
2
6
|
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
7
|
import z from 'zod';
|
|
4
8
|
|
|
9
|
+
import { executeWithProgress } from '../utils/execute-with-progress.js';
|
|
5
10
|
import { LinkedApiTool } from '../utils/linked-api-tool.js';
|
|
11
|
+
import { LinkedApiProgressNotification } from '../utils/types.js';
|
|
6
12
|
|
|
7
13
|
export class NvGetConversationTool extends LinkedApiTool<
|
|
8
14
|
{ personUrl: string; since?: string },
|
|
@@ -17,18 +23,36 @@ export class NvGetConversationTool extends LinkedApiTool<
|
|
|
17
23
|
public override async execute({
|
|
18
24
|
linkedapi,
|
|
19
25
|
args: { personUrl, since },
|
|
26
|
+
workflowTimeout,
|
|
27
|
+
progressToken,
|
|
28
|
+
progressCallback,
|
|
20
29
|
}: {
|
|
21
30
|
linkedapi: LinkedApi;
|
|
22
31
|
args: { personUrl: string; since?: string };
|
|
23
32
|
workflowTimeout: number;
|
|
24
33
|
progressToken?: string | number;
|
|
34
|
+
progressCallback: (progress: LinkedApiProgressNotification) => void;
|
|
25
35
|
}): Promise<TMappedResponse<TConversationPollResult>> {
|
|
26
36
|
const conversations = await this.getConversation(linkedapi, personUrl, since);
|
|
27
37
|
if (conversations.errors.length === 0) {
|
|
28
38
|
return conversations;
|
|
29
39
|
}
|
|
30
|
-
|
|
31
|
-
|
|
40
|
+
try {
|
|
41
|
+
await executeWithProgress({
|
|
42
|
+
progressCallback,
|
|
43
|
+
operation: linkedapi.nvSyncConversation,
|
|
44
|
+
workflowTimeout,
|
|
45
|
+
params: { personUrl },
|
|
46
|
+
progressToken,
|
|
47
|
+
});
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if (error instanceof LinkedApiWorkflowTimeoutError) {
|
|
50
|
+
error.message = `${error.message}
|
|
51
|
+
|
|
52
|
+
After get_workflow_result completes this sync workflow, call nv_get_conversation again with the same personUrl to read the synchronized messages.`;
|
|
53
|
+
}
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
32
56
|
return await this.getConversation(linkedapi, personUrl, since);
|
|
33
57
|
}
|
|
34
58
|
|
|
@@ -17,7 +17,7 @@ export class NvSendMessageTool extends OperationTool<TNvSendMessageParams, unkno
|
|
|
17
17
|
return {
|
|
18
18
|
name: this.name,
|
|
19
19
|
description:
|
|
20
|
-
'Allows you to send a message to a person in Sales Navigator (nv.sendMessage action)',
|
|
20
|
+
'Allows you to send a message to a person in Sales Navigator (nv.sendMessage action). If this workflow is still running, do not retry this tool; retrying can send duplicate Sales Navigator messages to the same person.',
|
|
21
21
|
inputSchema: {
|
|
22
22
|
type: 'object',
|
|
23
23
|
properties: {
|
|
@@ -17,7 +17,7 @@ export class ReactToPostTool extends OperationTool<TReactToPostParams, unknown>
|
|
|
17
17
|
return {
|
|
18
18
|
name: this.name,
|
|
19
19
|
description:
|
|
20
|
-
'Allows you to react to a post using any available reaction type (st.reactToPost action).',
|
|
20
|
+
'Allows you to react to a post using any available reaction type (st.reactToPost action). If this workflow is still running, do not retry this tool; retrying can queue duplicate reaction attempts.',
|
|
21
21
|
inputSchema: {
|
|
22
22
|
type: 'object',
|
|
23
23
|
properties: {
|
|
@@ -15,7 +15,7 @@ export class RemoveConnectionTool extends OperationTool<TRemoveConnectionParams,
|
|
|
15
15
|
return {
|
|
16
16
|
name: this.name,
|
|
17
17
|
description:
|
|
18
|
-
'Allows you to remove a person from your connections (st.removeConnection action).',
|
|
18
|
+
'Allows you to remove a person from your connections (st.removeConnection action). If this workflow is still running, do not retry this tool; retrying can queue duplicate removal attempts for the same person.',
|
|
19
19
|
inputSchema: {
|
|
20
20
|
type: 'object',
|
|
21
21
|
properties: {
|
|
@@ -20,7 +20,7 @@ export class SendConnectionRequestTool extends OperationTool<
|
|
|
20
20
|
return {
|
|
21
21
|
name: this.name,
|
|
22
22
|
description:
|
|
23
|
-
'Allows you to send a connection request to a person (st.sendConnectionRequest action).',
|
|
23
|
+
'Allows you to send a connection request to a person (st.sendConnectionRequest action). If this workflow is still running, do not retry this tool; retrying can queue duplicate connection request attempts for the same person.',
|
|
24
24
|
inputSchema: {
|
|
25
25
|
type: 'object',
|
|
26
26
|
properties: {
|
|
@@ -15,7 +15,8 @@ export class SendMessageTool extends OperationTool<TSendMessageParams, unknown>
|
|
|
15
15
|
public override getTool(): Tool {
|
|
16
16
|
return {
|
|
17
17
|
name: this.name,
|
|
18
|
-
description:
|
|
18
|
+
description:
|
|
19
|
+
'Allows you to send a message to a person (st.sendMessage action). If this workflow is still running, do not retry this tool; retrying can send duplicate messages to the same person.',
|
|
19
20
|
inputSchema: {
|
|
20
21
|
type: 'object',
|
|
21
22
|
properties: {
|
|
@@ -19,7 +19,7 @@ export class WithdrawConnectionRequestTool extends OperationTool<
|
|
|
19
19
|
return {
|
|
20
20
|
name: this.name,
|
|
21
21
|
description:
|
|
22
|
-
'Allows you to withdraw the connection request sent to a person (st.withdrawConnectionRequest action).',
|
|
22
|
+
'Allows you to withdraw the connection request sent to a person (st.withdrawConnectionRequest action). If this workflow is still running, do not retry this tool; retrying can queue duplicate withdrawal attempts for the same person.',
|
|
23
23
|
inputSchema: {
|
|
24
24
|
type: 'object',
|
|
25
25
|
properties: {
|
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
export function defineRequestTimeoutInSeconds(mcpClient: string): number {
|
|
2
|
-
const
|
|
2
|
+
const BIG_TIMEOUT = 600 as const;
|
|
3
|
+
const normalizedMcpClient = mcpClient.trim().toLowerCase();
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
switch (normalizedMcpClient) {
|
|
6
|
+
case 'codex':
|
|
7
|
+
case 'codex-cli':
|
|
8
|
+
case 'codex-desktop':
|
|
9
|
+
case 'openai-codex':
|
|
10
|
+
return BIG_TIMEOUT;
|
|
11
|
+
case 'claude-code':
|
|
12
|
+
case 'claude_code':
|
|
13
|
+
case 'claude code':
|
|
14
|
+
return BIG_TIMEOUT;
|
|
6
15
|
case 'claude':
|
|
16
|
+
case 'claude-desktop':
|
|
17
|
+
case 'anthropic':
|
|
7
18
|
return 180;
|
|
8
19
|
case 'chatgpt':
|
|
20
|
+
case 'openai':
|
|
9
21
|
return 50;
|
|
10
22
|
case 'cursor':
|
|
11
23
|
case 'vscode':
|
|
12
24
|
case 'windsurf':
|
|
13
|
-
return
|
|
25
|
+
return BIG_TIMEOUT;
|
|
14
26
|
default:
|
|
15
27
|
return 60;
|
|
16
28
|
}
|