trackmcp
Back to directory
dannydaley76

job-ad-intelligence-mcp

View on GitHub

Job Ad Intelligence MCP

0 stars TypeScriptOthers Updated May 7, 2026

Documentation

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

bash
# 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

VariableDefaultDescription
`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:

code
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):

code
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:

json
{
  "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:

json
{
  "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:

json
{
  "salary_text": "£45k to £60k per annum",
  "location": "London, UK"
}

Output:

json
{
  "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:

json
{
  "title": "Senior Software Engineer",
  "description": "You'll have 6+ years of experience..."
}

Output:

json
{
  "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:

json
{
  "cv_text": "..full CV text...",
  "job_ad_text": "..full job ad text.."
}

Output:

json
{
  "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:

json
{
  "job_ad_text": "..full job ad text.."
}

Output:

json
{
  "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`)

json
{
  "mcpServers": {
    "job-ad-intelligence": {
      "url": "http://localhost:3100/mcp",
      "transport": "streamable-http"
    }
  }
}

Programmatic (TypeScript with `@modelcontextprotocol/sdk`)

typescript
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)

1. Get a wallet address (any EVM wallet, e.g. MetaMask)

2. Get testnet USDC from the Coinbase CDP Faucet

3. Set your `.env`:

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`:

bash
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)

1. Sign up at portal.cdp.coinbase.com and create API keys

2. Update `.env`:

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

3. 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

code
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

bash
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_fit` tool compares only skills, experience, tools, and seniority — no protected-characteristic inference

Frequently asked questions

What is job-ad-intelligence-mcp?

job-ad-intelligence-mcp is Job Ad Intelligence MCP

How do I install job-ad-intelligence-mcp?

Open the GitHub repository and follow its README. Most MCP servers are added to your client's MCP config, then called by your agent.

Is job-ad-intelligence-mcp open source?

Yes — it is hosted on GitHub at https://github.com/dannydaley76/job-ad-intelligence-mcp.

Related MCP tools

Run your own MCP server? See who uses it and what to fix.

Measure it with TrackMCP