diff --git a/package-lock.json b/package-lock.json index 9d62d80..b1da9e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@linkedapi/mcp", - "version": "2.3.16", + "version": "2.3.17", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@linkedapi/mcp", - "version": "2.3.16", + "version": "2.3.17", "license": "MIT", "dependencies": { - "@linkedapi/node": "^2.3.15", + "@linkedapi/node": "^2.3.17", "@modelcontextprotocol/sdk": "^1.17.4", "zod": "^4.1.1" }, @@ -921,9 +921,9 @@ } }, "node_modules/@linkedapi/node": { - "version": "2.3.15", - "resolved": "https://registry.npmjs.org/@linkedapi/node/-/node-2.3.15.tgz", - "integrity": "sha512-3MJ5QWYNHFyxbw88qPckWHb8XP2rc5iD0exRM6XsQjWYxAJM/3aW8rnQTti5bYBPM4FzURKUvKnv4aSWjRJ9bQ==", + "version": "2.3.17", + "resolved": "https://registry.npmjs.org/@linkedapi/node/-/node-2.3.17.tgz", + "integrity": "sha512-2eudeWgD00FTeLjiBzN1Lq/Dwlu68YT4ZmwM3rfb9NlErL/2jeT8dPYvhVApRUGl8ifrmP2pz8rZE/ELHgXgWQ==", "license": "MIT" }, "node_modules/@modelcontextprotocol/sdk": { diff --git a/package.json b/package.json index d868def..c9334ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@linkedapi/mcp", - "version": "2.3.16", + "version": "2.3.17", "description": "MCP server that lets AI assistants control LinkedIn accounts and retrieve real-time data.", "main": "dist/index.js", "bin": { @@ -30,7 +30,7 @@ "author": "Linked API", "license": "MIT", "dependencies": { - "@linkedapi/node": "^2.3.15", + "@linkedapi/node": "^2.3.17", "@modelcontextprotocol/sdk": "^1.17.4", "zod": "^4.1.1" }, diff --git a/src/linked-api-tools.ts b/src/linked-api-tools.ts index c59caaf..67902c2 100644 --- a/src/linked-api-tools.ts +++ b/src/linked-api-tools.ts @@ -43,6 +43,7 @@ import { NvManageConversationTool } from './tools/nv-manage-conversation.js'; import { NvSearchCompaniesTool } from './tools/nv-search-companies.js'; import { NvSearchPeopleTool } from './tools/nv-search-people.js'; import { NvSendMessageTool } from './tools/nv-send-message.js'; +import { NvSyncConversationTool } from './tools/nv-sync-conversation.js'; import { NvSyncInboxTool } from './tools/nv-sync-inbox.js'; import { ReactToCommentTool } from './tools/react-to-comment.js'; import { ReactToPostTool } from './tools/react-to-post.js'; @@ -62,6 +63,7 @@ import { SearchPostsTool } from './tools/search-posts.js'; import { SendConnectionRequestTool } from './tools/send-connection-request.js'; import { SendFeedbackTool } from './tools/send-feedback.js'; import { SendMessageTool } from './tools/send-message.js'; +import { SyncConversationTool } from './tools/sync-conversation.js'; import { SyncInboxTool } from './tools/sync-inbox.js'; import { SyncNetworkTool } from './tools/sync-network.js'; import { WithdrawConnectionRequestTool } from './tools/withdraw-connection-request.js'; @@ -90,9 +92,10 @@ export class LinkedApiTools { this.tools = [ // Standard tools new SendMessageTool(), - new GetConversationTool(), new SyncInboxTool(), new GetInboxTool(), + new SyncConversationTool(), + new GetConversationTool(), new SyncNetworkTool(), new GetNetworkTool(), new ManageConversationTool(), @@ -125,8 +128,9 @@ export class LinkedApiTools { new RetrievePerformanceTool(), // Sales Navigator tools new NvSendMessageTool(), - new NvGetConversationTool(), new NvSyncInboxTool(), + new NvSyncConversationTool(), + new NvGetConversationTool(), new NvManageConversationTool(), new NvSearchCompaniesTool(), new NvSearchPeopleTool(), diff --git a/src/tools/get-conversation.ts b/src/tools/get-conversation.ts index 3413e98..96d24e4 100644 --- a/src/tools/get-conversation.ts +++ b/src/tools/get-conversation.ts @@ -57,7 +57,7 @@ operationName: OPERATION_NAME.syncConversation }; return { name: this.name, description: - '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. The result carries syncUntil — the moment syncing of this conversation stops. Once it is in the past the messages are still returned but no longer updated; call sync_conversation again for the same person to resume updates. It also carries personUrn — the permanent LinkedIn member URN (urn:li:member:) of the person, or null when LinkedIn does not expose it.', + 'Legacy. Prefer get_inbox with personUrl. 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. The result carries syncUntil — the moment syncing of this conversation stops. Once it is in the past the messages are still returned but no longer updated; call sync_conversation again for the same person to resume updates. It also carries personUrn — the permanent LinkedIn member URN (urn:li:member:) of the person, or null when LinkedIn does not expose it.', inputSchema: { type: 'object', properties: { diff --git a/src/tools/get-inbox.ts b/src/tools/get-inbox.ts index 0e01036..8d6ef12 100644 --- a/src/tools/get-inbox.ts +++ b/src/tools/get-inbox.ts @@ -1,28 +1,40 @@ -import LinkedApi, { TInboxPollRequest, TInboxPollResult, TMappedResponse } from '@linkedapi/node'; +import LinkedApi, { + TInboxPersonPollResult, + TInboxPollRequest, + TInboxPollResult, + TMappedResponse, +} from '@linkedapi/node'; import { Tool } from '@modelcontextprotocol/sdk/types.js'; import z from 'zod'; import { LinkedApiTool } from '../utils/linked-api-tool.js'; -export class GetInboxTool extends LinkedApiTool { +type TGetInboxArgs = TInboxPollRequest & { personUrl?: string }; + +export class GetInboxTool extends LinkedApiTool< + TGetInboxArgs, + TInboxPollResult | TInboxPersonPollResult +> { public readonly name = 'get_inbox'; protected readonly schema = z.object({ since: z.string().optional(), type: z.enum(['st', 'nv']).optional(), threadId: z.string().optional(), + personUrl: z.string().optional(), }); public override async execute({ linkedapi, - args: { since, type, threadId }, + args: { since, type, threadId, personUrl }, }: { linkedapi: LinkedApi; - args: TInboxPollRequest; - }): Promise> { + args: TGetInboxArgs; + }): Promise> { return linkedapi.pollInbox({ since, type, threadId, + personUrl, }); } @@ -30,14 +42,14 @@ export class GetInboxTool extends LinkedApiTool) of the person on the other side of the thread, or null when LinkedIn does not expose it. personHashedUrl is LinkedIn's hashed profile URL and is always present; personPublicUrl is the public vanity URL when that person has been resolved, otherwise null.", + "Default way to read LinkedIn messages (standard and Sales Navigator), newest first. Without personUrl returns new messages across all conversations since inbox monitoring was enabled; requires sync_inbox (or nv_sync_inbox) once per account. With personUrl returns the whole known conversation with that person, including history loaded by sync_conversation, as { messages, threads }: each message also carries inboxMessageId (its id in the result without personUrl, or null) and threadId may be null; each thread carries historyFrom and historyComplete. Check threads[].historyComplete – if false and you need earlier messages, call sync_conversation once, then call get_inbox again. Every message carries personUrn — the permanent LinkedIn member URN (urn:li:member:) of the person on the other side of the thread, or null when LinkedIn does not expose it. personHashedUrl is LinkedIn's hashed profile URL and is always present; personPublicUrl is the public vanity URL when that person has been resolved, otherwise null.", inputSchema: { type: 'object', properties: { since: { type: 'string', description: - "Optional ISO 8601 timestamp to only retrieve messages after this date (e.g., '2024-01-15T10:30:00Z'). If not provided, all captured messages are returned.", + "Optional ISO 8601 timestamp to only retrieve messages at or after this date (e.g., '2024-01-15T10:30:00Z'). If not provided, all captured messages are returned. Message times are whole minutes.", }, type: { type: 'string', @@ -50,6 +62,11 @@ export class GetInboxTool extends LinkedApiTool) of the person, or null when LinkedIn does not expose it.', + 'Legacy. Prefer get_inbox with personUrl. 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. The result carries personUrn — the permanent LinkedIn member URN (urn:li:member:) of the person, or null when LinkedIn does not expose it.', inputSchema: { type: 'object', properties: { diff --git a/src/tools/nv-sync-conversation.ts b/src/tools/nv-sync-conversation.ts index 35ac9c9..64ad7b2 100644 --- a/src/tools/nv-sync-conversation.ts +++ b/src/tools/nv-sync-conversation.ts @@ -16,19 +16,19 @@ export class NvSyncConversationTool extends OperationTool return { name: this.name, description: - 'Enable whole-inbox monitoring in Sales Navigator so every incoming conversation can be polled with get_inbox (nv.syncInbox action). Run once per account; only messages that arrive after it is enabled are captured.', + 'Recommended first step before reading Sales Navigator messages. Enable whole-inbox monitoring in Sales Navigator so every incoming conversation can be polled with get_inbox (nv.syncInbox action). Run once per account, before sending messages whose replies you want to read; only messages that arrive after it is enabled are captured.', inputSchema: { type: 'object', properties: {}, diff --git a/src/tools/sync-conversation.ts b/src/tools/sync-conversation.ts index cbefd24..997290b 100644 --- a/src/tools/sync-conversation.ts +++ b/src/tools/sync-conversation.ts @@ -16,19 +16,19 @@ export class SyncConversationTool extends OperationTool { return { name: this.name, description: - 'Enable whole-inbox monitoring so every incoming conversation can be polled with get_inbox (st.syncInbox action). Run once per account; only messages that arrive after it is enabled are captured.', + 'Recommended first step before reading messages. Enable whole-inbox monitoring so every incoming conversation can be polled with get_inbox (st.syncInbox action). Run once per account, before sending messages whose replies you want to read; only messages that arrive after it is enabled are captured.', inputSchema: { type: 'object', properties: {},