Job Ad Intelligence MCP
Um servidor MCP pago que ajuda agentes de IA a analisar anúncios de emprego. Cinco ferramentas: extrair dados estruturados de qualquer anúncio de emprego (texto ou URL), normalizar strings salariais em mínimo/máximo/moeda/período, detectar nível de senioridade a partir de títulos de cargos, pontuar um currículo contra um anúncio de emprego e gerar perguntas direcionadas para candidatura. Preço de $0,002 a $0,05 por chamada, pago em USDC na Base via x402. Nenhuma chave de API necessária.
Documentação
Job Ad Intelligence MCP
Um serviço pago compatível com MCP que ajuda agentes de LLM a analisar anúncios de emprego. Expõe cinco ferramentas para extrair dados estruturados, normalizar salários, detectar senioridade, avaliar a adequação de candidatos e gerar perguntas de candidatura.
Construído com Node.js, TypeScript, o Model Context Protocol SDK e um guarda de pagamento pronto para x402.
Início 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
Variáveis de ambiente
| Variável | Padrão | Descrição |
|---|---|---|
PORT | 3000 | Porta HTTP para escutar |
HOST | 0.0.0.0 | Endereço de bind |
PAYMENTS_ENABLED | false | Ativar gate de pagamento x402 (true/false) |
FETCH_TIMEOUT_MS | 10000 | Máx. ms para aguardar ao buscar uma URL de anúncio de emprego |
FETCH_MAX_BYTES | 1048576 | Tamanho máximo de resposta ao buscar uma URL (bytes) |
NODE_ENV | development | Ambiente do Node |
Consulte .env.example para um exemplo completo anotado.
Endpoint MCP
O servidor expõe um transporte HTTP Streamable em:
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
Verificação de saúde (sem autenticação necessária):
GET http://localhost:3100/health
Referência de ferramentas
1. extract_job_ad
Extrai dados estruturados de um anúncio de emprego. Aceita texto bruto ou uma URL.
Entrada:
{
"text": "Senior Software Engineer at Acme Corp...",
"url": "https://example.com/jobs/123"
}
Um de text ou url é obrigatório. Quando ambos são fornecidos, text é preferido.
Saída:
{
"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
Analisa uma string de salário de formato livre em um objeto estruturado.
Entrada:
{
"salary_text": "£45k to £60k per annum",
"location": "London, UK"
}
Saída:
{
"raw": "£45k to £60k per annum",
"min": 45000,
"max": 60000,
"currency": "GBP",
"period": "year",
"notes": []
}
Lida com: £45k–£60k, up to £70,000, from £500 per day, $120k, €80,000 pa, competitive, DOE.
3. detect_seniority
Classifica o nível de senioridade a partir de um título de cargo e descrição opcional.
Entrada:
{
"title": "Senior Software Engineer",
"description": "You'll have 6+ years of experience..."
}
Saída:
{
"seniority": "senior",
"signals": ["Title match: Senior / Sr / experienced keyword detected in title \"Senior Software Engineer\""],
"confidence": 0.8
}
Níveis: entry | junior | mid | senior | lead | head_of | executive | unknown
4. score_candidate_fit
Compara um currículo contra um anúncio de emprego. Baseado apenas em habilidades — sem inferência de características protegidas.
Entrada:
{
"cv_text": "..full CV text...",
"job_ad_text": "..full job ad text.."
}
Saída:
{
"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
Gera de 5 a 10 perguntas direcionadas que um candidato pode fazer antes de se candidatar ou durante a triagem, com base em sinais no anúncio de emprego.
Entrada:
{
"job_ad_text": "..full job ad text.."
}
Saída:
{
"questions": [
{
"question": "Can you share the salary range for this role?",
"why_it_matters": "The advert uses vague compensation language...",
"category": "compensation"
}
]
}
Categorias: role | company | compensation | flexibility | process | expectations
Uso com um cliente MCP
Claude Desktop (claude_desktop_config.json)
{
"mcpServers": {
"job-ad-intelligence": {
"url": "http://localhost:3100/mcp",
"transport": "streamable-http"
}
}
}
Programático (TypeScript com @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();
Gate de pagamento (x402)
O servidor usa o protocolo x402 oficial via @x402/express. Os pagamentos são coletados em USDC na Base (ou em qualquer rede EVM/Solana suportada).
Modo de desenvolvimento (padrão)
PAYMENTS_ENABLED=false — todas as solicitações passam livremente. Nenhuma carteira necessária.
Ativando pagamentos (testnet — seguro, sem dinheiro real)
- Obtenha um endereço de carteira (qualquer carteira EVM, ex.: MetaMask)
- Obtenha USDC de testnet do Coinbase CDP Faucet
- Defina seu
.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
Sem pagamento, cada solicitação /mcp recebe um 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": [...] }
Os clientes pagam assinando uma transferência de USDC e incluindo o cabeçalho X-PAYMENT — o SDK do cliente x402 (ou um agente de IA ciente de x402) lida com isso automaticamente.
Indo para mainnet (pagamentos reais)
- Cadastre-se em portal.cdp.coinbase.com e crie chaves de API
- Atualize
.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
- Em
src/payments/paymentGuard.ts, troque o cliente facilitador conforme mostrado nos comentários (~linha 30)
Toda a lógica de pagamento está isolada em src/payments/paymentGuard.ts — nenhum outro arquivo precisa ser alterado.
Estrutura do projeto
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 segurança
- A busca de URL rejeita
localhost, faixas de IP privadas e protocolos não-http/https (proteção SSRF) - O tamanho da resposta é limitado a
FETCH_MAX_BYTES(padrão 1 MB) - O timeout de busca é aplicado em
FETCH_TIMEOUT_MS(padrão 10s) - Sem segredos codificados — toda configuração via variáveis de ambiente
- A ferramenta
score_candidate_fitcompara apenas habilidades, experiência, ferramentas e senioridade — sem inferência de características protegidas