get_linkedin_posts
Retrieve recent LinkedIn posts with engagement metrics such as likes, comments, and shares.
Instructions
Retrieve the user's recent LinkedIn posts with engagement metrics.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| limit | No | Optional. Number of posts to retrieve (1-20). Defaults to 5. |
Implementation Reference
- cli.js:646-795 (handler)The tool 'get_linkedin_posts' handler. It reads the API key, validates the 'limit' argument (1-20), calls the backend API at backendLinkedinPostsApiUrl, formats the response posts with engagement metrics (reactions, comments, reposts), and returns them as text content. Includes error handling with suggestions from backend.
} else if (name === 'get_linkedin_posts') { console.error(`${packageName}: Received call for get_linkedin_posts tool.`); const apiKey = process.env.LINKEDIN_MCP_API_KEY; const limit = args?.limit || 5; if (!apiKey) { sendResponse({ jsonrpc: "2.0", error: { code: -32001, message: "Server Configuration Error: API Key not set." }, id }); return; } if (typeof limit !== 'number' || limit < 1 || limit > 20) { sendResponse({ jsonrpc: "2.0", error: { code: -32602, message: "Invalid arguments: 'limit' must be a number between 1 and 20." }, id }); return; } try { const headers = { "Authorization": `Bearer ${apiKey}`, "Content-Type": "application/json", "Accept": "*/*" // Accept any content type }; const payload = { limit }; console.error(`${packageName}: Calling LinkedIn posts API: ${backendLinkedinPostsApiUrl} with payload:`, JSON.stringify(payload, null, 2)); const apiResponse = await axios.post(backendLinkedinPostsApiUrl, payload, { headers, timeout: 60000 }); console.error(`${packageName}: LinkedIn posts API response status: ${apiResponse.status}`); console.error(`${packageName}: LinkedIn posts API response data:`, JSON.stringify(apiResponse.data, null, 2)); if (apiResponse.data && apiResponse.data.success) { // More flexible posts extraction - handle different response structures const posts = apiResponse.data.posts || []; // Safely map over posts with fallback values for missing properties const formattedPosts = posts.map(post => { if (!post) return {}; // Skip null/undefined posts return { text: post.text || '', postedDate: post.postedDate || post.posted_at || '', postUrl: post.post_url || post.postUrl || '', reactions: post.total_reactions_count || post.reactions || 0, comments: post.comments_count || post.comments || 0, reposts: post.reposts_count || post.reposts || 0 }; }).filter(post => post.text); // Only include posts with text content // Include staleness info if available const dataInfo = apiResponse.data.data_last_updated || 'Unknown'; const stalenessInfo = apiResponse.data.data_staleness_info || ''; const infoText = stalenessInfo ? `Found ${formattedPosts.length} LinkedIn posts. Last updated: ${dataInfo}. ${stalenessInfo}` : `Found ${formattedPosts.length} LinkedIn posts. Last updated: ${dataInfo}`; // Format posts as text to avoid "unsupported content type: data" error let postsAsText = formattedPosts.map((post, index) => { return `Post ${index + 1}:\n` + `Content: ${post.text}\n` + `Posted: ${post.postedDate}\n` + `URL: ${post.postUrl}\n` + `Metrics: ${post.reactions} reactions, ${post.comments} comments, ${post.reposts} reposts\n`; }).join('\n\n'); sendResponse({ jsonrpc: "2.0", result: { content: [ { type: "text", text: infoText }, { type: "text", text: postsAsText } ], isError: false }, id }); } else { // More comprehensive error handling const errorMessage = apiResponse.data?.error || apiResponse.data?.message || "Backend API Error (no detail)"; // Include suggestion if available const suggestion = apiResponse.data?.suggestion ? `\n\nSuggestion: ${apiResponse.data.suggestion}` : ''; console.error(`${packageName}: LinkedIn posts API Error: ${errorMessage}`); sendResponse({ jsonrpc: "2.0", result: { content: [ { type: "text", text: `Failed to get LinkedIn posts: ${errorMessage}${suggestion}` } ], isError: true }, id }); } } catch (error) { let errorMessage = `Failed to call LinkedIn posts API: ${error.message}`; if (error.response) { // Log complete response for debugging console.error(`${packageName}: LinkedIn posts API Full Response Headers:`, error.response.headers); console.error(`${packageName}: LinkedIn posts API Full Response Body:`, error.response.data); // Extract error message with all details from the response const responseData = error.response.data || {}; const extractedError = responseData.error || responseData.message || (typeof responseData === 'string' ? responseData : null); // Include any suggestion provided const suggestion = responseData.suggestion ? `\n\nSuggestion: ${responseData.suggestion}` : ''; // Use the backend's full error message if (extractedError) { errorMessage = `${extractedError}${suggestion}`; } else { // Fallback with a generic message but including the status errorMessage = `Backend API Error (Status ${error.response.status}): Unknown error${suggestion}`; } console.error(`${packageName}: LinkedIn posts API Error Response:`, error.response.data); } else if (error.request) { errorMessage = "No response received from LinkedIn posts API. The server may be unavailable or experiencing issues."; } console.error(`${packageName}: ${errorMessage}`); sendResponse({ jsonrpc: "2.0", result: { content: [ { type: "text", text: `Failed to get LinkedIn posts: ${errorMessage}` } ], isError: true }, id }); } - cli.js:1339-1351 (schema)The schema/registration definition for 'get_linkedin_posts' tool in the tools/list response. Defines the input schema with an optional 'limit' parameter (number, 1-20, defaults to 5).
{ name: "get_linkedin_posts", description: "Retrieve the user's recent LinkedIn posts with engagement metrics.", inputSchema: { type: "object", properties: { limit: { type: "number", description: "Optional. Number of posts to retrieve (1-20). Defaults to 5." } } } }, - cli.js:16-20 (helper)Backend API URL constant for the LinkedIn posts endpoint (backendLinkedinPostsApiUrl), used by the get_linkedin_posts handler.
const backendLinkedinPostsApiUrl = 'https://ligosocial.com/api/mcp/linkedin/posts'; const backendLinkedinProfileApiUrl = 'https://ligosocial.com/api/mcp/linkedin/profile'; const backendLinkedinSetUrlApiUrl = 'https://ligosocial.com/api/mcp/linkedin/set-url'; const backendLinkedinRefreshProfileApiUrl = 'https://ligosocial.com/api/mcp/linkedin/refresh-profile'; const backendLinkedinRefreshPostsApiUrl = 'https://ligosocial.com/api/mcp/linkedin/refresh-posts';