mcp-server-104
mcp-server-104
Servidor MCP para el portal de empleo 104 de Taiwán. Permite que Claude (o cualquier cliente MCP) busque directamente ofertas de empleo en tiempo real de 104.
¿Es esta herramienta adecuada para ti?
Tu situación | Mejor herramienta |
Buscas trabajo ocasionalmente por tu cuenta | Abre directamente el sitio web de 104 |
Quieres escribir un scraper puntual para obtener datos | Con un script de Playwright / cycletls basta, no necesitas MCP |
Quieres que Claude te ayude a analizar/comparar/resumir/automatizar ofertas de empleo | Este MCP |
Related MCP server: job-source-mcp
Instalación
Elige una de las siguientes tres opciones, según el cliente que uses:
A: Cliente con comando rápido — se resuelve en una línea, la configuración se escribe automáticamente:
claude mcp add job104 -- npx -y mcp-server-104 # Claude Codecodex mcp add job104 -- npx -y mcp-server-104 # OpenAI Codex CLI(新版才有;舊版走 B 的 TOML)B: Cliente con configuración manual — pega la configuración en el archivo de configuración MCP de ese cliente:
Claude Desktop / Cursor / Windsurf (JSON):
{
"mcpServers": {
"job104": { "command": "npx", "args": ["-y", "mcp-server-104"] }
}
}OpenAI Codex CLI versión antigua (~/.codex/config.toml):
[mcp_servers.job104]
command = "npx"
args = ["-y", "mcp-server-104"]A y B hacen lo mismo: le indican al cliente «inicia este servidor con npx». El núcleo en todos los casos es
npx -y mcp-server-104; la diferencia está solo en cómo cada cliente lo registra.⚠️ La versión web/de escritorio de ChatGPT no puede conectarse a este tipo de servidor local (stdio) — solo admite MCP por URL remota, y en su nube no hay una máquina tuya donde ejecutar
npx.
C: Desarrollador, quieres modificar el código — tras clonar este repo:
npm install && npm run build
claude mcp add job104 -- node /你的路徑/104-mcp-server/dist/index.jsLos comandos de uso diario y la estrategia de pruebas están en la sección «Desarrollo» más abajo.
Método de obtención de datos
La API de búsqueda de 104 está detrás de la protección anti-bots de Cloudflare. Con curl o fetch de Node (incluso con Referer / User-Agent) te bloquea — devuelve 403 o la página de desafío "Just a moment..." de Cloudflare.
La clave no son las cabeceras, es la huella TLS. Cloudflare comprueba la huella del handshake TLS (JA3); la huella de los programas normales no parece de navegador y se bloquea directamente.
Este proyecto usa cycletls para hacerse pasar por la huella TLS de Chrome, haciendo que Cloudflare crea que la petición viene de un navegador real → la deja pasar. Así no hace falta abrir un navegador (un orden de magnitud más ligero que Playwright / Selenium, rápido y fácil de desplegar), y se obtiene el JSON real solo con HTTP.
cycletls usa internamente un subproceso de cliente TLS escrito en Go, que se inicia una vez al arrancar el servidor y se comparte durante toda la sesión.
Qué hay ahora
Tool | Estado | Descripción |
| ✅ Datos reales | Busca ofertas por palabra clave + múltiples filtros, con paginación |
| ✅ Datos reales | Obtiene el detalle completo de una oferta: JD completo, salario, ubicación, requisitos de estudios/experiencia, habilidades, idiomas, beneficios, sector |
| ✅ Datos reales | Lista todas las ofertas activas de una empresa (paginado) |
Parámetros de search_jobs
Parámetro | Obligatorio | Descripción |
| ✅ | Palabra clave del puesto, por ejemplo |
| Nombre de la zona de trabajo, por ejemplo | |
| Salario mensual mínimo (Nuevos dólares taiwaneses), por ejemplo | |
| Pon | |
| Pon | |
| Nombre de la categoría del puesto, por ejemplo | |
| Remoto: | |
| Tipo de jornada: | |
| Años de experiencia requeridos: | |
| Número de página (20 resultados por página), por defecto 1. Para ver más, avanza de página | |
| Límite de resultados devueltos en esta página, máximo 20, por defecto 5 |
Detalles de implementación de los filtros (obtenidos observando las peticiones reales de la interfaz web de 104 + verificados con
metadata.total):
salaryMindebe enviarse junto conscmin+sctp=M+scstrict=1; sinscstrictel filtro de salario se ignora por completo.El valor de salario «negociable» es
0, y 104 lo conserva por defecto (el negociable podría ser muy alto).excludeNegotiablelos excluye.El salario máximo
9,999,999es el valor centinela de «sin límite superior» de 104; el servidor lo normaliza a «N o más». El prefijo del salario se indica según el tipo originals10(10=negociable, 30=por hora, 40=por día, 50=por mes, 60=por año) — los de media jornada suelen ser por hora, no lo leas como salario mensual.
remoteWork=1 completo/2 parcial,ro=1 jornada completa/2 media jornada,jobexp=1/3/5/10/99 (rangos de años mutuamente excluyentes).Zonas/categorías usan tabla de códigos en árbol + poda: si se acierta un nodo padre (como «Condados de Hsinchu»), se usa el código padre, sin expandir a un montón de códigos hijos — expandir demasiado hace que 104 devuelva
400. Las zonas con el mismo nombre en varios lugares (como «Xinyi») no se unen ni se buscan; se devuelveambiguousAreapara que el modelo confirme con el usuario (unir zonas geográficamente inconexas no tiene sentido); en cambio, los múltiples aciertos de categorías sí se unen (buscar categorías relacionadas juntas suele ser lo deseado).Detección de anuncios: 104 mete anuncios al principio de los resultados (campo original
jobType=1), que ignoran la palabra clave (por ejemplo, una búsqueda de enfermeros muestra «COACH ventas de artículos de lujo»). Cada resultado devuelve el flagfeaturedpara marcarlos, yexcludeFeatured=truelos filtra todos de una vez.jobType=2(posición prioritaria de pago) sigue coincidiendo con la palabra clave y se considera resultado válido, sin marcar. La lista de búsqueda excluye deliberadamente el JD completo (para ser conciso y evitar que el modelo, al organizar la lista, asocie la URL de un resultado con otro); el contenido completo se obtiene conget_job_detail.
Los nombres de campos son coherentes entre las tres herramientas (todos corresponden a la semántica de los campos originales de 104, para evitar que el mismo nombre signifique cosas distintas):
Concepto
search_jobs
get_job_detail
get_company_jobs
Código de oferta (slug, se puede pasar de vuelta a
get_job_detail)
jobId
jobId
jobIdURL de la oferta
url
url
urlZona (nivel de distrito)
area
area
areaDirección completa (distrito + calle)
—
location—
Años de experiencia requeridos
—
experience
experienceHerramientas/idiomas dominados (C++, Linux)
skills
skills—
Competencias del puesto (nivel de categoría, como «desarrollo de sistemas de software»)
—
jobSkills—
URL de la página de la empresa (para pasar a
get_company_jobs)
companyUrl
companyUrl—
Si es posición de anuncio (
jobType=1)
featured—
—
Fecha de actualización (el «MM/DD actualizado» de la página)
appearDate
appearDate—
jobIdes siempre un slug (como7uqyj), no el número interno de 104 — solo el slug se puede pasar de vuelta aget_job_detail.skillses siempre «tecnología concreta».appearDatese unifica comoAAAA/MM/DD. Las ofertas de empresa deliberadamente no devuelven fecha: la API de empresa solo tiene el formato original8/20sin año, y las ofertas zombi sin actualizar parecen siempre recientes (se ha comprobado que hay ofertas de 2025 mezcladas), lo que engaña silenciosamente entre años — si quieres la fecha de una oferta concreta, pasa sujobIdaget_job_detailpara obtener la completa.
Parámetros de get_job_detail
Parámetro | Obligatorio | Descripción |
| ✅ | URL o código de la oferta, por ejemplo |
Parámetros de get_company_jobs
Parámetro | Obligatorio | Descripción |
| ✅ | URL o código de la empresa, por ejemplo |
| Número de página (20 resultados por página), por defecto 1 | |
| Límite de resultados devueltos en esta página, máximo 20, por defecto 10 |
Cómo se encadenan las tres herramientas:
search_jobs/get_job_detaildevuelven en cada resultado dos URLs:url(oferta) ycompanyUrl(empresa).Si quieres el contenido completo de una oferta → pasa su
urlaget_job_detail.Si quieres ver «qué otras ofertas tiene esta empresa» → pasa
companyUrlaget_company_jobs(es la lista de ofertas de una empresa concreta, no una búsqueda por palabra clave).
search_jobs ─ url ──────→ get_job_detail
│ │
└─ companyUrl ───────────┴──→ get_company_jobsReferencia de la API interna de 104
Endpoint principal:
GET https://www.104.com.tw/jobs/search/api/jobsCabeceras necesarias: Referer: https://www.104.com.tw/jobs/search/, Accept-Language: zh-TW
Parámetros de consulta habituales (este proyecto solo usa una parte; el resto queda para ampliaciones futuras):
Parámetro | Significado | Valor de ejemplo |
| Palabra clave | Texto libre |
| Operación de palabra clave |
|
| Ordenación |
|
| Paginación |
|
| Código de zona (separado por comas) | Consultar |
| Código de categoría (separado por comas) | Consultar |
| Salario mínimo | Entero |
| Remoto |
|
| Jornada completa/media jornada |
|
| Años de experiencia |
|
| Nivel de estudios |
|
Tablas de códigos de zona / categoría (en static.104.com.tw, sin bloqueo de Cloudflare, se pueden obtener con un fetch normal):
https://static.104.com.tw/category-tool/json/Area.json
https://static.104.com.tw/category-tool/json/JobCat.jsonOtros endpoints:
Detalle de oferta:
GET https://www.104.com.tw/job/ajax/content/{slug}(Referer apuntando a/job/{slug})Ofertas de empresa:
GET https://www.104.com.tw/api/companies/{code}/jobs?page=1&pageSize=20(devuelvelist.topJobs+list.normalJobs)
Estructura de archivos
src/
index.ts 進入點:建 server、掛 tool、接 stdio、處理關閉
config.ts 所有設定 / 魔術數字(JA3 指紋、endpoint、節流區間…)
types.ts 乾淨型別 + normalizeJob / JobDetail / CompanyJob(防腐層)
query.ts 純函式:組查詢網址、client 端過濾、enum 對照
slug.ts 從 104 網址取出職缺 slug / 公司碼(types/query 共用)
codes.ts 地區/職類「名稱→官方代碼」解析(樹狀比對+剪枝,快取代碼表)
api/
httpClient.ts cycletls 單例(TLS 指紋偽裝)
throttle.ts 禮貌性隨機節流 1.5~3.5s
job104.ts 104 抓取層:組 URL → 打 API → 重試 → 正規化
tools/
searchJobs.ts search_jobs
getJobDetail.ts get_job_detail
getCompanyJobs.ts get_company_jobs
scripts/
smoke-test.mjs 手動發 JSON-RPC 驗證,不用開 Claude 也能測
test/
types.test.mjs normalize 邏輯(薪資格式、面議、哨兵值…)
query.test.mjs 組網址 / slug / 公司碼 / 過濾 / enum 對照
codes.test.mjs 代碼表樹狀比對 + 剪枝Desarrollo
npm run build # 編譯 src → dist
npm test # 跑單元測試(先 build 再 node --test,零額外依賴)
node scripts/smoke-test.mjs # 煙霧測試(連真實 104)
npm run inspect # 開 MCP Inspector GUI 除錯Después de modificar el código hay que ejecutar npm run build y reiniciar Claude Code (o reconectar con /mcp) para que surta efecto — el cliente solo obtiene la lista de herramientas una vez al iniciar la sesión.
Estrategia de pruebas: toda la lógica pura (normalización, construcción de URLs, filtrado) está extraída a types.ts / query.ts, y se prueba con el node --test integrado de Node: rápido y sin necesidad de conexión — si algo se rompe, lo sabes al momento. La parte que toca la red (job104.ts / httpClient.ts) se verifica con smoke-tests contra el 104 real.
⚠️ Aviso legal
104 no tiene API oficial pública. Este proyecto usa endpoints internos no oficiales del frontend web, que pueden dejar de funcionar en cualquier momento si 104 cambia su sitio.
El acceso automatizado puede violar los términos de servicio de 104. Este proyecto es solo para uso personal, de baja frecuencia y con fines educativos.
No lo uses para scraping de alta frecuencia, rastreo masivo, ni lo montes como servicio público — es fácil que te bloqueen y hay riesgo legal.
Este proyecto ya incluye limitación de velocidad cortés (intervalo aleatorio de 1,5~3,5 segundos entre peticiones); no lo elimines ni lo reduzcas.
El usuario asume toda responsabilidad por las consecuencias del uso de este proyecto.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseAqualityCmaintenanceEnables users to search LinkedIn's public job listings with advanced filters like location, salary, and experience level. It allows MCP-compatible clients to retrieve real-time job opportunities without requiring LinkedIn authentication or API keys.12MIT
- AlicenseNot gradedqualityBmaintenanceSearches job listings from Taiwanese job boards (104 and Yourator) and returns normalized results.MIT
- AlicenseAqualityAmaintenanceSearches 104 job listings with natural-language filters and retrieves full postings via MCP tools.322MIT
- FlicenseNot gradedqualityBmaintenanceEnables job search on LinkedIn through MCP tools, including keyword and location search, filtering by remote, easy apply, experience level, job type, and date, and retrieving job details.
Related MCP Connectors
Search live startup jobs from Claude, Cursor, or ChatGPT via MCP. Free, no account needed.
Job search and interview prep MCP. 11 tools, OAuth 2.1, cross-LLM. four-leaf.ai.
Search remote and onsite jobs through the public Corvi Careers MCP server.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/a7512cs/104-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server