mcp-server-104
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-server-104搜尋台北市的Rust工程師職缺,月薪6萬以上,排除面議"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-server-104
An MCP server for Taiwan's 104 Job Bank (104人力銀行). Lets Claude (or any MCP client) search 104's live job listings directly.
Is this tool right for you?
Your situation | Best tool |
Occasionally job hunting on your own | Just open the 104 website |
Want to write a one-off crawler to scrape data | A Playwright / cycletls script is enough, no MCP needed |
Want Claude to analyze/compare/summarize/automate job listings | This MCP |
Related MCP server: job-source-mcp
Installation
Pick one of the following three, depending on which client you use:
A: Clients with quick-command support — one line, config written automatically:
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: Clients where you paste config manually — paste the config into that client's MCP config file:
Claude Desktop / Cursor / Windsurf (JSON):
{
"mcpServers": {
"job104": { "command": "npx", "args": ["-y", "mcp-server-104"] }
}
}OpenAI Codex CLI legacy (~/.codex/config.toml):
[mcp_servers.job104]
command = "npx"
args = ["-y", "mcp-server-104"]A and B do the same thing: tell the client "start this server with npx". The core is
npx -y mcp-server-104everywhere; the only difference is how each client registers it.⚠️ ChatGPT web/desktop cannot connect to this kind of local (stdio) server — it only supports remote URL-based MCP, and there's no machine of yours on its cloud to run
npx.
C: Developers who want to modify the code — after cloning this repo:
npm install && npm run build
claude mcp add job104 -- node /你的路徑/104-mcp-server/dist/index.jsSee "Development" below for daily commands and testing strategy.
How data is fetched
104's search API sits behind Cloudflare bot protection. Using curl or Node fetch (even with Referer / User-Agent headers) gets blocked — returning 403 or Cloudflare's "Just a moment..." challenge page.
The key isn't the header, it's the TLS fingerprint. Cloudflare checks the TLS handshake fingerprint (JA3); a normal program's fingerprint clearly isn't a browser and gets blocked outright.
This project uses cycletls to impersonate Chrome's TLS fingerprint, making Cloudflare think the request comes from a real browser → let through. This way no browser is needed (an order of magnitude lighter, faster, and easier to deploy than Playwright / Selenium), and real JSON is obtained over plain HTTP.
cycletls is backed by a Go-based TLS client subprocess, started once when the server launches and shared for the entire session.
What's available now
Tool | Status | Description |
| ✅ Real data | Search jobs by keyword + multiple filters, with pagination |
| ✅ Real data | Get full details for a single job: complete JD, salary, location, education/experience requirements, skills, language abilities, benefits, industry |
| ✅ Real data | List all open positions at a given company (paginated) |
search_jobs parameters
Parameter | Required | Description |
| ✅ | Job title keyword, e.g. |
| Work location name, e.g. | |
| Minimum monthly salary (TWD), e.g. | |
| Set | |
| Set | |
| Job category name, e.g. | |
| Remote: | |
| Employment type: | |
| Years of experience required: | |
| Page number (20 per page), default 1. Flip further for more results | |
| Max results returned on this page, up to 20, default 5 |
Implementation details of filter parameters (all derived from observing 104's official site UI requests + verifying against
metadata.total):
salaryMinmust be sent together withscmin+sctp=M+scstrict=1; withoutscstrictthe salary filter is completely ignored."Negotiable" salary value is
0, and 104 keeps these by default (negotiable could be very high).excludeNegotiableremoves them.The salary cap
9,999,999is 104's "no upper limit" sentinel value, normalized server-side to "N and above". The salary prefix follows the originals10type (10=negotiable, 30=hourly, 40=daily, 50=monthly, 60=annual) — part-time jobs are mostly hourly, don't read them as monthly.
remoteWork=1 full/2 partial,ro=1 full-time/2 part-time,jobexp=1/3/5/10/99 (mutually exclusive experience bands).Area/job class use a tree code table + pruning: when a parent node matches (e.g. "新竹縣市"), the parent code is used rather than expanding into a bunch of child codes — expanding too much makes 104 return
400. For areas with the same name in multiple places (e.g. "信義區"), neither union nor search is performed;ambiguousAreais returned for the model to confirm with the user (unioning geographically unrelated places is meaningless). For job classes with multiple matches, union is kept (searching related job classes together is usually what's wanted).Ad detection: 104 stuffs ads at the top of results (raw field
jobType=1), which ignore the keyword (e.g. a nurse search surfaces "COACH 精品銷售"). Each result carries afeaturedflag marking it, andexcludeFeatured=truefilters the whole batch out.jobType=2(paid priority slots) still matches the keyword and is treated as a valid result without the flag. Search listings deliberately omit the full JD (kept lean, to avoid the model mismatching a listing's URL to another entry when summarizing lists); useget_job_detailfor full content.
Field naming is consistent across all three tools (all mapped to 104's original field semantics, avoiding same-name-different-meaning):
Concept
search_jobs
get_job_detail
get_company_jobs
Job code (slug, can be fed back to
get_job_detail)
jobId
jobId
jobIdJob URL
url
url
urlArea (district level)
area
area
areaFull address (district + street)
—
location—
Years of experience required
—
experience
experienceProficient tools/languages (C++, Linux)
skills
skills—
Job skills (job-class level, e.g. "軟體工程系統開發")
—
jobSkills—
Company page URL (feed to
get_company_jobs)
companyUrl
companyUrl—
Whether it's an ad slot (
jobType=1)
featured—
—
Update date (the page's "MM/DD更新")
appearDate
appearDate—
jobIdis always a slug (e.g.7uqyj), not 104's internal number — only the slug can be fed back toget_job_detail.skillsalways means "specific technologies".appearDateis uniformlyYYYY/MM/DD. Company job listings deliberately omit dates: the company API's raw data only has year-less formats like8/20, so stale zombie listings always look recently updated (testing found 2025 listings mixed in), silently misleading across year boundaries — if you want a specific listing's date, feed itsjobIdtoget_job_detailfor the full version.
get_job_detail parameters
Parameter | Required | Description |
| ✅ | Job URL or code, e.g. |
get_company_jobs parameters
Parameter | Required | Description |
| ✅ | Company URL or code, e.g. |
| Page number (20 per page), default 1 | |
| Max results returned on this page, up to 20, default 10 |
How the three tools chain together:
Every result from
search_jobs/get_job_detailreturns two URLs:url(job) andcompanyUrl(company).Want the full content of a listing → feed its
urltoget_job_detail.Want to see "what else this company has open" → feed
companyUrltoget_company_jobs(it's a specific company's job list, not a keyword search).
search_jobs ─ url ──────→ get_job_detail
│ │
└─ companyUrl ───────────┴──→ get_company_jobs104 internal API reference
Main endpoint:
GET https://www.104.com.tw/jobs/search/api/jobsRequired headers: Referer: https://www.104.com.tw/jobs/search/, Accept-Language: zh-TW
Common query parameters (this project currently only uses a subset; the rest are for future expansion):
Parameter | Meaning | Example value |
| Keyword | Free text |
| Keyword operator |
|
| Sort |
|
| Pagination |
|
| Area code (comma-separated) | Look up |
| Job class code (comma-separated) | Look up |
| Minimum salary | Integer |
| Remote |
|
| Full/part-time |
|
| Experience |
|
| Education |
|
Area / job class code tables (hosted on static.104.com.tw, no Cloudflare protection, retrievable with a plain fetch):
https://static.104.com.tw/category-tool/json/Area.json
https://static.104.com.tw/category-tool/json/JobCat.jsonOther endpoints:
Job detail:
GET https://www.104.com.tw/job/ajax/content/{slug}(Referer points to/job/{slug})Company jobs:
GET https://www.104.com.tw/api/companies/{code}/jobs?page=1&pageSize=20(returnslist.topJobs+list.normalJobs)
File structure
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 代碼表樹狀比對 + 剪枝Development
npm run build # 編譯 src → dist
npm test # 跑單元測試(先 build 再 node --test,零額外依賴)
node scripts/smoke-test.mjs # 煙霧測試(連真實 104)
npm run inspect # 開 MCP Inspector GUI 除錯After changing code, run npm run build, then restart Claude Code (or use /mcp reconnect) for changes to take effect — the client only fetches the tool list once at session startup.
Testing strategy: pure logic (normalize, URL building, filtering) is extracted into types.ts / query.ts and tested with Node's built-in node --test — fast and no network needed, so breakage is caught immediately. Network-touching parts (job104.ts / httpClient.ts) are verified against the real 104 via smoke tests.
⚠️ Disclaimer
104 has no public official API. This project uses unofficial internal endpoints from the web frontend, which may stop working at any time due to 104's changes.
Automated access may violate 104's terms of service. This project is for personal, low-frequency, learning purposes only.
Do not use it for high-frequency scraping, large-scale crawling, or hosting as a public service — you risk being blocked and face legal exposure.
This project has built-in polite rate limiting (random 1.5~3.5 second interval between requests); do not remove or lower it.
Users are solely responsible for any consequences of using this project.
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