@linkedapi/mcp 1.0.0 → 2.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/README.md +9 -0
- package/dist/index.js +19 -9
- package/dist/index.js.map +1 -1
- package/dist/linked-api-server.d.ts +8 -5
- package/dist/linked-api-server.d.ts.map +1 -1
- package/dist/linked-api-server.js +63 -49
- package/dist/linked-api-server.js.map +1 -1
- package/dist/linked-api-tools.d.ts +17 -5
- 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/prompts/index.d.ts +2 -2
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js +3 -1
- package/dist/prompts/index.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-api-usage-stats.d.ts +0 -2
- package/dist/tools/get-api-usage-stats.d.ts.map +1 -1
- package/dist/tools/get-api-usage-stats.js.map +1 -1
- package/dist/tools/get-conversation.d.ts +3 -4
- package/dist/tools/get-conversation.d.ts.map +1 -1
- package/dist/tools/get-conversation.js +7 -6
- package/dist/tools/get-conversation.js.map +1 -1
- package/dist/tools/get-workflow-result.d.ts +6 -4
- package/dist/tools/get-workflow-result.d.ts.map +1 -1
- package/dist/tools/get-workflow-result.js +37 -10
- package/dist/tools/get-workflow-result.js.map +1 -1
- package/dist/tools/nv-get-conversation.d.ts +3 -4
- package/dist/tools/nv-get-conversation.d.ts.map +1 -1
- package/dist/tools/nv-get-conversation.js +7 -6
- 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/types/linked-api-tool-result.type.d.ts +6 -0
- package/dist/types/linked-api-tool-result.type.d.ts.map +1 -0
- package/dist/types/linked-api-tool-result.type.js +3 -0
- package/dist/types/linked-api-tool-result.type.js.map +1 -0
- 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/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 +0 -9
- 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 -16
- package/dist/utils/linked-api-tool.d.ts.map +1 -1
- package/dist/utils/linked-api-tool.js +4 -10
- package/dist/utils/linked-api-tool.js.map +1 -1
- package/dist/utils/types.d.ts +0 -6
- package/dist/utils/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +19 -11
- package/src/linked-api-server.ts +83 -57
- package/src/linked-api-tools.ts +45 -32
- package/src/prompts/index.ts +3 -1
- 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-api-usage-stats.ts +0 -2
- package/src/tools/get-conversation.ts +13 -10
- package/src/tools/get-workflow-result.ts +63 -17
- package/src/tools/nv-get-conversation.ts +13 -10
- 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/types/linked-api-tool-result.type.ts +12 -0
- package/src/utils/define-request-timeout.ts +16 -4
- package/src/utils/handle-linked-api-error.ts +2 -10
- package/src/utils/json-http-transport.ts +4 -3
- package/src/utils/linked-api-tool.ts +16 -32
- package/src/utils/types.ts +0 -6
- package/dist/utils/execute-with-progress.d.ts +0 -8
- package/dist/utils/execute-with-progress.d.ts.map +0 -1
- package/dist/utils/execute-with-progress.js +0 -75
- package/dist/utils/execute-with-progress.js.map +0 -1
- package/src/utils/execute-with-progress.ts +0 -93
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import LinkedApi, {
|
|
1
|
+
import LinkedApi, {
|
|
2
|
+
OPERATION_NAME,
|
|
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 type { TWorkflowAck } from '../types/linked-api-tool-result.type.js';
|
|
5
10
|
import { LinkedApiTool } from '../utils/linked-api-tool.js';
|
|
6
11
|
|
|
7
12
|
export class GetConversationTool extends LinkedApiTool<
|
|
@@ -20,19 +25,17 @@ export class GetConversationTool extends LinkedApiTool<
|
|
|
20
25
|
}: {
|
|
21
26
|
linkedapi: LinkedApi;
|
|
22
27
|
args: { personUrl: string; since?: string };
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}): Promise<TMappedResponse<TConversationPollResult>> {
|
|
26
|
-
const conversations = await this.getConversation(linkedapi, personUrl, since);
|
|
28
|
+
}): Promise<TMappedResponse<TConversationPollResult> | TWorkflowAck> {
|
|
29
|
+
const conversations = await this.pollConversation(linkedapi, personUrl, since);
|
|
27
30
|
if (conversations.errors.length === 0) {
|
|
28
31
|
return conversations;
|
|
29
32
|
}
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const ack = await linkedapi.syncConversation.execute({ personUrl });
|
|
34
|
+
return { ...ack,
|
|
35
|
+
operationName: OPERATION_NAME.syncConversation };
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
private async
|
|
38
|
+
private async pollConversation(
|
|
36
39
|
linkedapi: LinkedApi,
|
|
37
40
|
personUrl: string,
|
|
38
41
|
since?: string,
|
|
@@ -54,7 +57,7 @@ export class GetConversationTool extends LinkedApiTool<
|
|
|
54
57
|
return {
|
|
55
58
|
name: this.name,
|
|
56
59
|
description:
|
|
57
|
-
'
|
|
60
|
+
'Retrieve a conversation with a LinkedIn person via standard LinkedIn messaging. Returns the conversation immediately if it has already been synced. If the conversation has not been synced yet, this tool starts a syncConversation workflow and returns its ack ({status, workflowId, operationName: "syncConversation", message}); the client should call get_workflow_result with that workflowId until completion, then call get_conversation again to read the synced messages.',
|
|
58
61
|
inputSchema: {
|
|
59
62
|
type: 'object',
|
|
60
63
|
properties: {
|
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
import LinkedApi, {
|
|
1
|
+
import LinkedApi, {
|
|
2
|
+
Operation,
|
|
3
|
+
OPERATION_NAME,
|
|
4
|
+
TMappedResponse,
|
|
5
|
+
TOperationName,
|
|
6
|
+
TWorkflowInProgressResponse,
|
|
7
|
+
} from '@linkedapi/node';
|
|
2
8
|
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
9
|
import z from 'zod';
|
|
4
10
|
|
|
5
|
-
import {
|
|
11
|
+
import type { TWorkflowAck } from '../types/linked-api-tool-result.type.js';
|
|
12
|
+
import { defineRequestTimeoutInSeconds } from '../utils/define-request-timeout.js';
|
|
6
13
|
import { LinkedApiTool } from '../utils/linked-api-tool.js';
|
|
7
14
|
|
|
15
|
+
const POLL_INTERVAL_MS = 5000 as const;
|
|
16
|
+
const REQUEST_BUDGET_BUFFER_SECONDS = 5 as const;
|
|
17
|
+
|
|
8
18
|
interface IGetWorkflowResultParams {
|
|
9
19
|
workflowId: string;
|
|
10
20
|
operationName: string;
|
|
21
|
+
waitSeconds?: number;
|
|
11
22
|
}
|
|
12
23
|
|
|
13
24
|
export class GetWorkflowResultTool extends LinkedApiTool<IGetWorkflowResultParams, unknown> {
|
|
@@ -15,44 +26,66 @@ export class GetWorkflowResultTool extends LinkedApiTool<IGetWorkflowResultParam
|
|
|
15
26
|
protected readonly schema = z.object({
|
|
16
27
|
workflowId: z.string(),
|
|
17
28
|
operationName: z.enum(Object.values(OPERATION_NAME)),
|
|
29
|
+
waitSeconds: z.number().int().min(0).optional(),
|
|
18
30
|
});
|
|
19
31
|
|
|
20
32
|
public override async execute({
|
|
21
33
|
linkedapi,
|
|
22
|
-
args: { workflowId, operationName },
|
|
23
|
-
|
|
24
|
-
progressToken,
|
|
34
|
+
args: { workflowId, operationName, waitSeconds },
|
|
35
|
+
mcpClient,
|
|
25
36
|
}: {
|
|
26
37
|
linkedapi: LinkedApi;
|
|
27
38
|
args: IGetWorkflowResultParams;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
39
|
+
mcpClient: string;
|
|
40
|
+
}): Promise<TMappedResponse<unknown> | TWorkflowAck> {
|
|
41
|
+
const typedOperationName = operationName as TOperationName;
|
|
31
42
|
const operation = linkedapi.operations.find(
|
|
32
|
-
(operation) => operation.operationName ===
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
43
|
+
(operation) => operation.operationName === typedOperationName,
|
|
44
|
+
)! as Operation<unknown, unknown>;
|
|
45
|
+
|
|
46
|
+
const cap = Math.max(
|
|
47
|
+
defineRequestTimeoutInSeconds(mcpClient) - REQUEST_BUDGET_BUFFER_SECONDS,
|
|
48
|
+
0,
|
|
49
|
+
);
|
|
50
|
+
const budgetSeconds = Math.min(waitSeconds ?? cap, cap);
|
|
51
|
+
const deadline = Date.now() + budgetSeconds * 1000;
|
|
52
|
+
|
|
53
|
+
while (true) {
|
|
54
|
+
const status = await operation.status(workflowId);
|
|
55
|
+
if (!isWorkflowInProgress(status)) {
|
|
56
|
+
return status;
|
|
57
|
+
}
|
|
58
|
+
const remainingMs = deadline - Date.now();
|
|
59
|
+
if (remainingMs <= 0) {
|
|
60
|
+
return { ...status,
|
|
61
|
+
workflowId,
|
|
62
|
+
operationName: typedOperationName };
|
|
63
|
+
}
|
|
64
|
+
await sleep(Math.min(POLL_INTERVAL_MS, remainingMs));
|
|
65
|
+
}
|
|
38
66
|
}
|
|
39
67
|
|
|
40
68
|
public override getTool(): Tool {
|
|
41
69
|
return {
|
|
42
70
|
name: this.name,
|
|
43
71
|
description:
|
|
44
|
-
'
|
|
72
|
+
'Check the current state of a previously started Linked API workflow. Returns the final result when the workflow has completed, otherwise returns the current in-progress snapshot ({status, workflowId, operationName, message}). The server long-polls up to waitSeconds while the workflow is still running; if the budget elapses without completion, the in-progress snapshot is returned and the client should call this tool again with the same workflowId and operationName.',
|
|
45
73
|
inputSchema: {
|
|
46
74
|
type: 'object',
|
|
47
75
|
properties: {
|
|
48
76
|
workflowId: {
|
|
49
77
|
type: 'string',
|
|
50
|
-
description: 'The workflow ID
|
|
78
|
+
description: 'Required. The workflow ID returned by the original Linked API tool call.',
|
|
51
79
|
},
|
|
52
80
|
operationName: {
|
|
53
81
|
type: 'string',
|
|
54
82
|
description:
|
|
55
|
-
'
|
|
83
|
+
'Required. The operationName returned by the original Linked API tool call. Used to map the response to the right operation.',
|
|
84
|
+
},
|
|
85
|
+
waitSeconds: {
|
|
86
|
+
type: 'number',
|
|
87
|
+
description:
|
|
88
|
+
'Optional. Maximum seconds the server should wait for the workflow to complete before returning the current in-progress snapshot. Defaults to the maximum the current MCP client allows; pass 0 to get an immediate one-shot snapshot.',
|
|
56
89
|
},
|
|
57
90
|
},
|
|
58
91
|
required: ['workflowId', 'operationName'],
|
|
@@ -60,3 +93,16 @@ export class GetWorkflowResultTool extends LinkedApiTool<IGetWorkflowResultParam
|
|
|
60
93
|
};
|
|
61
94
|
}
|
|
62
95
|
}
|
|
96
|
+
|
|
97
|
+
function isWorkflowInProgress(
|
|
98
|
+
result: TMappedResponse<unknown> | TWorkflowInProgressResponse,
|
|
99
|
+
): result is TWorkflowInProgressResponse {
|
|
100
|
+
return (
|
|
101
|
+
'workflowStatus' in result &&
|
|
102
|
+
(result.workflowStatus === 'running' || result.workflowStatus === 'pending')
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function sleep(ms: number): Promise<void> {
|
|
107
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
108
|
+
}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
import LinkedApi, {
|
|
1
|
+
import LinkedApi, {
|
|
2
|
+
OPERATION_NAME,
|
|
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 type { TWorkflowAck } from '../types/linked-api-tool-result.type.js';
|
|
5
10
|
import { LinkedApiTool } from '../utils/linked-api-tool.js';
|
|
6
11
|
|
|
7
12
|
export class NvGetConversationTool extends LinkedApiTool<
|
|
@@ -20,19 +25,17 @@ export class NvGetConversationTool extends LinkedApiTool<
|
|
|
20
25
|
}: {
|
|
21
26
|
linkedapi: LinkedApi;
|
|
22
27
|
args: { personUrl: string; since?: string };
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}): Promise<TMappedResponse<TConversationPollResult>> {
|
|
26
|
-
const conversations = await this.getConversation(linkedapi, personUrl, since);
|
|
28
|
+
}): Promise<TMappedResponse<TConversationPollResult> | TWorkflowAck> {
|
|
29
|
+
const conversations = await this.pollConversation(linkedapi, personUrl, since);
|
|
27
30
|
if (conversations.errors.length === 0) {
|
|
28
31
|
return conversations;
|
|
29
32
|
}
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const ack = await linkedapi.nvSyncConversation.execute({ personUrl });
|
|
34
|
+
return { ...ack,
|
|
35
|
+
operationName: OPERATION_NAME.nvSyncConversation };
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
private async
|
|
38
|
+
private async pollConversation(
|
|
36
39
|
linkedapi: LinkedApi,
|
|
37
40
|
personUrl: string,
|
|
38
41
|
since?: string,
|
|
@@ -54,7 +57,7 @@ export class NvGetConversationTool extends LinkedApiTool<
|
|
|
54
57
|
return {
|
|
55
58
|
name: this.name,
|
|
56
59
|
description:
|
|
57
|
-
'
|
|
60
|
+
'Retrieve a conversation with a LinkedIn person via Sales Navigator messaging. Returns the conversation immediately if it has already been synced. If the conversation has not been synced yet, this tool starts an nvSyncConversation workflow and returns its ack ({status, workflowId, operationName: "nvSyncConversation", message}); the client should call get_workflow_result with that workflowId until completion, then call nv_get_conversation again to read the synced messages.',
|
|
58
61
|
inputSchema: {
|
|
59
62
|
type: 'object',
|
|
60
63
|
properties: {
|
|
@@ -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: {
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
TMappedResponse,
|
|
3
|
+
TOperationName,
|
|
4
|
+
TWorkflowInProgressResponse,
|
|
5
|
+
TWorkflowStartedResponse,
|
|
6
|
+
} from '@linkedapi/node';
|
|
7
|
+
|
|
8
|
+
export type TWorkflowAck = (TWorkflowStartedResponse | TWorkflowInProgressResponse) & {
|
|
9
|
+
operationName: TOperationName;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type TLinkedApiToolResult<TResult> = TWorkflowAck | TMappedResponse<TResult>;
|
|
@@ -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
|
}
|
|
@@ -1,16 +1,8 @@
|
|
|
1
|
-
import { LinkedApiError
|
|
1
|
+
import { LinkedApiError } from '@linkedapi/node';
|
|
2
2
|
|
|
3
3
|
import { authenticationPrompt } from '../prompts';
|
|
4
4
|
|
|
5
|
-
export function handleLinkedApiError(error: LinkedApiError):
|
|
6
|
-
if (error instanceof LinkedApiWorkflowTimeoutError) {
|
|
7
|
-
const { message, workflowId, operationName } = error;
|
|
8
|
-
return {
|
|
9
|
-
message,
|
|
10
|
-
workflowId,
|
|
11
|
-
operationName,
|
|
12
|
-
};
|
|
13
|
-
}
|
|
5
|
+
export function handleLinkedApiError(error: LinkedApiError): Record<string, unknown> {
|
|
14
6
|
switch (error.type) {
|
|
15
7
|
case 'identificationTokenRequired':
|
|
16
8
|
case 'linkedApiTokenRequired':
|
|
@@ -77,12 +77,13 @@ export class JsonHTTPServerTransport implements Transport {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
async send(message: JSONRPCMessage, options?: TransportSendOptions): Promise<void> {
|
|
80
|
+
const isResponse = isJSONRPCResponse(message) || isJSONRPCError(message);
|
|
80
81
|
let relatedId = options?.relatedRequestId;
|
|
81
|
-
if (
|
|
82
|
+
if (isResponse) {
|
|
82
83
|
relatedId = message.id;
|
|
83
84
|
}
|
|
84
|
-
// If a related HTTP connection is pending, complete it
|
|
85
|
-
if (relatedId !== undefined) {
|
|
85
|
+
// If a related HTTP connection is pending, only final responses complete it.
|
|
86
|
+
if (isResponse && relatedId !== undefined) {
|
|
86
87
|
const connId = this.requestIdToConn.get(relatedId);
|
|
87
88
|
if (connId) {
|
|
88
89
|
const ctx = this.connections.get(connId);
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import LinkedApi, { Operation,
|
|
1
|
+
import LinkedApi, { Operation, TOperationName } from '@linkedapi/node';
|
|
2
2
|
import { Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
-
import { LinkedApiProgressNotification } from 'src/utils/types';
|
|
4
3
|
import z from 'zod';
|
|
5
4
|
|
|
6
|
-
import {
|
|
5
|
+
import type { TLinkedApiToolResult, TWorkflowAck } from '../types/linked-api-tool-result.type.js';
|
|
6
|
+
|
|
7
|
+
interface TLinkedApiToolExecuteOptions<TParams> {
|
|
8
|
+
linkedapi: LinkedApi;
|
|
9
|
+
args: TParams;
|
|
10
|
+
mcpClient: string;
|
|
11
|
+
}
|
|
7
12
|
|
|
8
13
|
export abstract class LinkedApiTool<TParams, TResult> {
|
|
9
14
|
public abstract readonly name: string;
|
|
10
15
|
protected abstract readonly schema: z.ZodSchema;
|
|
11
|
-
protected readonly progressCallback: (progress: LinkedApiProgressNotification) => void;
|
|
12
|
-
|
|
13
|
-
constructor(progressCallback: (progress: LinkedApiProgressNotification) => void) {
|
|
14
|
-
this.progressCallback = progressCallback;
|
|
15
|
-
}
|
|
16
16
|
|
|
17
17
|
public abstract getTool(): Tool;
|
|
18
18
|
|
|
@@ -20,39 +20,23 @@ export abstract class LinkedApiTool<TParams, TResult> {
|
|
|
20
20
|
return this.schema.parse(args) as TParams;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
public abstract execute(
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
workflowTimeout,
|
|
27
|
-
progressToken,
|
|
28
|
-
}: {
|
|
29
|
-
linkedapi: LinkedApi;
|
|
30
|
-
args: TParams;
|
|
31
|
-
workflowTimeout: number;
|
|
32
|
-
progressToken?: string | number;
|
|
33
|
-
}): Promise<TMappedResponse<TResult>>;
|
|
23
|
+
public abstract execute(
|
|
24
|
+
options: TLinkedApiToolExecuteOptions<TParams>,
|
|
25
|
+
): Promise<TLinkedApiToolResult<TResult>>;
|
|
34
26
|
}
|
|
35
27
|
|
|
36
28
|
export abstract class OperationTool<TParams, TResult> extends LinkedApiTool<TParams, TResult> {
|
|
37
29
|
public abstract readonly operationName: TOperationName;
|
|
38
30
|
|
|
39
|
-
public override execute({
|
|
31
|
+
public override async execute({
|
|
40
32
|
linkedapi,
|
|
41
33
|
args,
|
|
42
|
-
|
|
43
|
-
progressToken,
|
|
44
|
-
}: {
|
|
45
|
-
linkedapi: LinkedApi;
|
|
46
|
-
args: TParams;
|
|
47
|
-
workflowTimeout: number;
|
|
48
|
-
progressToken?: string | number;
|
|
49
|
-
}): Promise<TMappedResponse<TResult>> {
|
|
34
|
+
}: TLinkedApiToolExecuteOptions<TParams>): Promise<TWorkflowAck> {
|
|
50
35
|
const operation = linkedapi.operations.find(
|
|
51
36
|
(operation) => operation.operationName === this.operationName,
|
|
52
37
|
)! as Operation<TParams, TResult>;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
});
|
|
38
|
+
const response = await operation.execute(args);
|
|
39
|
+
return { ...response,
|
|
40
|
+
operationName: this.operationName };
|
|
57
41
|
}
|
|
58
42
|
}
|
package/src/utils/types.ts
CHANGED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { Operation, TMappedResponse } from '@linkedapi/node';
|
|
2
|
-
import { LinkedApiProgressNotification } from './types';
|
|
3
|
-
export declare function executeWithProgress<TParams, TResult>(progressCallback: (progress: LinkedApiProgressNotification) => void, operation: Operation<TParams, TResult>, workflowTimeout: number, { params, workflowId, progressToken, }?: {
|
|
4
|
-
params?: TParams;
|
|
5
|
-
workflowId?: string;
|
|
6
|
-
progressToken?: string | number;
|
|
7
|
-
}): Promise<TMappedResponse<TResult>>;
|
|
8
|
-
//# sourceMappingURL=execute-with-progress.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"execute-with-progress.d.ts","sourceRoot":"","sources":["../../src/utils/execute-with-progress.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiC,SAAS,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAE5F,OAAO,EAAE,6BAA6B,EAAE,MAAM,SAAS,CAAC;AAExD,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EACxD,gBAAgB,EAAE,CAAC,QAAQ,EAAE,6BAA6B,KAAK,IAAI,EACnE,SAAS,EAAE,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,EACtC,eAAe,EAAE,MAAM,EACvB,EACE,MAAM,EACN,UAAU,EACV,aAAa,GACd,GAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAAO,GACjF,OAAO,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAqDnC"}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.executeWithProgress = executeWithProgress;
|
|
4
|
-
const node_1 = require("@linkedapi/node");
|
|
5
|
-
async function executeWithProgress(progressCallback, operation, workflowTimeout, { params, workflowId, progressToken, } = {}) {
|
|
6
|
-
let progress = 0;
|
|
7
|
-
progressCallback({
|
|
8
|
-
progressToken,
|
|
9
|
-
progress,
|
|
10
|
-
total: 100,
|
|
11
|
-
message: `Starting workflow ${operation.operationName}...`,
|
|
12
|
-
});
|
|
13
|
-
const interval = setInterval(() => {
|
|
14
|
-
if (progress < 50) {
|
|
15
|
-
progress += 5;
|
|
16
|
-
}
|
|
17
|
-
else if (progress < 98) {
|
|
18
|
-
progress += 1;
|
|
19
|
-
}
|
|
20
|
-
progressCallback({
|
|
21
|
-
progressToken,
|
|
22
|
-
progress,
|
|
23
|
-
total: 100,
|
|
24
|
-
message: `Executing workflow ${operation.operationName}...`,
|
|
25
|
-
});
|
|
26
|
-
}, Math.max(workflowTimeout / 20, 10000));
|
|
27
|
-
try {
|
|
28
|
-
if (!workflowId) {
|
|
29
|
-
workflowId = await operation.execute(params);
|
|
30
|
-
}
|
|
31
|
-
const result = await operation.result(workflowId, {
|
|
32
|
-
timeout: workflowTimeout,
|
|
33
|
-
});
|
|
34
|
-
clearInterval(interval);
|
|
35
|
-
progressCallback({
|
|
36
|
-
progressToken,
|
|
37
|
-
progress: 100,
|
|
38
|
-
total: 100,
|
|
39
|
-
message: `Workflow ${operation.operationName} completed successfully`,
|
|
40
|
-
});
|
|
41
|
-
return result;
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
clearInterval(interval);
|
|
45
|
-
if (error instanceof node_1.LinkedApiWorkflowTimeoutError) {
|
|
46
|
-
throw generateTimeoutError(error);
|
|
47
|
-
}
|
|
48
|
-
throw error;
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function generateTimeoutError(error) {
|
|
52
|
-
const restoreMessage = `Workflow is running in the background. Continue listening for updates.
|
|
53
|
-
|
|
54
|
-
ACTION REQUIRED (MCP CLIENT):
|
|
55
|
-
- Do not retry the original tool; the workflow is already running
|
|
56
|
-
- Call the get_workflow_result tool with the parameters below and keep listening until completion
|
|
57
|
-
|
|
58
|
-
PARAMETERS TO USE:
|
|
59
|
-
- workflowId: ${error.workflowId}
|
|
60
|
-
- operationName: ${error.operationName}
|
|
61
|
-
|
|
62
|
-
GENERIC MCP CALL EXAMPLE:
|
|
63
|
-
{
|
|
64
|
-
"name": "get_workflow_result",
|
|
65
|
-
"arguments": {
|
|
66
|
-
"workflowId": "${error.workflowId}",
|
|
67
|
-
"operationName": "${error.operationName}"
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
Background processing keeps the MCP client responsive while the workflow completes.`;
|
|
72
|
-
error.message = restoreMessage;
|
|
73
|
-
return error;
|
|
74
|
-
}
|
|
75
|
-
//# sourceMappingURL=execute-with-progress.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"execute-with-progress.js","sourceRoot":"","sources":["../../src/utils/execute-with-progress.ts"],"names":[],"mappings":";;AAIA,kDA8DC;AAlED,0CAA4F;AAIrF,KAAK,UAAU,mBAAmB,CACvC,gBAAmE,EACnE,SAAsC,EACtC,eAAuB,EACvB,EACE,MAAM,EACN,UAAU,EACV,aAAa,MACiE,EAAE;IAElF,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,gBAAgB,CAAC;QACf,aAAa;QACb,QAAQ;QACR,KAAK,EAAE,GAAG;QACV,OAAO,EAAE,qBAAqB,SAAS,CAAC,aAAa,KAAK;KAC3D,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,WAAW,CAC1B,GAAG,EAAE;QACH,IAAI,QAAQ,GAAG,EAAE,EAAE,CAAC;YAClB,QAAQ,IAAI,CAAC,CAAC;QAChB,CAAC;aAAM,IAAI,QAAQ,GAAG,EAAE,EAAE,CAAC;YACzB,QAAQ,IAAI,CAAC,CAAC;QAChB,CAAC;QAED,gBAAgB,CAAC;YACf,aAAa;YACb,QAAQ;YACR,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,sBAAsB,SAAS,CAAC,aAAa,KAAK;SAC5D,CAAC,CAAC;IACL,CAAC,EACD,IAAI,CAAC,GAAG,CAAC,eAAe,GAAG,EAAE,EAAE,KAAK,CAAC,CACtC,CAAC;IAEF,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,UAAU,GAAG,MAAM,SAAS,CAAC,OAAO,CAAC,MAAiB,CAAC,CAAC;QAC1D,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,UAAU,EAAE;YAChD,OAAO,EAAE,eAAe;SACzB,CAAC,CAAC;QACH,aAAa,CAAC,QAAQ,CAAC,CAAC;QAExB,gBAAgB,CAAC;YACf,aAAa;YACb,QAAQ,EAAE,GAAG;YACb,KAAK,EAAE,GAAG;YACV,OAAO,EAAE,YAAY,SAAS,CAAC,aAAa,yBAAyB;SACtE,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,aAAa,CAAC,QAAQ,CAAC,CAAC;QACxB,IAAI,KAAK,YAAY,oCAA6B,EAAE,CAAC;YACnD,MAAM,oBAAoB,CAAC,KAAK,CAAC,CAAC;QACpC,CAAC;QAED,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAoC;IAChE,MAAM,cAAc,GAAG;;;;;;;gBAOT,KAAK,CAAC,UAAU;mBACb,KAAK,CAAC,aAAa;;;;;;qBAMjB,KAAK,CAAC,UAAU;wBACb,KAAK,CAAC,aAAa;;;;oFAIyC,CAAC;IAEnF,KAAK,CAAC,OAAO,GAAG,cAAc,CAAC;IAC/B,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import { LinkedApiWorkflowTimeoutError, Operation, TMappedResponse } from '@linkedapi/node';
|
|
2
|
-
|
|
3
|
-
import { LinkedApiProgressNotification } from './types';
|
|
4
|
-
|
|
5
|
-
export async function executeWithProgress<TParams, TResult>(
|
|
6
|
-
progressCallback: (progress: LinkedApiProgressNotification) => void,
|
|
7
|
-
operation: Operation<TParams, TResult>,
|
|
8
|
-
workflowTimeout: number,
|
|
9
|
-
{
|
|
10
|
-
params,
|
|
11
|
-
workflowId,
|
|
12
|
-
progressToken,
|
|
13
|
-
}: { params?: TParams; workflowId?: string; progressToken?: string | number } = {},
|
|
14
|
-
): Promise<TMappedResponse<TResult>> {
|
|
15
|
-
let progress = 0;
|
|
16
|
-
|
|
17
|
-
progressCallback({
|
|
18
|
-
progressToken,
|
|
19
|
-
progress,
|
|
20
|
-
total: 100,
|
|
21
|
-
message: `Starting workflow ${operation.operationName}...`,
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
const interval = setInterval(
|
|
25
|
-
() => {
|
|
26
|
-
if (progress < 50) {
|
|
27
|
-
progress += 5;
|
|
28
|
-
} else if (progress < 98) {
|
|
29
|
-
progress += 1;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
progressCallback({
|
|
33
|
-
progressToken,
|
|
34
|
-
progress,
|
|
35
|
-
total: 100,
|
|
36
|
-
message: `Executing workflow ${operation.operationName}...`,
|
|
37
|
-
});
|
|
38
|
-
},
|
|
39
|
-
Math.max(workflowTimeout / 20, 10000),
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
if (!workflowId) {
|
|
44
|
-
workflowId = await operation.execute(params as TParams);
|
|
45
|
-
}
|
|
46
|
-
const result = await operation.result(workflowId, {
|
|
47
|
-
timeout: workflowTimeout,
|
|
48
|
-
});
|
|
49
|
-
clearInterval(interval);
|
|
50
|
-
|
|
51
|
-
progressCallback({
|
|
52
|
-
progressToken,
|
|
53
|
-
progress: 100,
|
|
54
|
-
total: 100,
|
|
55
|
-
message: `Workflow ${operation.operationName} completed successfully`,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
return result;
|
|
59
|
-
} catch (error) {
|
|
60
|
-
clearInterval(interval);
|
|
61
|
-
if (error instanceof LinkedApiWorkflowTimeoutError) {
|
|
62
|
-
throw generateTimeoutError(error);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
throw error;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function generateTimeoutError(error: LinkedApiWorkflowTimeoutError) {
|
|
70
|
-
const restoreMessage = `Workflow is running in the background. Continue listening for updates.
|
|
71
|
-
|
|
72
|
-
ACTION REQUIRED (MCP CLIENT):
|
|
73
|
-
- Do not retry the original tool; the workflow is already running
|
|
74
|
-
- Call the get_workflow_result tool with the parameters below and keep listening until completion
|
|
75
|
-
|
|
76
|
-
PARAMETERS TO USE:
|
|
77
|
-
- workflowId: ${error.workflowId}
|
|
78
|
-
- operationName: ${error.operationName}
|
|
79
|
-
|
|
80
|
-
GENERIC MCP CALL EXAMPLE:
|
|
81
|
-
{
|
|
82
|
-
"name": "get_workflow_result",
|
|
83
|
-
"arguments": {
|
|
84
|
-
"workflowId": "${error.workflowId}",
|
|
85
|
-
"operationName": "${error.operationName}"
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
Background processing keeps the MCP client responsive while the workflow completes.`;
|
|
90
|
-
|
|
91
|
-
error.message = restoreMessage;
|
|
92
|
-
return error;
|
|
93
|
-
}
|