mcp-server-104
mcp-server-104
MCP-Server für die taiwanesische Jobbörse 104. Ermöglicht Claude (oder jedem MCP-Client), direkt in den aktuellen Stellenangeboten von 104 zu suchen.
Ist dieses Tool das Richtige für dich?
Deine Situation | Bestes Tool |
Gelegentlich selbst auf Jobsuche | Direkt die 104-Website nutzen |
Einmaligen Scraper für Daten schreiben wollen | Playwright / cycletls-Skript reicht, kein MCP nötig |
Claude bei der Analyse/ dem Vergleich/ der Zusammenfassung/ Automatisierung von Stellenangeboten helfen lassen | Dieses MCP |
Related MCP server: job-source-mcp
Installation
Wähle eine der folgenden drei Optionen, je nachdem, welchen Client du verwendest:
A: Client mit Schnellbefehl – In einer Zeile erledigt, Konfiguration wird automatisch geschrieben:
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: Client mit manueller Konfiguration – Füge die Konfiguration in die MCP-Konfigurationsdatei des jeweiligen Clients ein:
Claude Desktop / Cursor / Windsurf (JSON):
{
"mcpServers": {
"job104": { "command": "npx", "args": ["-y", "mcp-server-104"] }
}
}OpenAI Codex CLI (ältere Version) (~/.codex/config.toml):
[mcp_servers.job104]
command = "npx"
args = ["-y", "mcp-server-104"]A und B tun dasselbe: Sie teilen dem Client mit, „diesen Server mit npx starten“. Der Kern ist überall
npx -y mcp-server-104, der Unterschied liegt nur darin, wie die jeweiligen Clients ihn registrieren.⚠️ Die ChatGPT-Web-/Desktop-Version kann keine lokalen (stdio) Server wie diesen anbinden – sie unterstützt nur Remote-URL-basierte MCP, und in ihrer Cloud gibt es keinen Computer, auf dem
npxausgeführt werden kann.
C: Entwickler, die Code ändern möchten – Nach dem Klonen dieses Repos:
npm install && npm run build
claude mcp add job104 -- node /你的路徑/104-mcp-server/dist/index.jsTägliche Befehle und Teststrategie findest du unten unter „Entwicklung“.
Wie die Daten abgerufen werden
Die Such-API von 104 liegt hinter dem Cloudflare-Bot-Schutz. Mit curl oder Node fetch (selbst mit Referer/User-Agent) wird man blockiert – es kommt eine 403-Antwort oder die Cloudflare-Herausforderungsseite „Just a moment...“.
Der Schlüssel ist nicht der Header, sondern der TLS-Fingerabdruck. Cloudflare prüft den Fingerabdruck des TLS-Handshakes (JA3); der Fingerabdruck normaler Programme sieht nicht wie ein Browser aus und wird direkt blockiert.
Dieses Projekt verwendet cycletls, um den TLS-Fingerabdruck von Chrome zu imitieren, sodass Cloudflare denkt, die Anfrage stamme von einem echten Browser → lässt sie durch. So ist kein Browser erforderlich (eine Größenordnung leichter als Playwright/Selenium, schneller, besser bereitstellbar), und reines HTTP liefert echte JSON-Daten.
cycletls basiert auf einem in Go geschriebenen TLS-Client-Subprozess, der beim Serverstart einmal gestartet und während der gesamten Sitzung gemeinsam genutzt wird.
Was es jetzt gibt
Tool | Status | Beschreibung |
| ✅ Echte Daten | Suche nach Stellenangeboten nach Schlüsselwort + mehreren Filtern, mit Paginierung |
| ✅ Echte Daten | Ruft die vollständigen Details einer einzelnen Stelle ab: vollständige JD, Gehalt, Standort, Ausbildungs-/Berufserfahrungsanforderungen, Fähigkeiten, Sprachkenntnisse, Zusatzleistungen, Branche |
| ✅ Echte Daten | Listet alle offenen Stellen eines Unternehmens auf (paginiert) |
Parameter von search_jobs
Parameter | Erforderlich | Beschreibung |
| ✅ | Stellenschlüsselwort, z. B. |
| Name des Arbeitsbereichs, z. B. | |
| Untergrenze des Monatsgehalts (in NT$), z.B. | |
| Auf | |
| Auf | |
| Name der Berufskategorie, z.B. | |
| Remote: | |
| Art der Anstellung: | |
| Erforderliche Berufserfahrung: | |
| Seitenzahl (20 Einträge pro Seite), Standard: 1. Für mehr nach hinten blättern | |
| Maximale Anzahl der zurückgegebenen Einträge auf dieser Seite, max. 20, Standard: 5 |
Hinweise zur Implementierung der Filterparameter (alle durch Beobachtung der tatsächlichen Anfragen der 104-Website-UI +
metadata.total-Tests ermittelt):
salaryMaxmuss zusammen mitscmin+sctp=M+scstrict=1gesendet werden; ohnescstrictwird der Gehaltsfilter vollständig ignoriert.Der Gehaltswert für „Verhandelbar“ ist
0, und 104 behält diese standardmäßig bei (Verhandelbar kann hoch sein).excludeNegotiableschließt sie aus.Der Gehaltsobergrenze
9,999,999ist der Sentinel-Wert für „keine Obergrenze“ von 104; der Server normalisiert ihn zu „N oder mehr“. Das Gehaltspräfix wird gemäß dem ursprünglichens10-Typ angegeben (10=Verhandelbar, 30=Stundenlohn, 40=Tageslohn, 50=Monatslohn, 60=Jahreslohn) – Teilzeit ist meist Stundenlohn, nicht als Monatslohn lesen.
remoteWork=1 vollständig/2 teilweise,ro=1 Vollzeit/2 Teilzeit,jobexp=1/3/5/10/99 (sich gegenseitig ausschließende Erfahrungsstufen).Bereich/Jobkategorie verwenden Baumcodetabelle + Pruning: Wenn ein übergeordneter Knoten (z.B. „Neue Landkreise/Städte“) getroffen wird, wird der übergeordnete Code verwendet, nicht in eine Reihe von Untercodes expandiert – zu viele Expansionen führen zu
400von 104. Bei gleichen Namen an mehreren Orten (z.B. „信義区“) wird weder eine Vereinigung noch eine Suche durchgeführt, sondernambiguousAreazurückgegeben, damit das Modell den Benutzer bestätigen kann (eine Vereinigung geografisch nicht zusammenhängender Orte ist sinnlos); bei mehreren Treffern in der Jobklasse bleibt die Vereinigung erhalten (zusammenhängende Jobklassen gemeinsam zu suchen ist normalerweise gewünscht).Anzeigenerkennung: 104 platziert Anzeigen am Anfang der Ergebnisse (ursprüngliches Feld
jobType=1), die Schlüsselwörter ignorieren (z.B. erscheint bei der Suche nach „Krankenschwester“ ein „COACH Luxusverkauf“). Jeder Eintrag gibt einfeatured-Flag zurück, um dies zu markieren;excludeFeatured=truekann sie in einem Rutsch herausfiltern.jobType=2(bezahlte Prioritätsposition) entspricht weiterhin dem Schlüsselwort und wird als gültiges Ergebnis nicht markiert. Die Suchliste enthält absichtlich keine vollständige JD (kompakt, um zu vermeiden, dass das Modell beim Erstellen einer Liste die URL eines Eintrags einem anderen zuordnet); den vollständigen Inhalt erhältst du mitget_job_detail.
Feldnamen sind über die drei Tools hinweg konsistent (alle entsprechen der Semantik der ursprünglichen 104-Felder, um Namensgleichheit mit unterschiedlichen Bedeutungen zu vermeiden):
Konzept
search_jobs
get_job_detail
get_company_jobs
Stellen-Code (Slug, kann an
get_job_detailzurückgegeben werden)
jobId
jobId
jobIdStellen-URL
url
url
urlBereich (Bezirksebene)
area
area
areaVollständige Adresse (Bezirk+Straße)
–
location–
Erforderliche Berufserfahrung
–
experience
experienceBevorzugte Tools/Sprachen (C++, Linux)
skills
skills–
Berufliche Fähigkeiten (Jobklassenebene, z.B. „Software-Engineering-Systementwicklung“)
–
jobSkills–
Firmen-URL (für
get_company_jobs)
companyUrl
companyUrl–
Ist Anzeigenplatz (
jobType=1)
featured–
–
Aktualisierungsdatum („MM/DD aktualisiert“ auf der Seite)
appearDate
appearDate–
jobIdist immer ein slug (z.B.7uqyj), nicht die interne Nummer von 104 – nur der slug kann anget_job_detailzurückgegeben werden.skillsist überall „konkrete Technologie“.appearDateist einheitlichYYYY/MM/DD. Firmenjobs geben bewusst kein Datum zurück: Die Firmen-API hat ursprünglich nur Formate wie8/20ohne Jahr, und veraltete Zombie-Stellen sehen immer wie kürzlich aktualisiert aus (in Tests waren 2025er-Stellen darunter gemischt), was über Jahre hinweg stillschweigend irreführend ist – wenn du das Datum eines Eintrags möchtest, gib seinejobIdanget_job_detail, um das vollständige Datum zu erhalten.
Parameter von get_job_detail
Parameter | Erforderlich | Beschreibung |
| ✅ Ja | Stellen-URL oder Code, z.B. |
Parameter von get_company_jobs
Parameter | Erforderlich | Beschreibung |
| ✅ Ja | Firmen-URL oder Code, z.B. |
| Seitenzahl (Standard: 1) | |
| Maximale Anzahl der zurückgegebenen Einträge auf dieser Seite, max. 20, Standard: 10 |
Wie die drei Tools zusammenarbeiten:
search_jobs/get_job_detailgeben jeweilsurl(Stelle) undcompanyUrl(Firma) zurück.Wenn du den vollständigen Inhalt einer Stelle sehen möchtest → gib ihre
urlanget_job_detail.Wenn du sehen möchtest, „welche anderen Stellen diese Firma hat“ → gib
companyUrlanget_company_jobs(es ist eine Liste von Stellen für bestimmte Firma, keine Schlüsselwortsuche).
search_jobs ─ url ──────→ get_job_detail
│ │
└─ companyUrl ───────────┴──→ get_company_jobsReferenz der internen API von 104
Haupt-Endpoint:
GET https://www.104.com.tw/jobs/search/api/jobsErforderliche Header: Referer: https://www.104.com.tw/jobs/search/, Accept-Language: zh-TW
Häufig verwendete Abfrageparameter (dieses Projekt verwendet derzeit nur einen Teil; der Rest ist für zukünftige Erweiterungen):
Parameter | Bedeutung | Beispielwert |
| Schlüsselwort | Freitext |
| Schlüsselwortoperation |
|
| Sortierung |
|
| Paginierung |
|
| Bereichscode (durch Komma getrennt) | Siehe |
| Jobklassencode (durch Komma getrennt) | Siehe |
| Mindestgehalt | Ganzzahl |
| Remote |
|
| Vollzeit/Teilzeit |
|
| Berufserfahrung |
|
| Ausbildung |
|
Bereichs-/Jobklass-Codetabellen (auf static.104.com.tw, nicht durch Cloudflare blockiert, normaler Fetch reicht):
https://static.104.com.tw/category-tool/json/Area.json
https://static.104.com.tw/category-tool/json/JobCat.jsonAndere Endpoints:
Stellendetails:
GET https://www.104.com.tw/job/ajax/content/{slug}(Referer zeigt auf/job/{slug})Firmenjobs:
GET https://www.104.com.tw/api/companies/{code}/jobs?page=1&pageSize=20(gibtlist.topJobs+list.normalJobszurück)
Dateistruktur
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 代碼表樹狀比對 + 剪枝Entwicklung
npm run build # 編譯 src → dist
npm test # 跑單元測試(先 build 再 node --test,零額外依賴)
node scripts/smoke-test.mjs # 煙霧測試(連真實 104)
npm run inspect # 開 MCP Inspector GUI 除錯Nach Codeänderungen muss npm run build ausgeführt und dann Claude Code neu gestartet werden (oder /mcp reconnect verwenden), damit die Änderungen wirksam werden – der Client ruft die Tool-Liste nur einmal beim Start der Sitzung ab.
Teststrategie: Reine Logik (Normalisierung, URL-Aufbau, Filterung) ist in types.ts / query.ts ausgelagert und wird mit dem integrierten node --test von Node getestet – schnell und ohne Netzwerkverbindung – sofortige Fehlererkennung. Netzwerkbezogene Teile (job104.ts / httpClient.ts) werden mit einem Smoke-Test gegen die echte 104-Website verifiziert.
⚠️ Haftungsausschluss
104 hat keine offizielle öffentliche API. Dieses Projekt verwendet nicht offizielle interne Endpoints des Web-Frontends, die jederzeit durch Änderungen von 104 ungültig werden können.
Automatisierter Zugriff kann gegen die Nutzungsbedingungen von 104 verstoßen. Dieses Projekt ist nur für persönliche, niederfrequente, Lernzwecke gedacht.
Nicht für Hochfrequenz-Abrufe, Massen-Scraping oder als öffentlicher Dienst verwenden – das kann zur Sperrung führen und birgt rechtliche Risiken.
Dieses Projekt enthält bereits eine höfliche Drosselung (zufällige Verzögerung von 1,5 bis 3,5 Sekunden zwischen Anfragen). Bitte nicht entfernen oder reduzieren.
Jegliche Konsequenzen aus der Nutzung dieses Projekts trägt der Benutzer selbst.
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