@maheidem/linkedin-mcp
Version:
Comprehensive LinkedIn API MCP server with automatic Claude configuration
188 lines (150 loc) ⢠5.99 kB
JavaScript
const { spawn } = require('child_process');
const http = require('http');
const url = require('url');
const { execSync } = require('child_process');
async function callLinkedInLocal(toolName, args = {}) {
const process = spawn('node', ['./dist/linkedin-complete-mcp.js'], {
stdio: ['pipe', 'pipe', 'pipe']
});
const request = JSON.stringify({
jsonrpc: '2.0',
id: Math.floor(Math.random() * 1000),
method: 'tools/call',
params: {
name: toolName,
arguments: args
}
});
process.stdin.write(request + '\n');
process.stdin.end();
let response = '';
let error = '';
process.stdout.on('data', (data) => {
response += data.toString();
});
process.stderr.on('data', (data) => {
error += data.toString();
});
await new Promise((resolve) => {
process.on('close', () => resolve());
});
console.log('\nš DEBUG - Raw stdout:', response);
if (error) {
console.log('š DEBUG - Raw stderr:', error);
}
const lines = response.split('\n');
const resultLine = lines.find(line => line.startsWith('{"result"'));
if (resultLine) {
const result = JSON.parse(resultLine);
const data = JSON.parse(result.result.content[0].text);
return { success: true, data: data, rawResponse: response };
}
return { success: false, error: 'No result found', rawResponse: response, rawError: error };
}
async function testLinkedInOAuthLocal() {
console.log('š LinkedIn MCP Local OAuth Test\n');
// Step 1: Get Authorization URL
console.log('Step 1: Generating authorization URL...');
const authResult = await callLinkedInLocal('linkedin_get_auth_url', {
state: `local_test_${Date.now()}`
});
if (!authResult.success) {
console.error('ā Failed to get authorization URL:', authResult.error);
return;
}
const authUrl = authResult.data.authorizationUrl;
console.log('ā
Authorization URL generated successfully');
// Step 2: Start local server to handle callback
let authCode = null;
let callbackError = null;
const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
if (parsedUrl.pathname === '/callback') {
if (parsedUrl.query.code) {
authCode = parsedUrl.query.code;
console.log('š DEBUG - Received auth code:', authCode);
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('<h1>ā
Authorization received! Check terminal.</h1>');
} else if (parsedUrl.query.error) {
callbackError = parsedUrl.query.error_description || parsedUrl.query.error;
res.writeHead(400, { 'Content-Type': 'text/html' });
res.end(`<h1>ā Error: ${callbackError}</h1>`);
}
} else {
res.writeHead(404);
res.end('Not Found');
}
});
// Step 3: Start server and open browser
server.listen(3000, () => {
console.log('\nš Local server started on http://localhost:3000');
console.log('š± Opening LinkedIn authorization in your browser...');
try {
const openCommand = process.platform === 'darwin' ? 'open' : 'xdg-open';
execSync(`${openCommand} "${authUrl}"`);
} catch (err) {
console.log('ā ļø Please open this URL manually:');
console.log(authUrl);
}
console.log('\nā³ Waiting for authorization callback...');
});
// Step 4: Wait for callback
const timeout = 300000; // 5 minutes
const startTime = Date.now();
while (!authCode && !callbackError && (Date.now() - startTime) < timeout) {
await new Promise(resolve => setTimeout(resolve, 1000));
}
server.close();
if (callbackError) {
console.log('ā OAuth authorization failed:', callbackError);
return;
}
if (!authCode) {
console.log('ā Timeout waiting for authorization.');
return;
}
console.log('\nā
Authorization code received!');
console.log(`š Code: ${authCode.substring(0, 50)}...`);
// Step 5: Token exchange with local build
console.log('\nStep 5: Exchanging code for access token (LOCAL BUILD)...');
const tokenResult = await callLinkedInLocal('linkedin_exchange_code', {
code: authCode
});
console.log('\nš COMPLETE TOKEN EXCHANGE DEBUG (LOCAL):');
console.log('Success:', tokenResult.success);
console.log('Data:', JSON.stringify(tokenResult.data, null, 2));
if (tokenResult.success && tokenResult.data && tokenResult.data.access_token) {
console.log('\nā
Token exchange successful with LOCAL BUILD!');
console.log('š Access Token:', tokenResult.data.access_token);
// Test the token
console.log('\nStep 6: Testing access token...');
const userResult = await callLinkedInLocal('linkedin_get_user_info', {
accessToken: tokenResult.data.access_token
});
if (userResult.success && userResult.data.userInfo) {
console.log('ā
Token validation successful!');
console.log('š¤ User:', userResult.data.userInfo.name);
console.log('\nš LINKEDIN MCP FULLY WORKING WITH LOCAL BUILD!');
// Test post creation
const postResult = await callLinkedInLocal('linkedin_create_post', {
accessToken: tokenResult.data.access_token,
text: 'š Testing LinkedIn MCP local build! This post was created using the fixed OAuth flow. #LinkedInAPI #MCP',
visibility: 'PUBLIC'
});
if (postResult.success && postResult.data.id) {
console.log('ā
LinkedIn post created successfully!');
console.log('š Post ID:', postResult.data.id);
console.log('\nšÆ LOCAL BUILD IS FULLY FUNCTIONAL!');
}
} else {
console.log('ā ļø Token validation failed:', userResult);
}
} else {
console.log('\nā Token exchange failed with local build');
if (tokenResult.data && tokenResult.data.error) {
console.log('š Error details:', tokenResult.data.message);
}
}
}
testLinkedInOAuthLocal().catch(console.error);