mcp-server-104
mcp-server-104
MCP-сервер для тайваньской биржи труда 104. Позволяет Claude (или любому MCP-клиенту) напрямую искать актуальные вакансии 104.
Подходит ли вам этот инструмент?
Ваша ситуация | Лучший инструмент |
Изредка ищете работу сами | Просто откройте сайт 104 |
Хотите написать разовый скрипт-парсер для сбора данных | Достаточно скрипта на Playwright / cycletls, MCP не нужен |
Хотите, чтобы Claude анализировал/сравнивал/собирал/автоматизировал вакансии | Этот MCP |
Related MCP server: job-source-mcp
Установка
Выберите один из трёх вариантов, в зависимости от вашего клиента:
A: клиент с быстрыми командами — одна строка, настройка записывается автоматически:
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: клиент с ручной вставкой настроек — вставьте настройки в MCP-конфиг вашего клиента:
Claude Desktop / Cursor / Windsurf (JSON):
{
"mcpServers": {
"job104": { "command": "npx", "args": ["-y", "mcp-server-104"] }
}
}OpenAI Codex CLI (старая версия, ~/.codex/config.toml):
[mcp_servers.job104]
command = "npx"
args = ["-y", "mcp-server-104"]A и B делают одно и то же: говорят клиенту «запусти этот сервер через npx». Ядро везде одно —
npx -y mcp-server-104, разница только в том, как каждый клиент его регистрирует.⚠️ Веб/десктопная версия ChatGPT не подключается к таким локальным (stdio) серверам — она поддерживает только MCP с удалённым URL, а в её облаке нет вашего компьютера, где можно запустить
npx.
C: разработчик, хотите менять код — после клонирования этого репозитория:
npm install && npm run build
claude mcp add job104 -- node /你的路徑/104-mcp-server/dist/index.jsПовседневные команды и стратегия тестирования — см. раздел «Разработка» ниже.
Как получаются данные
Поисковый API 104 скрыт за защитой Cloudflare от ботов. curl или Node fetch (даже с Referer / User-Agent) блокируются — возвращается 403 или страница-челлендж Cloudflare "Just a moment...".
Ключ не в заголовках, а в TLS-отпечатке. Cloudflare проверяет отпечаток TLS-рукопожатия (JA3); отпечаток обычной программы сразу видно, что это не браузер, и запрос блокируется.
Этот проект использует cycletls, чтобы подделать TLS-отпечаток Chrome и заставить Cloudflare думать, что запрос идёт от настоящего браузера → пропускает. Так не нужно открывать браузер (на порядок легче Playwright / Selenium, быстрее, проще в развёртывании) — чистый HTTP, и вы получаете настоящий JSON.
Под капотом cycletls — дочерний процесс TLS-клиента, написанный на Go; запускается один раз при старте сервера и используется всё время.
Что уже есть
Tool | Статус | Описание |
| ✅ реальные данные | Поиск вакансий по ключевому слову + множеству фильтров, с пагинацией |
| ✅ реальные данные | Полные детали одной вакансии: полное JD, зарплата, место, требования к образованию/опыту, навыки, языки, льготы, отрасль |
| ✅ реальные данные | Список всех открытых вакансий компании (с пагинацией) |
Параметры search_jobs
Параметр | Обязателен | Описание |
| ✅ | Ключевое слово должности, например |
| Название региона, например | |
| Нижняя граница месячной зарплаты (NTD), например | |
|
| |
|
| |
| Название категории должности, например | |
| Удалёнка: | |
| Тип занятости: | |
| Требуемый стаж: | |
| Номер страницы (20 записей на страницу), по умолчанию 1. Хотите больше — листайте дальше | |
| Максимум записей на этой странице, до 20, по умолчанию 5 |
Нюансы реализации параметров фильтрации (все получены наблюдением за реальными запросами UI сайта 104 + замером
metadata.totalна практике):
Для
salaryMinнужно отправлятьscmin+sctp=M+scstrict=1одновременно; безscstrictфильтр зарплаты полностью игнорируется.Значение зарплаты «по договорённости» —
0, 104 по умолчанию сохраняет такие (по договорённости может быть очень высокая).excludeNegotiableисключает их.Верхний предел зарплаты
9,999,999— это сентинел-значение 104 «без ограничения», на стороне сервера нормализуется в «от N». Префикс зарплаты помечается по исходному типуs10(10=по договорённости, 30=почасовая, 40=подённая, 50=месячная, 60=годовая) — у частичной занятости чаще почасовая, не читайте как месячную.
remoteWork=1 полностью/2 частично,ro=1 полная/2 частичная занятость,jobexp=1/3/5/10/99 (взаимоисключающие диапазоны стажа).Регионы/категории используют древовидную таблицу кодов + отсечение ветвей: при попадании в родительский узел (например, «新竹縣市») используется родительский код, без разворачивания в кучу дочерних — слишком много кодов заставит 104 вернуть
400. При одинаковых именах в разных местах (например, «信義區») не делается ни объединение, ни поиск — возвращаетсяambiguousArea, чтобы модель уточнила у пользователя (объединять географически несвязанные места бессмысленно); при множественных попаданиях категорий объединение сохраняется (искать связанные категории вместе обычно и нужно).Обнаружение рекламы: 104 вставляет рекламу в начало результатов (исходное поле
jobType=1), и она игнорирует ключевое слово (например, в поиске медсестёр вылезает «продажи люксовых товаров COACH»). Каждая запись помечается флагомfeatured,excludeFeatured=trueотфильтровывает их пачкой.jobType=2(платное приоритетное место) всё же соответствует ключевому слову и считается валидным результатом без пометки. Список поиска намеренно не содержит полного JD (компактнее и чтобы модель при составлении списка не перепутала URL одной записи с другой) — полное содержимое берите черезget_job_detail.
Именование полей единообразно во всех трёх инструментах (везде соответствует семантике исходных полей 104, чтобы избежать одинаковых имён для разных вещей):
Понятие
search_jobs
get_job_detail
get_company_jobs
Код вакансии (slug, можно скормить обратно в
get_job_detail)
jobId
jobId
jobIdURL вакансии
url
url
urlРегион (уровень района)
area
area
areaПолный адрес (район + улица)
—
location—
Требуемый стаж
—
experience
experienceИнструменты/языки (C++, Linux)
skills
skills—
Профессиональные навыки (уровень категории, например «разработка программных инженерных систем»)
—
jobSkills—
URL страницы компании (скормить в
get_company_jobs)
companyUrl
companyUrl—
Рекламное ли место (
jobType=1)
featured—
—
Дата обновления («MM/DD更新» на странице)
appearDate
appearDate—
jobIdвсегда slug (например,7uqyj), а не внутренний номер 104 — только slug можно скормить обратно вget_job_detail.skillsвезде означает «конкретные технологии».appearDateвезде в форматеYYYY/MM/DD. Даты в вакансиях компании намеренно не возвращаются: исходный API компании даёт только формат без года, вроде8/20, и давно не обновлявшиеся «зомби-вакансии» всегда выглядят свежими (на практике среди них попадаются вакансии 2025 года) — тихое введение в заблуждение через границу года; хотите дату конкретной записи — скормите еёjobIdвget_job_detailи получите полную.
Параметры get_job_detail
Параметр | Обязателен | Описание |
| ✅ | URL или код вакансии, например |
Параметры get_company_jobs
Параметр | Обязателен | Описание |
| ✅ | URL или код компании, например |
| Номер страницы (20 записей на страницу), по умолчанию 1 | |
| Максимум записей на этой странице, до 20, по умолчанию 10 |
Как связаны три инструмента:
search_jobs/get_job_detailв каждой записи возвращают два URL:url(вакансия) иcompanyUrl(компания).Хотите полное содержимое вакансии → скормите её
urlвget_job_detail.Хотите узнать «какие ещё вакансии есть у этой компании» → скормите
companyUrlвget_company_jobs(это список вакансий конкретной компании, а не поиск по ключевому слову).
search_jobs ─ url ──────→ get_job_detail
│ │
└─ companyUrl ───────────┴──→ get_company_jobsСправочник внутреннего API 104
Основной endpoint:
GET https://www.104.com.tw/jobs/search/api/jobsОбязательные заголовки: Referer: https://www.104.com.tw/jobs/search/, Accept-Language: zh-TW
Часто используемые параметры запроса (проект сейчас использует только часть, остальные — на будущее):
Параметр | Значение | Пример |
| Ключевое слово | произвольный текст |
| Операция по ключевому слову |
|
| Сортировка |
|
| Пагинация |
|
| Код региона (через запятую) | см. |
| Код категории (через запятую) | см. |
| Минимальная зарплата | целое число |
| Удалёнка |
|
| Полная/частичная занятость |
|
| Стаж |
|
| Образование |
|
Таблицы кодов регионов/категорий (лежат на static.104.com.tw, без защиты Cloudflare, обычный fetch их достанет):
https://static.104.com.tw/category-tool/json/Area.json
https://static.104.com.tw/category-tool/json/JobCat.jsonПрочие endpoints:
Детали вакансии:
GET https://www.104.com.tw/job/ajax/content/{slug}(Referer указывает на/job/{slug})Вакансии компании:
GET https://www.104.com.tw/api/companies/{code}/jobs?page=1&pageSize=20(возвращаетlist.topJobs+list.normalJobs)
Структура файлов
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 代碼表樹狀比對 + 剪枝Разработка
npm run build # 編譯 src → dist
npm test # 跑單元測試(先 build 再 node --test,零額外依賴)
node scripts/smoke-test.mjs # 煙霧測試(連真實 104)
npm run inspect # 開 MCP Inspector GUI 除錯После изменения кода нужно npm run build, затем перезапустить Claude Code (или reconnect через /mcp) — клиент забирает список инструментов только один раз при старте сессии.
Стратегия тестирования: вся чистая логика (normalize, сборка URL, фильтрация) вынесена в types.ts / query.ts и тестируется встроенным в Node node --test — быстро и без сети, поломку видно сразу. Части, работающие с сетью (job104.ts / httpClient.ts), проверяются smoke-тестами против реального 104.
⚠️ Отказ от ответственности
У 104 нет публичного официального API. Проект использует неофициальные внутренние endpoints веб-фронтенда, которые могут в любой момент перестать работать из-за обновлений 104.
Автоматизированный доступ может нарушать условия обслуживания 104. Проект предназначен только для личного, низкочастотного, учебного использования.
Не используйте для высокочастотного сбора, массового парсинга или развёртывания как публичного сервиса — легко получить блокировку, плюс юридические риски.
В проект уже встроено вежливое ограничение частоты (случайный интервал 1.5~3.5 секунды между запросами), не удаляйте и не уменьшайте его.
Любые последствия использования этого проекта — на ответственности пользователя.
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