Job Ad Intelligence MCP
A paid MCP server that helps AI agents analyse job advertisements. Five tools: extract structured data from any job ad (text or URL), normalise salary strings into min/max/currency/period, detect seniority level from job titles, score a CV against a job ad, and generate targeted application questions. Priced from $0.002 to $0.05 per call, paid in USDC on Base via x402. No API key required.
Job Ad Intelligence MCP
A paid MCP-compatible service that helps LLM agents analyse job advertisements. Exposes five tools for extracting structured data, normalising salaries, detecting seniority, scoring candidate fit, and generating application questions.
Built with Node.js, TypeScript, the Model Context Protocol SDK, and an x402-ready payment guard.
Quick start
# 1. Clone / copy the project
cd job-ad-intelligence-mcp
# 2. Install dependencies
npm install
# 3. Configure environment
cp .env.example .env
# Edit .env as needed (defaults are fine for local development)
# 4. Run in development mode
npm run dev
# 5. Run tests
npm test
# 6. Build for production
npm run build
npm start
Environment variables
| Variable | Default | Description |
|---|---|---|
PORT | 3000 | HTTP port to listen on |
HOST | 0.0.0.0 | Bind address |
PAYMENTS_ENABLED | false | Enable x402 payment gating (true/false) |
FETCH_TIMEOUT_MS | 10000 | Max ms to wait when fetching a job ad URL |
FETCH_MAX_BYTES | 1048576 | Max response size when fetching a URL (bytes) |
NODE_ENV | development | Node environment |
See .env.example for a full annotated example.
MCP endpoint
The server exposes a Streamable HTTP transport at:
POST http://localhost:3100/mcp ← send MCP messages / initialise session
GET http://localhost:3100/mcp ← SSE stream for server-initiated messages
DELETE http://localhost:3100/mcp ← terminate session
Health check (no auth required):
GET http://localhost:3100/health
Tools reference
1. extract_job_ad
Extracts structured data from a job advert. Accepts either raw text or a URL.
Input:
{
"text": "Senior Software Engineer at Acme Corp...",
"url": "https://example.com/jobs/123"
}
One of text or url is required. When both are provided, text is preferred.
Output:
{
"title": "Senior Software Engineer",
"company": "Acme Corp",
"location": "London",
"remote_policy": "hybrid",
"employment_type": "full_time",
"salary": {
"raw": "£70,000 - £90,000",
"min": 70000,
"max": 90000,
"currency": "GBP",
"period": "year"
},
"seniority": "senior",
"skills": {
"required": ["typescript", "react", "postgresql"],
"preferred": ["graphql", "redis"]
},
"responsibilities": ["Build and maintain APIs", "..."],
"requirements": ["5+ years experience", "..."],
"benefits": ["25 days holiday", "health insurance"],
"application_instructions": "Apply via our portal...",
"confidence": 0.85
}
2. normalise_salary
Parses a freeform salary string into a structured object.
Input:
{
"salary_text": "£45k to £60k per annum",
"location": "London, UK"
}
Output:
{
"raw": "£45k to £60k per annum",
"min": 45000,
"max": 60000,
"currency": "GBP",
"period": "year",
"notes": []
}
Handles: £45k–£60k, up to £70,000, from £500 per day, $120k, €80,000 pa, competitive, DOE.
3. detect_seniority
Classifies the seniority level from a job title and optional description.
Input:
{
"title": "Senior Software Engineer",
"description": "You'll have 6+ years of experience..."
}
Output:
{
"seniority": "senior",
"signals": ["Title match: Senior / Sr / experienced keyword detected in title \"Senior Software Engineer\""],
"confidence": 0.8
}
Levels: entry | junior | mid | senior | lead | head_of | executive | unknown
4. score_candidate_fit
Compares a CV against a job advert. Skills-based only — no protected-characteristic inference.
Input:
{
"cv_text": "..full CV text...",
"job_ad_text": "..full job ad text.."
}
Output:
{
"overall_score": 78,
"summary": "Overall fit is good (78/100)...",
"matched_skills": ["typescript", "react", "docker"],
"missing_skills": ["kubernetes", "terraform"],
"experience_alignment": "strong",
"red_flags": [],
"interview_talking_points": ["Highlight direct experience with: typescript, react..."],
"disclaimer": "This is an automated skills-based comparison and should not be used as the sole basis for hiring or employment decisions."
}
5. generate_application_questions
Generates 5–10 targeted questions a candidate can ask before applying or during screening, based on signals in the job ad.
Input:
{
"job_ad_text": "..full job ad text.."
}
Output:
{
"questions": [
{
"question": "Can you share the salary range for this role?",
"why_it_matters": "The advert uses vague compensation language...",
"category": "compensation"
}
]
}
Categories: role | company | compensation | flexibility | process | expectations
Using with an MCP client
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"job-ad-intelligence": {
"url": "http://localhost:3100/mcp",
"transport": "streamable-http"
}
}
}
Programmatic (TypeScript with @modelcontextprotocol/sdk)
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
const transport = new StreamableHTTPClientTransport(
new URL('http://localhost:3100/mcp'),
);
const client = new Client({ name: 'my-agent', version: '1.0.0' });
await client.connect(transport);
// List available tools
const tools = await client.listTools();
console.log(tools);
// Call normalise_salary
const result = await client.callTool({
name: 'normalise_salary',
arguments: { salary_text: '£45k - £60k per year' },
});
console.log(result.content[0].text);
await client.close();
Payment gating (x402)
The server uses the official x402 protocol via @x402/express. Payments are collected in USDC on Base (or any supported EVM/Solana network).
Development mode (default)
PAYMENTS_ENABLED=false — all requests pass through freely. No wallet needed.
Enabling payments (testnet — safe, no real money)
- Get a wallet address (any EVM wallet, e.g. MetaMask)
- Get testnet USDC from the Coinbase CDP Faucet
- Set your
.env:
PAYMENTS_ENABLED=true
X402_PAY_TO_ADDRESS=0xYourWalletAddress
X402_PRICE=$0.001
X402_NETWORK=eip155:84532 # Base Sepolia (testnet)
X402_FACILITATOR_URL=https://x402.org/facilitator
Without payment, every /mcp request gets a 402 Payment Required:
curl -X POST http://localhost:3100/mcp \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1"}}}'
# → HTTP 402 { "error": "Payment Required", "accepts": [...] }
Clients pay by signing a USDC transfer and including the X-PAYMENT header — the x402 client SDK (or an x402-aware AI agent) handles this automatically.
Going to mainnet (real payments)
- Sign up at portal.cdp.coinbase.com and create API keys
- Update
.env:
X402_NETWORK=eip155:8453
X402_FACILITATOR_URL=https://api.cdp.coinbase.com/platform/v2/x402
CDP_API_KEY_ID=your-key-id
CDP_API_KEY_SECRET=your-key-secret
- In
src/payments/paymentGuard.ts, swap the facilitator client as shown in the comments (~line 30)
All payment logic is isolated in src/payments/paymentGuard.ts — no other files need to change.
Project structure
src/
server.ts ← MCP server + Express HTTP transport
types.ts ← All TypeScript types and Zod schemas
payments/
paymentGuard.ts ← x402-ready payment middleware (isolated)
tools/
extractJobAd.ts ← Tool 1
normaliseSalary.ts ← Tool 2
detectSeniority.ts ← Tool 3
scoreCandidateFit.ts ← Tool 4
generateApplicationQuestions.ts ← Tool 5
utils/
fetchUrl.ts ← Secure URL fetcher with SSRF protection
salaryParser.ts ← Deterministic salary parsing
textHelpers.ts ← Shared text utilities
tests/
normaliseSalary.test.ts
detectSeniority.test.ts
scoreCandidateFit.test.ts
paymentGuard.test.ts
generateApplicationQuestions.test.ts
Scripts
npm run dev # Run with tsx (hot-reload friendly)
npm run build # Compile TypeScript to dist/
npm start # Run compiled JS
npm test # Run all tests with Vitest
npm run test:run # Run tests once (no watch)
npm run lint # ESLint
Security notes
- URL fetching rejects
localhost, private IP ranges, and non-http/https protocols (SSRF protection) - Response size is capped at
FETCH_MAX_BYTES(default 1 MB) - Fetch timeout is enforced at
FETCH_TIMEOUT_MS(default 10s) - No hardcoded secrets — all configuration via environment variables
- The
score_candidate_fittool compares only skills, experience, tools, and seniority — no protected-characteristic inference
Servidores relacionados
通胜 MCP 服务
Provides Chinese Tung Shing (almanac) data, including calendar conversions, auspicious activities, and traditional metaphysical elements.
Servicialo
Open protocol for professional service delivery. AI agents can discover, schedule, verify and settle professional services.
Strider DoorDash
MCP server for DoorDash food delivery - AI agents can search restaurants, browse menus, and place delivery orders.
Content Distribution MCP
Multi-platform content distribution - draft, repurpose, schedule, analyze posts for LinkedIn, Instagram, X/Twitter, TikTok. 7 MCP tools.
Malaysia Prayer Time MCP Server
Provides accurate Islamic prayer times for locations throughout Malaysia using the waktusolat.app API.
Aare.guru
Get water temperature and swimming conditions for the Aare river in Switzerland.
Minecraft MCP Integration
Enables AI assistants to interact with a Minecraft server using the Model Context Protocol (MCP).
Arcadia Finance
Manage Uniswap and Aerodrome liquidity positions with automated rebalancing and leverage.
YouTube Studio MCP
Local MCP server for YouTube metadata, thumbnails, comments, and analytics.
anneal-memory
Two-layer memory for AI agents with an immune system — episodes compress into identity through citation-validated graduation. Zero dependencies, 5 MCP tools.