Skip to main content
Glama
dma9527

irs-taxpayer-mcp

by dma9527

analyze_paycheck

Verify paycheck withholding accuracy by comparing your pay stub numbers to expected tax calculations. Input your earnings and deductions to check if your employer is withholding the correct federal and state tax amounts.

Instructions

Analyze a paycheck to verify withholding accuracy. Input your pay stub numbers and see if your employer is withholding the right amount.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
taxYearYesTax year
filingStatusYes
payFrequencyYes
grossPayYesGross pay this period
federalWithheldYesFederal tax withheld this period
stateWithheldNoState tax withheld this period
socialSecurityWithheldNoSocial Security withheld
medicareWithheldNoMedicare withheld
retirement401kNo401k/403b pre-tax contribution this period
hsaContributionNoHSA contribution this period
stateCodeNo

Implementation Reference

  • The complete implementation of the analyze_paycheck tool. It takes paycheck input (pay frequency, gross pay, withholding amounts, 401k/HSA contributions), annualizes the values, calculates expected federal tax using calculateTax(), computes expected FICA (Social Security and Medicare), and generates a detailed analysis report comparing actual vs expected withholding with recommendations for adjustments.
    server.tool( "analyze_paycheck", "Analyze a paycheck to verify withholding accuracy. " + "Input your pay stub numbers and see if your employer is withholding the right amount.", { taxYear: z.number().describe("Tax year"), filingStatus: FilingStatusEnum, payFrequency: z.enum(["weekly", "biweekly", "semimonthly", "monthly"]), grossPay: z.number().min(0).describe("Gross pay this period"), federalWithheld: z.number().min(0).describe("Federal tax withheld this period"), stateWithheld: z.number().min(0).optional().describe("State tax withheld this period"), socialSecurityWithheld: z.number().min(0).optional().describe("Social Security withheld"), medicareWithheld: z.number().min(0).optional().describe("Medicare withheld"), retirement401k: z.number().min(0).optional().describe("401k/403b pre-tax contribution this period"), hsaContribution: z.number().min(0).optional().describe("HSA contribution this period"), stateCode: z.string().length(2).optional(), }, async (params) => { const periods: Record<string, number> = { weekly: 52, biweekly: 26, semimonthly: 24, monthly: 12 }; const periodsPerYear = periods[params.payFrequency]; const annualGross = params.grossPay * periodsPerYear; const annual401k = (params.retirement401k ?? 0) * periodsPerYear; const annualHSA = (params.hsaContribution ?? 0) * periodsPerYear; const annualTaxableIncome = annualGross - annual401k - annualHSA; // Expected federal tax const expected = calculateTax({ taxYear: params.taxYear, filingStatus: params.filingStatus, grossIncome: annualTaxableIncome, w2Income: annualTaxableIncome, }); const expectedPerPeriod = Math.round(expected.totalFederalTax / periodsPerYear); const actualFederal = params.federalWithheld; const federalDiff = actualFederal - expectedPerPeriod; // Expected FICA const expectedSS = Math.round(Math.min(annualGross, 168600) * 0.062 / periodsPerYear); const expectedMedicare = Math.round(annualGross * 0.0145 / periodsPerYear); const actualSS = params.socialSecurityWithheld ?? 0; const actualMedicare = params.medicareWithheld ?? 0; const lines = [ `## ๐Ÿ’ฐ Paycheck Analysis โ€” TY${params.taxYear}`, `**${params.payFrequency}** pay (${periodsPerYear} periods/year) | **${params.filingStatus.replace(/_/g, " ")}**`, "", `### This Paycheck`, `| Item | Actual | Expected | Diff |`, `|------|--------|----------|------|`, `| Gross Pay | $${fmt(params.grossPay)} | โ€” | โ€” |`, `| Federal Tax | $${fmt(actualFederal)} | ~$${fmt(expectedPerPeriod)} | ${federalDiff >= 0 ? "+" : ""}$${fmt(federalDiff)} |`, actualSS > 0 ? `| Social Security | $${fmt(actualSS)} | ~$${fmt(expectedSS)} | ${actualSS - expectedSS >= 0 ? "+" : ""}$${fmt(actualSS - expectedSS)} |` : "", actualMedicare > 0 ? `| Medicare | $${fmt(actualMedicare)} | ~$${fmt(expectedMedicare)} | ${actualMedicare - expectedMedicare >= 0 ? "+" : ""}$${fmt(actualMedicare - expectedMedicare)} |` : "", params.stateWithheld ? `| State Tax | $${fmt(params.stateWithheld)} | โ€” | โ€” |` : "", params.retirement401k ? `| 401k/403b | $${fmt(params.retirement401k)} | โ€” | pre-tax |` : "", params.hsaContribution ? `| HSA | $${fmt(params.hsaContribution)} | โ€” | pre-tax |` : "", "", `### Annual Projection`, `| Item | Amount |`, `|------|--------|`, `| Annual Gross | $${fmt(annualGross)} |`, annual401k > 0 ? `| 401k Contributions | -$${fmt(annual401k)} |` : "", annualHSA > 0 ? `| HSA Contributions | -$${fmt(annualHSA)} |` : "", `| Taxable Income | $${fmt(annualTaxableIncome)} |`, `| Expected Annual Federal Tax | $${fmt(expected.totalFederalTax)} |`, `| Annual Federal Withheld (projected) | $${fmt(actualFederal * periodsPerYear)} |`, "", ]; const annualFederalWithheld = actualFederal * periodsPerYear; const annualDiff = annualFederalWithheld - expected.totalFederalTax; if (Math.abs(annualDiff) < 500) { lines.push("โœ… **Withholding looks accurate.** You're within $500 of your expected tax."); } else if (annualDiff > 0) { lines.push( `โš ๏ธ **Over-withholding by ~$${fmt(Math.round(annualDiff))}/year.** You'll likely get a refund.`, `Consider reducing W-4 withholding to keep ~$${fmt(Math.round(annualDiff / periodsPerYear))} more per paycheck.`, ); } else { lines.push( `โš ๏ธ **Under-withholding by ~$${fmt(Math.round(Math.abs(annualDiff)))}/year.** You may owe at tax time.`, `Consider increasing W-4 withholding by ~$${fmt(Math.round(Math.abs(annualDiff) / periodsPerYear))} per paycheck.`, ); } lines.push("", `> โš ๏ธ Simplified estimate. Actual withholding depends on W-4 settings, pre-tax benefits, and other factors.`); return { content: [{ type: "text", text: lines.filter(Boolean).join("\n") }] }; } );
  • Zod schema defining the input validation for analyze_paycheck. Includes taxYear, filingStatus (enum), payFrequency (enum: weekly/biweekly/semimonthly/monthly), grossPay, federalWithheld, optional stateWithheld, socialSecurityWithheld, medicareWithheld, retirement401k, hsaContribution, and stateCode.
    { taxYear: z.number().describe("Tax year"), filingStatus: FilingStatusEnum, payFrequency: z.enum(["weekly", "biweekly", "semimonthly", "monthly"]), grossPay: z.number().min(0).describe("Gross pay this period"), federalWithheld: z.number().min(0).describe("Federal tax withheld this period"), stateWithheld: z.number().min(0).optional().describe("State tax withheld this period"), socialSecurityWithheld: z.number().min(0).optional().describe("Social Security withheld"), medicareWithheld: z.number().min(0).optional().describe("Medicare withheld"), retirement401k: z.number().min(0).optional().describe("401k/403b pre-tax contribution this period"), hsaContribution: z.number().min(0).optional().describe("HSA contribution this period"), stateCode: z.string().length(2).optional(), },
  • src/index.ts:28-49 (registration)
    Registration of the comprehensive tools module which includes analyze_paycheck. Line 28 imports registerComprehensiveTools from ./tools/comprehensive-tools.js, and line 49 calls it to register 6 tools including 'paycheck' (analyze_paycheck).
    import { registerComprehensiveTools } from "./tools/comprehensive-tools.js"; import { registerAdvancedTools } from "./tools/advanced-tools.js"; import { registerSmartTools } from "./tools/smart-tools.js"; import http from "node:http"; const server = new McpServer({ name: "irs-taxpayer-mcp", version: "0.5.3", description: "Tax calculation, credits, deductions, state taxes, and retirement strategy tools for individual US taxpayers. " + "All financial calculations run locally โ€” your income data never leaves your machine.", }); // Register all tool groups registerTaxCalculationTools(server); // 6 tools: calculate, brackets, compare, quarterly, total, w4 registerDeductionTools(server); // 2 tools: list deductions, standard vs itemized registerIrsLookupTools(server); // 3 tools: deadlines, refund status, form info registerCreditTools(server); // 5 tools: list credits, eligibility, retirement accounts, strategies, EITC registerStateTaxTools(server); // 4 tools: state info, estimate, compare states, no-tax states registerPlanningTools(server); // 6 tools: planning tips, year compare, SE tax, mortgage, education, MFJ vs MFS registerObbbTools(server); // 2 tools: OBBB deductions calculator, what changed between years registerComprehensiveTools(server); // 6 tools: report, 1099, calendar, paycheck, scenario, audit
  • FilingStatusEnum - a reusable Zod enum defining the valid filing statuses (single, married_filing_jointly, married_filing_separately, head_of_household) used by analyze_paycheck and other tools.
    export const FilingStatusEnum = z.enum([ "single", "married_filing_jointly", "married_filing_separately", "head_of_household", ]);
  • The calculateTax function used by analyze_paycheck to compute expected federal tax. Takes TaxInput including taxYear, filingStatus, and gross income, returns TaxBreakdown with totalFederalTax and other details. This is the core tax calculation engine that the paycheck analyzer depends on.
    export function calculateTax(input: TaxInput): TaxBreakdown { // Input validation const errors = validate( validateTaxYear(input.taxYear), validateIncome(input.grossIncome, "grossIncome"), ); if (errors.length > 0) { throw new Error(formatValidationErrors(errors)); } const taxData = getTaxYearData(input.taxYear); if (!taxData) { throw new Error(`Tax year ${input.taxYear} is not supported. Supported years: 2024, 2025`); } // Step 1: Calculate AGI const aboveTheLine = input.aboveTheLineDeductions ?? 0; const w2 = input.w2Income ?? 0; const seDeduction = input.selfEmploymentIncome ? calculateSelfEmploymentTax(input.selfEmploymentIncome, taxData, w2) * 0.5 : 0; const agi = input.grossIncome - aboveTheLine - seDeduction; // Step 2: Determine deduction (standard vs itemized) let standardDeduction = taxData.standardDeduction[input.filingStatus]; // Additional deduction for age 65+ or blind const additionalAmount = taxData.additionalDeduction.age65OrBlind[input.filingStatus]; if (input.age65OrOlder) standardDeduction += additionalAmount; if (input.blind) standardDeduction += additionalAmount; if (input.spouseAge65OrOlder) standardDeduction += additionalAmount; if (input.spouseBlind) standardDeduction += additionalAmount; const itemized = input.itemizedDeductions ?? 0; const useItemized = itemized > standardDeduction; const deductionAmount = useItemized ? itemized : standardDeduction; // Step 3: Calculate taxable income const longTermGains = (input.capitalGainsLongTerm !== false ? (input.capitalGains ?? 0) : 0); const shortTermGains = input.shortTermCapitalGains ?? (input.capitalGainsLongTerm === false ? (input.capitalGains ?? 0) : 0); const ordinaryIncome = input.grossIncome - longTermGains; const taxableOrdinaryIncome = Math.max(0, ordinaryIncome - aboveTheLine - seDeduction - deductionAmount); // Step 4: QBI deduction const qbi = input.qualifiedBusinessIncome ?? 0; const taxableBeforeQBI = taxableOrdinaryIncome + longTermGains; const qbiDeduction = calculateQBIDeduction(qbi, taxableBeforeQBI, taxData); const adjustedTaxableOrdinary = Math.max(0, taxableOrdinaryIncome - qbiDeduction); // Step 5: Calculate ordinary income tax const { breakdown, total: ordinaryTax, marginalRate } = calculateBracketTax( adjustedTaxableOrdinary, taxData.brackets[input.filingStatus] ); // Step 6: Capital gains tax (long-term only) const cgTax = longTermGains > 0 ? calculateCapitalGainsTax(longTermGains, adjustedTaxableOrdinary, input.filingStatus, taxData) : 0; // Step 7: Self-employment tax const seTax = input.selfEmploymentIncome ? calculateSelfEmploymentTax(input.selfEmploymentIncome, taxData, w2) : 0; // Step 8: NIIT (3.8% on investment income above threshold) const investmentIncome = longTermGains + shortTermGains; const niit = calculateNIIT(agi, investmentIncome, input.filingStatus); // Step 9: Additional Medicare Tax (0.9% on earned income above threshold) const earnedIncome = (input.w2Income ?? 0) + (input.selfEmploymentIncome ?? 0); const additionalMedicareTax = calculateAdditionalMedicareTax(earnedIncome, input.filingStatus, taxData); // Step 10: Child Tax Credit const dependents = input.dependents ?? 0; let childCredit = dependents * taxData.childTaxCredit.amount; const phaseoutStart = taxData.childTaxCredit.phaseoutStart[input.filingStatus]; if (agi > phaseoutStart) { const excess = Math.ceil((agi - phaseoutStart) / 1000) * taxData.childTaxCredit.phaseoutRate; childCredit = Math.max(0, childCredit - excess); } const totalTaxBeforeAMT = Math.max(0, ordinaryTax + cgTax + seTax + niit + additionalMedicareTax - childCredit); // Step 11: AMT const isoSpread = input.isoExerciseSpread ?? 0; const saltDeducted = useItemized ? (input.stateTaxDeducted ?? 0) : 0; const regularIncomeTax = ordinaryTax + cgTax; const amt = calculateAMT(regularIncomeTax, taxableOrdinaryIncome + longTermGains, input.filingStatus, taxData, isoSpread, saltDeducted); const totalTax = totalTaxBeforeAMT + amt; const taxableIncome = adjustedTaxableOrdinary + longTermGains; return { taxYear: input.taxYear, filingStatus: input.filingStatus, grossIncome: input.grossIncome, adjustedGrossIncome: agi, deductionType: useItemized ? "itemized" : "standard", deductionAmount, taxableIncome, bracketBreakdown: breakdown, ordinaryIncomeTax: ordinaryTax, capitalGainsTax: cgTax, selfEmploymentTax: seTax, niit, additionalMedicareTax, qbiDeduction, amt, totalFederalTax: totalTax, effectiveRate: input.grossIncome > 0 ? totalTax / input.grossIncome : 0, marginalRate, childTaxCredit: childCredit, estimatedQuarterlyPayment: Math.ceil(totalTax / 4), }; }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dma9527/irs-taxpayer-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server