Job Ad Intelligence MCP

We need to translate the given text from English to Spanish. The text describes a paid MCP server for analyzing job ads. We must preserve product names, protocol names, URLs, numbers, technical terms. The name "Job Ad Intelligence MCP" is not in the text? Actually it's in the instruction but not in the <text>. The text starts with "A paid MCP server..." So we translate that. Keep "MCP server" as is? "MCP" is a protocol name? Probably keep as MCP. Numbers: $0.002 to $0.05, USDC, Base, x402. Also "API key" is technical. Translate the rest naturally. Translation: "Un servidor MCP de pago que ayuda a los agentes de IA a analizar anuncios de empleo. Cinco herramientas: extraer datos estructurados de cualquier anuncio de empleo (texto o URL), normalizar cadenas salariales a mínimo/máximo/moneda/periodo, detectar el nivel de antig

Documentación

Job Ad Intelligence MCP

Un servicio compatible con MCP de pago que ayuda a los agentes LLM a analizar anuncios de empleo. Expone cinco herramientas para extraer datos estructurados, normalizar salarios, detectar seniority, puntuar la adecuación de candidatos y generar preguntas de solicitud.

Construido con Node.js, TypeScript, el Model Context Protocol SDK y un guardián de pago preparado para x402.


Inicio rápido

# 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

Variables de entorno

VariablePor defectoDescripción
PORT3000Puerto HTTP en el que escuchar
HOST0.0.0.0Dirección de enlace
PAYMENTS_ENABLEDfalseHabilitar el bloqueo de pago x402 (true/false)
FETCH_TIMEOUT_MS10000Máximo de ms de espera al obtener una URL de anuncio de empleo
FETCH_MAX_BYTES1048576Tamaño máximo de respuesta al obtener una URL (bytes)
NODE_ENVdevelopmentEntorno de Node

Consulta .env.example para ver un ejemplo completo anotado.


Endpoint MCP

El servidor expone un transporte HTTP Streamable en:

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

Comprobación de salud (sin autenticación requerida):

GET http://localhost:3100/health

Referencia de herramientas

1. extract_job_ad

Extrae datos estructurados de un anuncio de empleo. Acepta texto sin formato o una URL.

Entrada:

{
  "text": "Senior Software Engineer at Acme Corp...",
  "url": "https://example.com/jobs/123"
}

Se requiere uno de text o url. Cuando se proporcionan ambos, se prefiere text.

Salida:

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

Analiza una cadena de salario de formato libre en un objeto estructurado.

Entrada:

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

Salida:

{
  "raw": "£45k to £60k per annum",
  "min": 45000,
  "max": 60000,
  "currency": "GBP",
  "period": "year",
  "notes": []
}

Maneja: £45k–£60k, up to £70,000, from £500 per day, $120k, €80,000 pa, competitive, DOE.


3. detect_seniority

Clasifica el nivel de seniority a partir de un título de puesto y una descripción opcional.

Entrada:

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

Salida:

{
  "seniority": "senior",
  "signals": ["Title match: Senior / Sr / experienced keyword detected in title \"Senior Software Engineer\""],
  "confidence": 0.8
}

Niveles: entry | junior | mid | senior | lead | head_of | executive | unknown


4. score_candidate_fit

Compara un CV contra un anuncio de empleo. Solo basado en habilidades — sin inferencia de características protegidas.

Entrada:

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

Salida:

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

Genera de 5 a 10 preguntas específicas que un candidato puede hacer antes de postularse o durante la selección, basadas en las señales del anuncio de empleo.

Entrada:

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

Salida:

{
  "questions": [
    {
      "question": "Can you share the salary range for this role?",
      "why_it_matters": "The advert uses vague compensation language...",
      "category": "compensation"
    }
  ]
}

Categorías: role | company | compensation | flexibility | process | expectations


Uso con un cliente MCP

Claude Desktop (claude_desktop_config.json)

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

Programático (TypeScript con @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();

Bloqueo de pago (x402)

El servidor utiliza el protocolo x402 oficial a través de @x402/express. Los pagos se recaudan en USDC en Base (o en cualquier red EVM/Solana compatible).

Modo de desarrollo (por defecto)

PAYMENTS_ENABLED=false — todas las solicitudes pasan libremente. No se necesita billetera.

Habilitar pagos (testnet — seguro, sin dinero real)

  1. Obtén una dirección de billetera (cualquier billetera EVM, por ejemplo MetaMask)
  2. Obtén USDC de testnet del Coinbase CDP Faucet
  3. Configura tu .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

Sin pago, cada solicitud /mcp recibe un 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": [...] }

Los clientes pagan firmando una transferencia de USDC e incluyendo el encabezado X-PAYMENT — el SDK del cliente x402 (o un agente de IA consciente de x402) maneja esto automáticamente.

Ir a mainnet (pagos reales)

  1. Regístrate en portal.cdp.coinbase.com y crea claves API
  2. Actualiza .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
  1. En src/payments/paymentGuard.ts, cambia el cliente facilitador como se muestra en los comentarios (~línea 30)

Toda la lógica de pago está aislada en src/payments/paymentGuard.ts — no es necesario cambiar ningún otro archivo.


Estructura del proyecto

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

Notas de seguridad

  • La obtención de URL rechaza localhost, rangos de IP privados y protocolos no http/https (protección SSRF)
  • El tamaño de respuesta está limitado a FETCH_MAX_BYTES (1 MB por defecto)
  • El tiempo de espera de obtención se aplica en FETCH_TIMEOUT_MS (10s por defecto)
  • Sin secretos codificados — toda la configuración a través de variables de entorno
  • La herramienta score_candidate_fit compara solo habilidades, experiencia, herramientas y seniority — sin inferencia de características protegidas