@maheidem/linkedin-mcp
Version:
Comprehensive LinkedIn API MCP server with automatic Claude configuration
285 lines • 10.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.analyzeProfileTool = void 0;
exports.analyzeProfile = analyzeProfile;
const zod_1 = require("zod");
exports.analyzeProfileTool = {
name: 'analyze_profile',
description: 'Analyze LinkedIn profile and provide optimization recommendations',
inputSchema: {
type: 'object',
properties: {
profileData: {
type: 'object',
properties: {
headline: { type: 'string' },
summary: { type: 'string' },
experience: {
type: 'array',
items: {
type: 'object',
properties: {
title: { type: 'string' },
description: { type: 'string' }
}
}
},
skills: {
type: 'array',
items: { type: 'string' }
},
connections: { type: 'number' },
hasProfilePhoto: { type: 'boolean' },
hasBanner: { type: 'boolean' },
hasFeatured: { type: 'boolean' }
},
description: 'Current LinkedIn profile data'
}
},
required: ['profileData']
}
};
const ProfileDataSchema = zod_1.z.object({
profileData: zod_1.z.object({
headline: zod_1.z.string().optional(),
summary: zod_1.z.string().optional(),
experience: zod_1.z.array(zod_1.z.object({
title: zod_1.z.string(),
description: zod_1.z.string().optional()
})).optional(),
skills: zod_1.z.array(zod_1.z.string()).optional(),
connections: zod_1.z.number().optional(),
hasProfilePhoto: zod_1.z.boolean().optional(),
hasBanner: zod_1.z.boolean().optional(),
hasFeatured: zod_1.z.boolean().optional()
})
});
async function analyzeProfile(params) {
const { profileData } = ProfileDataSchema.parse(params);
const analysis = {
score: calculateProfileScore(profileData),
sections: analyzeSections(profileData),
keywords: analyzeKeywords(profileData),
recommendations: generateRecommendations(profileData),
quickWins: identifyQuickWins(profileData),
comparison: compareTobestPractices(profileData)
};
return {
overallScore: `${analysis.score}/100`,
scoreBreakdown: analysis.sections,
criticalGaps: analysis.recommendations.filter(r => r.priority === 'high'),
quickWins: analysis.quickWins,
keywordAnalysis: analysis.keywords,
actionPlan: createActionPlan(analysis),
estimatedImpact: estimateImpact(analysis.score)
};
}
function calculateProfileScore(profile) {
let score = 0;
// Basic profile elements (40 points)
if (profile.hasProfilePhoto)
score += 10;
if (profile.hasBanner)
score += 5;
if (profile.headline && profile.headline.length > 50)
score += 10;
if (profile.summary && profile.summary.length > 100)
score += 15;
// Content quality (30 points)
if (profile.headline && profile.headline.length > 150)
score += 5;
if (profile.summary && profile.summary.length > 500)
score += 10;
if (profile.experience?.some((exp) => exp.description?.length > 100))
score += 10;
if (profile.skills && profile.skills.length >= 10)
score += 5;
// Advanced features (20 points)
if (profile.hasFeatured)
score += 10;
if (profile.skills && profile.skills.length >= 20)
score += 5;
if (profile.connections && profile.connections > 500)
score += 5;
// Engagement indicators (10 points)
if (profile.summary?.includes('contact') || profile.summary?.includes('reach'))
score += 5;
if (profile.headline?.includes('|'))
score += 5;
return Math.min(score, 100);
}
function analyzeSections(profile) {
return {
headline: {
score: profile.headline ? (profile.headline.length > 150 ? 90 : 60) : 0,
status: profile.headline ? 'present' : 'missing',
quality: profile.headline?.length > 150 ? 'good' : 'needs improvement'
},
summary: {
score: profile.summary ? (profile.summary.length > 500 ? 85 : 50) : 0,
status: profile.summary ? 'present' : 'missing',
quality: profile.summary?.length > 500 ? 'good' : 'needs expansion'
},
experience: {
score: profile.experience?.length > 0 ? 70 : 0,
status: profile.experience?.length > 0 ? 'present' : 'missing',
quality: profile.experience?.some((e) => e.description) ? 'detailed' : 'needs detail'
},
skills: {
score: profile.skills ? (profile.skills.length >= 15 ? 80 : 50) : 0,
count: profile.skills?.length || 0,
quality: profile.skills?.length >= 15 ? 'comprehensive' : 'needs more'
}
};
}
function analyzeKeywords(profile) {
const text = [
profile.headline || '',
profile.summary || '',
...(profile.experience?.map((e) => e.description || '') || [])
].join(' ').toLowerCase();
const keywords = extractKeywords(text);
const density = calculateKeywordDensity(keywords, text);
return {
topKeywords: keywords.slice(0, 10),
density,
missing: identifyMissingKeywords(profile),
optimization: keywords.length < 20 ? 'low' : 'good'
};
}
function generateRecommendations(profile) {
const recommendations = [];
if (!profile.headline || profile.headline.length < 100) {
recommendations.push({
section: 'headline',
issue: 'Headline too short or missing',
recommendation: 'Expand headline to 200+ characters with keywords',
priority: 'high',
impact: 'high'
});
}
if (!profile.summary) {
recommendations.push({
section: 'summary',
issue: 'No About section',
recommendation: 'Add comprehensive About section (1000+ characters)',
priority: 'high',
impact: 'high'
});
}
if (!profile.skills || profile.skills.length < 10) {
recommendations.push({
section: 'skills',
issue: 'Insufficient skills listed',
recommendation: 'Add 15-20 relevant skills for better discoverability',
priority: 'medium',
impact: 'medium'
});
}
if (!profile.hasFeatured) {
recommendations.push({
section: 'featured',
issue: 'No featured content',
recommendation: 'Add 2-3 featured posts or media',
priority: 'medium',
impact: 'medium'
});
}
return recommendations;
}
function identifyQuickWins(profile) {
const quickWins = [];
if (!profile.hasBanner)
quickWins.push('Add a professional banner image (5 minutes)');
if (profile.headline && profile.headline.length < 100)
quickWins.push('Expand headline with keywords (10 minutes)');
if (!profile.skills || profile.skills.length < 5)
quickWins.push('Add 10+ relevant skills (15 minutes)');
if (!profile.summary)
quickWins.push('Write compelling About section (30 minutes)');
return quickWins;
}
function compareTobestPractices(profile) {
return {
headline: {
current: profile.headline?.length || 0,
bestPractice: 220,
gap: 220 - (profile.headline?.length || 0)
},
summary: {
current: profile.summary?.length || 0,
bestPractice: 1500,
gap: 1500 - (profile.summary?.length || 0)
},
skills: {
current: profile.skills?.length || 0,
bestPractice: 20,
gap: 20 - (profile.skills?.length || 0)
}
};
}
function createActionPlan(analysis) {
return [
{
week: 1,
tasks: analysis.quickWins.slice(0, 3),
estimatedTime: '1 hour',
expectedImpact: '+20 points'
},
{
week: 2,
tasks: ['Optimize all experience descriptions', 'Request skill endorsements'],
estimatedTime: '2 hours',
expectedImpact: '+15 points'
},
{
week: 3,
tasks: ['Create and add featured content', 'Engage with network posts'],
estimatedTime: '2 hours',
expectedImpact: '+10 points'
}
];
}
function estimateImpact(currentScore) {
const newScore = Math.min(currentScore + 45, 100);
return {
currentVisibility: `${currentScore}%`,
projectedVisibility: `${newScore}%`,
profileViews: '+50-100% increase expected',
searchAppearances: '+75-150% increase expected',
timeToResults: '2-4 weeks'
};
}
function extractKeywords(text) {
const words = text.split(/\s+/);
const wordCount = {};
words.forEach(word => {
if (word.length > 3) {
wordCount[word] = (wordCount[word] || 0) + 1;
}
});
return Object.entries(wordCount)
.sort((a, b) => b[1] - a[1])
.map(([word]) => word);
}
function calculateKeywordDensity(keywords, text) {
const totalWords = text.split(/\s+/).length;
const keywordCount = keywords.slice(0, 10).reduce((sum, keyword) => {
const regex = new RegExp(keyword, 'gi');
const matches = text.match(regex);
return sum + (matches ? matches.length : 0);
}, 0);
return Math.round((keywordCount / totalWords) * 100);
}
function identifyMissingKeywords(profile) {
const commonKeywords = [
'leadership', 'strategy', 'innovation', 'collaboration',
'results-driven', 'expertise', 'solutions', 'growth'
];
const profileText = [
profile.headline || '',
profile.summary || ''
].join(' ').toLowerCase();
return commonKeywords.filter(keyword => !profileText.includes(keyword));
}
//# sourceMappingURL=analyzer.js.map