swissdevjobs
Click on "Deploy 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., "@swissdevjobsfind me senior Python roles in Zurich over 140k"
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.
🇨🇭 swissdevjobs-cli
Search, filter, and apply across 9 job boards in 8 countries — ~4,700 tech jobs with salary data, plus all ~50,000 Swiss postings on jobs.ch & jobup.ch and ~9,000 Singapore IT postings on MyCareersFuture — without leaving your terminal.
🇨🇭 Switzerland · 🇩🇪 Germany · 🇬🇧 UK · 🇺🇸🇨🇦 US & Canada · 🇳🇱 Netherlands · 🇫🇷 France · 🇸🇬 Singapore
Every posting on swissdevjobs.ch — and its five sister boards covering Germany, the UK, the US & Canada, the Netherlands, and France — is required to publish a salary range. That makes them the rare job boards where you can filter by pay before you click. Since v0.5 the tool also searches jobs.ch and jobup.ch — Switzerland's two biggest boards, every industry, ~50,000 postings. It also searches MyCareersFuture, Singapore's government job portal, scoped to its ~9,000 Information Technology postings. One search, nine boards, JSON-first so an LLM agent can drive it.
It also remembers what you've already applied to, across every board, so the same job never shows up twice.
$ sdj list --tech Kubernetes --remote --min-salary 90000 --sort salary
57 shown · 57 match filters · 5521 in feed · 3 hidden (already applied)
----------------------------------------------------------------------------------------
6a60ec82… devitjobs-us p=2026-07-22 Senior Maximo Systems Developer/Integrator TEKsystems c/o Allegis Gr Ottawa USD 208'000–270'400 remote API, Ansible, Bash, ESB, GIS, IBM
6a6cc9bd… devitjobs-us p=2026-07-31 Senior Solutions Architect TEKsystems c/o Allegis Gr Calgary USD 187'200–228'800 remote AI, API, Architect, Cloud, DockerContents
Related MCP server: recruiting-jobs-mcp
Why
🌍 Nine boards, one tool | The all-IT devitjobs family across six countries, plus jobs.ch & jobup.ch for every industry in Switzerland and MyCareersFuture for Singapore IT roles — one search, per-board currencies |
💰 Salary is a first-class filter |
|
📅 Real posting dates | The site re-stamps |
🧠 Remembers where you applied | Local SQLite. Applied jobs vanish from |
🤖 Agent-native | An MCP server plus |
📦 Zero dependencies | Python stdlib only. No |
🎯 Refuses to black-hole your application | Detects postings the site can't actually deliver and tells you where to apply instead |
Please be reasonable
This talks to somebody else's website, built by a small team who chose to make salary transparency mandatory. Keep your request volume human. Don't strip the caching. Don't fire off applications to postings you haven't read — that wastes a real recruiter's afternoon and poisons the well for everyone using the board honestly.
Read swissdevjobs.ch's terms before you automate anything on top of this.
Install
In Claude Code — two commands
/plugin marketplace add Stupidoodle/swissdevjobs-cli
/plugin install swissdevjobs@swissdevjobsThat's it. Claude Code prompts for your name, email, and CV path, then starts
the MCP server with uvx — nothing to install first, and no shell profile is
touched. Ask it "find me senior Python roles in Zurich over 140k" and go.
Requires uv on your PATH
(brew install uv, or curl -LsSf https://astral.sh/uv/install.sh | sh).
As a CLI
uv tool install git+https://github.com/Stupidoodle/swissdevjobs-cli# pipx
pipx install git+https://github.com/Stupidoodle/swissdevjobs-cli
# from a checkout, editable
git clone https://github.com/Stupidoodle/swissdevjobs-cli
cd swissdevjobs-cli
uv tool install -e . # or: pipx install -e .
# no install at all — run it once
uvx --from git+https://github.com/Stupidoodle/swissdevjobs-cli sdj list --remoteNot published to PyPI.
Installs two equivalent binaries: sdj and swissdevjobs. Python 3.9+.
Boards
Three platforms, nine boards, one tool. The devitjobs family shares one backend across six countries — identical API, identical apply flow — the JobCloud platform (jobs.ch, jobup.ch) covers every industry in Switzerland, and MyCareersFuture is Singapore's government job portal, scoped to IT and publishing salary:
Board | Country | Scope | Currency | Salary data | Direct apply | |
🇨🇭 | Switzerland | IT | CHF | always | ✅ | |
🇩🇪 | Germany | IT | EUR | always | ✅ | |
🇬🇧 | United Kingdom | IT | GBP | always | ✅ | |
🇺🇸🇨🇦 | US & Canada | IT | USD | always | ✅ | |
🇳🇱 | Netherlands | IT | EUR | always | ✅ | |
🇫🇷 | France | IT | EUR | always | ✅ | |
🇨🇭 | Switzerland | all industries | CHF | none | 🌐 via ATS | |
🇨🇭 | Switzerland (Romandie) | all industries | CHF | none | 🌐 via ATS | |
🇸🇬 | Singapore | IT | SGD | always | 🌐 on the posting page |
All boards are searched by default. A selector is a board id (just that one)
or a country code (every board there) — --board, with --source and
--country as accepted aliases:
sdj list --board jobsch # just jobs.ch
sdj list --board de --board uk # just Germany + UK, this once
sdj config --boards ch,de # persist: all CH boards + Germany
sdj config --boards swissdevjobs # persist: one board only
sdj config --boards all # back to everythingsdj boards (and the MCP tool list_boards) prints all of this as data —
no need to memorize which board does what:
$ sdj boards
9 boards — select with --board <id|country>, persist with `sdj config --boards`
----------------------------------------------------------------------------------------------------
swissdevjobs ch SwissDevJobs CHF enabled it · salary · unfilterable: workload
germantechjobs de GermanTechJobs EUR enabled it · salary · unfilterable: workload
devitjobs-uk uk DevITjobs UK GBP enabled it · salary · unfilterable: workload
devitjobs-us us DevITjobs US/CA USD enabled it · salary · unfilterable: workload
devitjobs-nl nl DevITjobs NL EUR enabled it · salary · unfilterable: workload
devitjobs-fr fr DevITjobs FR EUR enabled it · salary · unfilterable: workload
jobsch ch jobs.ch CHF enabled all-industries · no-salary · search-driven · no-native-apply · unfilterable: salary, remote, visa, level, tech, language · categories: it
jobup ch jobup.ch CHF enabled all-industries · no-salary · search-driven · no-native-apply · unfilterable: salary, remote, visa, level, tech, language · categories: it
mycareersfuture sg MyCareersFuture SGD enabled it · salary · search-driven · no-native-apply · unfilterable: visa, level, workloadjobs.ch and jobup.ch are search-driven. Their ~50k-job inventory can't be
mirrored (the API serves up to 200 rows per page and stops paging at 100 pages), so
they answer your query server-side, newest first — pass free text for real
coverage, and --category it to stay in tech:
$ sdj list "pflegefachfrau" --board jobsch --limit 2 # any industry, server-side
2 shown · 100 match filters · 100 in feed
----------------------------------------------------------------------------------------
40b2f214… jobsch p=2026-08-25 Dauernachtwache - Dipl. Pflegefachperson HF / FH Stiftung entero Niederlenz —
b5367506… jobsch p=2026-08-25 Dipl. Pflegefachfrau HF/FH mit Fachverantwortung Spital Männedorf AG Männedorf —
$ sdj list "python" --board ch --category it # CH tech across all three boardsThey publish no salary data (rendered honestly as —) and have no native
apply — every posting routes to the company's own ATS, so direct-apply
refuses with the real apply URL instead of pretending.
MyCareersFuture is search-driven too. Singapore's government job portal
(Workforce Singapore) carries ~9,000 active Information Technology postings
and its API caps limit at 100 rows per page, so queries pass server-side,
newest first. Unlike jobs.ch it does publish salary — monthly SGD on the
wire, annualized by the adapter — and skills, so --tech and --remote
filter normally there. --visa, --level, and --workload are unavailable
by design: no sponsorship-eligibility field exists on the wire (Singapore's
employer applies for a specific work pass after the fact, so there is no
"will they sponsor" flag to read); the board's own position levels
("Professional", "Middle Management", "Fresh/entry level", …) have no honest
mapping onto the tool's Junior/Regular/Senior/Principal/CLevel enum, so they
stay in job.raw rather than being guessed at; and no workload-percentage
field exists at all. There is no native apply either — applying happens
on the posting page, through the portal's own flow, so direct-apply
refuses and hands back that page's URL.
Native postings on every devitjobs board publish a salary range. Syndicated listings
(marked isPartner by the boards — the majority outside Switzerland) sometimes
carry no range or a single-point figure; the tool renders those honestly and
refuses to native-apply to them, handing you the real ATS URL instead —
their pages have no native apply form, so a native submission would silently
vanish. The applied-jobs ledger is shared — apply to a role on one board and
the same company+role is hidden on all of them.
Want a board outside the family? Open a board request.
Configure
Reading jobs needs no configuration at all. Applying needs to know who you are.
sdj config --init # writes ~/.config/swissdevjobs-cli/.env (chmod 600)
sdj config # show what's resolved, and from whereThen edit the file:
SDJ_NAME="Your Name"
SDJ_EMAIL="you@example.com"
SDJ_CV="/absolute/path/to/cv.pdf"
# Optional: which boards to search (default: all)
# Board ids and/or country codes: jobsch, jobup, swissdevjobs,
# germantechjobs, devitjobs-*, ch, de, uk, us, nl, fr
# SDJ_BOARDS=ch,deWhere settings come from
.env files are read stdlib-only — no python-dotenv dependency. Anything already
exported in your shell always wins, so nothing on disk can silently shadow it.
flowchart TD
A["1 · Command-line flag<br/>--name / --email / --cv"]
B["2 · Real environment<br/>SDJ_NAME=… sdj …"]
C["3 · $SDJ_ENV_FILE"]
D["4 · ./.env<br/>walking up to /"]
E["5 · ~/.config/swissdevjobs-cli/.env"]
F["direct-apply refuses<br/>run: sdj config --init"]
A -->|"not set"| B
B -->|"not set"| C
C -->|"not set"| D
D -->|"not set"| E
E -->|"still not set"| F
classDef win fill:#c7f0d8,stroke:#1a7f45,color:#0b3d22
classDef mid fill:#dbe7ff,stroke:#2a5db0,color:#12233f
classDef low fill:#f0f0f4,stroke:#8a8a99,color:#2a2a33
classDef bad fill:#ffd6d6,stroke:#c0392b,color:#4a1210
class A win
class B mid
class C,D,E low
class F badHighest priority at the top. A project-local .env beats the global one, which is
handy if you keep a separate identity per job search.
variable | purpose |
| applicant full name for |
| applicant email for |
| default CV path, so you can omit |
| explicit |
| override |
| override |
| markdown application log to import on first run |
| pages fetched per jobs.ch/jobup.ch search (default 5 → 100 rows/board) |
Commands
flowchart TD
START(["sdj"]) --> DISCOVER["🔍 Discover"]
START --> ACT["✉️ Act"]
START --> TRACK["📊 Track"]
DISCOVER --> L["list<br/>search and filter the feed"]
DISCOVER --> S["show<br/>full posting text"]
DISCOVER --> T["tech<br/>most-wanted tech tags"]
DISCOVER --> O["open<br/>posting in your browser"]
ACT --> A["apply<br/>how do I apply to this one?"]
ACT --> DA["direct-apply<br/>submit through the site's form"]
ACT --> AU["auth<br/>clear a Cloudflare challenge"]
TRACK --> AP["applications<br/>everything you have sent"]
TRACK --> ST["stats<br/>cache and application counts"]
TRACK --> CF["config<br/>resolved settings and paths"]
classDef root fill:#8A63D2,stroke:#5b3fa0,color:#ffffff
classDef group fill:#dbe7ff,stroke:#2a5db0,color:#12233f
classDef leaf fill:#f5f6fa,stroke:#9aa0b5,color:#22262f
class START root
class DISCOVER,ACT,TRACK group
class L,S,T,O,A,DA,AU,AP,ST,CF leafEvery command takes --json.
sdj list # everything active
sdj list --tech Python --tech Kubernetes --remote # any of those tags, remote/hybrid
sdj list --min-salary 130000 --location Zurich --sort salary
sdj list "platform engineer" --level Senior --visa # free text + visa sponsorship
sdj list --company Google --include-applied # include ones you've done
sdj show 686f2a1c57370f0152e4950e # by id
sdj show senior-platform-engineer-acme # …or by slug, or a substring
sdj show acme --json # machine-readable
sdj open acme # launch the posting
sdj boards # every board, as data
sdj tech --limit 20 # what the market wantslist --json returns compact summary rows in an envelope — capped at 50
unless you pass --limit (0 = uncapped), with boards_searched and a
coverage note when a search-driven board ran without a query. Empty fields
are omitted and salary is numeric (salary_from/salary_to + currency).
The pre-0.6 raw wire rows are still there behind --raw:
$ sdj list "python" --board swissdevjobs --json --limit 2
{
"total_in_feed": 187,
"total_after_filters": 28,
"hidden_already_applied": 3,
"returned": 2,
"boards_searched": ["swissdevjobs"],
"jobs": [
{
"job_id": "6a8d83d641e56340faa426c0",
"title": "Senior Solutions Engineer Real Estate | Data & BIM",
"company": "Rockstar Recruiting AG",
"city": "Zurich",
"salary_from": 110000,
"salary_to": 130000,
"currency": "CHF",
"workplace": "hybrid",
"contract": ["permanent"],
"language": "German",
"technologies": ["BIM", "CAFM", "Embedded", "ERP", "Mobile", "Python", "AI"],
"posted_at": "2026-08-25T12:00:22+00:00",
"country": "ch",
"source": "swissdevjobs",
"url": "https://swissdevjobs.ch/jobs/Rockstar-Recruiting-AG-Senior-Solutions-Engineer-Real-Estate--Data--BIM"
},
…
]
}list columns: id · board · dates · title · company · city · salary · workplace · tags
On jobs.ch/jobup.ch rows the salary column reads — (the platform publishes
none) and tags are usually empty — their coverage comes from server-side
query search, not client-side tag filters.
The date column carries two values, and the difference matters:
| posted — real creation time, decoded from the ObjectId. Immutable. |
| active — |
A row reading p=2026-04-02 a=2026-08-22 is a four-month-old job wearing a fresh coat
of paint. Sort by --sort posted (the default) to see through it.
sdj apply <id> --json # what route does this posting use?
sdj apply <id> --open # …and open the ATS while you're at it
sdj direct-apply <id> --motivation ./letter.txt
sdj direct-apply <id> --cv ./cv_de.pdf --motivation "Sehr geehrte Damen und Herren, …"
sdj direct-apply <id> --lang-skills fluent --not-eu
sdj apply <id> --complete email # you sent it yourself — record it
sdj apply <id> --complete browser --notes "answered 3 screening questions"--motivation takes inline text or a file path — it checks whether the string is
an existing file. The letter must not contain < or >; the site rejects them.
sdj applications # newest first
sdj applications --json --limit 500
sdj stats # cached jobs, applications, db path
sdj config # identity + paths + which .env loadedHow applying works
Three postings on the same board can need three completely different actions. sdj apply
tells you which, and direct-apply refuses the cases it knows would vanish.
flowchart TD
START(["sdj apply JOB_ID"]) --> Q1{"redirectJobUrl points at<br/>talent.com or jometer?"}
Q1 -->|yes| AGG["🚫 aggregator_posting<br/>exit code 2"]
Q1 -->|no| Q2{"candidateContactWay?"}
Q2 -->|"Email, with<br/>an address"| DIRECT["✅ direct<br/>the site forwards it"]
Q2 -->|"CompanyWebsite,<br/>no address"| CW["🚫 company_website_posting<br/>exit code 2"]
AGG --> BROWSER["🌐 Go apply on the ATS<br/>Recruitee · Workday · Greenhouse<br/>Lever · Personio · SmartRecruiters"]
CW --> BROWSER
DIRECT --> POST["POST /api/jobApply<br/>multipart: name, email,<br/>motivation, CV PDF"]
POST --> OK{"HTTP 200?"}
OK -->|yes| MARK["💾 recorded in SQLite<br/>hidden from future list"]
OK -->|no| ERR["❌ raised with the response body"]
BROWSER -.->|"after you submit"| COMPLETE["sdj apply JOB_ID --complete browser"]
COMPLETE --> MARK
classDef start fill:#8A63D2,stroke:#5b3fa0,color:#ffffff
classDefThis server cannot be deployed
Maintenance
Related MCP Connectors
Job platform for AI agents. Track tech jobs from companies that match your stack.
Job alerts for AI agents: describe a role once, get only new postings from 1,500+ tech job boards.
JobsPipe — data pipeline of every job posting on the web. Search live, normalized job postings from 30+ ATS feeds and job boards for AI agents via MCP.
Google Jobs in 26 countries: job title, company, location, parsed salary, full description and every apply link, with date, type and remote filters and an only-new mode. Runs on Apify, $2 per 1,000 jobs.
Related MCP Servers
AlicenseNot gradedqualityDmaintenanceEnables AI agents to search and explore a large database of tech job listings with filtering options.MIT- FlicenseNot gradedqualityCmaintenanceEnables AI agents to pull live job listings from major ATS platforms (Greenhouse, Lever, Ashby, Workable), Hacker News hiring threads, and detect hiring signals on company career pages.-
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to search and explore remote job listings, salary stats, and popular roles, and supports token-authenticated actions like job alerts and apply redirects.MIT

Civify MCP Serverofficial
AlicenseAqualityBmaintenanceEnables AI agents to parse resumes, tailor CVs, score ATS compatibility, redact PII, scrape job postings, and track applications through a career platform.829 npmMIT