Germany Intelligence MCP
An evidence-based career intelligence layer for the German software job market, exposed to Claude as an MCP server.
README
<div align="center">
🇩🇪 Germany Intelligence MCP
An evidence-based career intelligence layer for the German software job market, exposed to Claude as an MCP server.
Not a job search tool. A decision engine that answers one question every morning:
“What should I learn today to maximise my chances of getting a software engineering job in Germany?”
</div>
⚠️ Read this before using it
v1.0 Core is infrastructure, not a finished product. Its own QA pass graded it C — Needs Another Stabilization Sprint, and that assessment is published in full:
| ✅ Works | Ingestion, deduplication, storage, trend analysis, full-text search, metrics, health, MCP tool surface |
| ⚠️ Known-wrong | German language detection has 50% recall. Extraction misses hyphenated German compounds in 24% of postings |
| ❌ Not implemented | 4 of 5 recommendation surfaces are stubs. The 5th would tell a C1 English speaker to learn English |
👉 docs/KNOWN-ISSUES.md — every defect, measured, reproducible, and pinned by a test.
This honesty is deliberate. The project's core promise is that no claim is made without evidence, and that has to apply to claims about the project itself.
The idea
Most job tools optimise for finding postings. This one assumes finding jobs is easy and knowing what to learn is hard. It treats the job market as a dataset and asks what the evidence actually says.
┌──────────────────────────────┐
│ Claude (reasoning layer) │
└───────────────┬──────────────┘
│ MCP / JSON-RPC over stdio
┌─────────────────────────────────────▼─────────────────────────────────────┐
│ L5 MCP TOOL SURFACE 10 coarse tools · Zod-validated · evidence │
├───────────────────────────────────────────────────────────────────────────┤
│ L4 ENGINES trend · essentialism · gap · match · learning │
├───────────────────────────────────────────────────────────────────────────┤
│ L3 KNOWLEDGE STORE SQLite + FTS5 · repositories own all SQL │
├───────────────────────────────────────────────────────────────────────────┤
│ L2 ANALYSIS PIPELINE normalise → extract → classify → dedupe │
├───────────────────────────────────────────────────────────────────────────┤
│ L1 SOURCE ADAPTERS anti-corruption layer · one interface, N APIs │
└───────────────────────────────────────────────────────────────────────────┘
Dependency rule: arrows point downward only. An engine never imports a source adapter; a source adapter never imports the database.
What "evidence-based" means here
No number is emitted without its denominator. This is enforced by the type system and the database schema, not by convention:
interface Evidence {
subject: string; // "ops.docker"
frequency: number; // 0.63
observedIn: number; // 214 ← the numerator
totalAnalysed: number; // 339 ← THE DENOMINATOR
windowDays: number; // 14
sampleJobIds: string[]; // openable, verifiable
meanConfidence: number; // how good was the extraction behind this
lowConfidence: boolean; // sample too small to act on
}
recommendation.evidence is NOT NULL. pickTodayFocus() cannot compile without
threading evidence through. A recommendation you cannot defend is unrepresentable.
Quick start
git clone https://github.com/mohitakki/germany-intelligence-mcp.git
cd germany-intelligence-mcp
npm ci # no native build, no compiler
cp .env.example .env
cp config/profile.example.json config/profile.json # then edit your skill levels
npm test # 106 tests, ~40 against real SQLite
npm run ingest # live fetch from the German market
npm run health # is the data trustworthy right now?
No native dependencies. Storage is node:sqlite behind a driver port — see
ADR-006 for why that decision and a production bug were
the same decision.
<details> <summary><b>Wire it into Claude Desktop</b></summary>
npm run build
claude_desktop_config.json:
{
"mcpServers": {
"germany-intelligence": {
"command": "node",
"args": ["--no-warnings", "/absolute/path/to/germany-intelligence-mcp/dist/index.js"],
"env": {
"GIM_DATA_DIR": "/absolute/path/to/germany-intelligence-mcp/data",
"GIM_PROFILE_PATH": "/absolute/path/to/germany-intelligence-mcp/config/profile.json"
}
}
}
}
Restart Claude, then ask: "Run my daily briefing." </details>
MCP tools
| Tool | Status | What it does |
|---|---|---|
health |
✅ | Database + FTS, providers, last ingest age, extraction quality, jobs indexed. Call this first if anything looks odd. |
refresh_market |
✅ | Fetch, normalise, dedupe, store. Returns full run metrics. |
get_market_trends |
✅ | Technology demand with counts, frequencies, confidence and sample job ids. |
get_skill_gap |
✅ | Your profile vs the market, bucketed and ranked, each item evidenced. |
search_jobs |
✅ | BM25 full-text + filters. One row per deduplicated vacancy. |
list_sources |
✅ | Providers, config state, legal status, compliance notes. |
mark_progress |
✅ | Records learning so a skill stops being recommended. |
get_daily_briefing |
◐ | Market summary + trends + gaps. Recommendation sections are empty. |
get_today_focus |
❌ | Throws — Phase 6. |
generate_interview_questions · analyse_resume |
❌ | Return [] — Phase 7/8. |
Ten coarse tools, not thirty. Every description is loaded into Claude's context on every turn, so tool sprawl directly degrades reasoning quality.
Data sources
Only sources that permit programmatic access are shipped.
| Source | Status | Coverage |
|---|---|---|
| Bundesagentur für Arbeit | ⚠️ implemented, never network-tested | Germany's largest job database (~1M postings). Official federal API, public client id, no registration. |
| Arbeitnow | ✅ verified end-to-end | Public board API, no key. Berlin/Munich tech, English-language — the visa-sponsoring startup segment. |
| ATS boards (Greenhouse, Lever, Ashby, Personio, Recruitee) | 🔲 Phase 3 | Canonical postings from the companies you actually want. Highest-value next build. |
src/sources/custom/ |
🔲 slot | Your own adapters, disabled by default, complianceNote required. |
<details> <summary><b>Why no LinkedIn / StepStone / Indeed / Xing</b></summary>
None offer an open job-search API. All four prohibit automated access in their Terms of Service and enforce it technically. No adapter ships for them and none should be added — an IP ban is the mild outcome; a legal notice while applying for a German work visa is the bad one.
src/sources/custom/ exists for sources you have a legitimate route to: company
career feeds, boards with a documented API, partner feeds, manual CSV import.
See ADR-001. </details>
How a recommendation earns its place
Five gates, all of which must pass:
- Sample floor — ≥ 20 deduplicated jobs analysed
- Evidence floor — the skill appears in ≥ 5 postings
- Relevance floor — ≥ 10% of jobs request it
- Deficit — your self-assessed level is below 4/5
- Cooldown — not recommended in the last 21 days
Survivors are ranked:
priority = marketFrequency × skillDeficit × momentum
Multiplicative, not additive — so a skill you already have scores ≈ 0 no matter how in-demand it is. That is the mechanism that stops the system telling you to learn React for the fortieth time.
The engineering story
This repo went through four adversarial cycles after "done". Each one is documented, because the findings are more interesting than the code.
🔴 The write path was dead and the run said ok
JobRepository.upsert ended with INSERT INTO job_fts (...) ON CONFLICT DO NOTHING.
SQLite rejects UPSERT against a virtual table. Every insert threw, runIngest caught
it per-job, and the run reported success having stored nothing.
The schema had been "verified" by running a hand-written query against hand-inserted rows — never the repository's own statement. A test that doesn't cross the boundary tests nothing. → ADR-006
🔴 A Berlin train line was inflating AWS demand
Eight of twelve ordinary German sentences produced phantom skill demand:
| Sentence (verbatim from real postings) | Phantom skill |
|---|---|
| "no less than three years of experience" | CSS/Less |
| "the position starts in spring 2027" | Spring Boot |
| "gut erreichbar mit der S3 und der U2" | AWS ← an S-Bahn line |
| "you react quickly to incidents" | React |
The defect wasn't the aliases — it was the default. A token was assumed technical unless something stopped it. Now ambiguous aliases are assumed non-technical unless a requirement marker or a confirmed technology list proves otherwise. → ADR-003
🔴 The dedup threshold was picked, not measured
Version one used Hamming ≤ 3 because it "felt safely conservative." It matched nothing but byte-identical text.
0 bits identical 11 bits ~15% extra boilerplate
5 bits hyphenation edit 13 bits bullets reordered
9 bits one extra sentence 31 bits a completely different job
Real variants cluster at 5–13; unrelated documents sit past 30. 12 sits in a wide, empty gap. → ADR-002
🔴 The extractor was accurate; the data was wrong
Ground truth over 111 real postings: 96.6% recall, 1 false positive in 59 mentions. The extractor is fine. The corpus genuinely contains almost no React/TypeScript work — TypeScript appears in 3 of 111 postings.
Diagnosing this as a source selection problem rather than an extraction problem is the difference between fixing it and tuning the wrong knob.
Documentation
| Document | What it is |
|---|---|
| KNOWN-ISSUES.md | Every measured defect with a reproduction. Read before trusting output. |
| RUNBOOK.md | Daily operation, metric interpretation, backup, replay, troubleshooting |
| ARCHITECTURE.md | Layers, data flow, dependency rules, trade-offs |
| ROADMAP.md | Build order with reasoning, ~35 h to complete |
| docs/ADR/ | 7 decision records — why, with rejected alternatives |
<details> <summary><b>Architecture Decision Records</b></summary>
| ADR | Decision |
|---|---|
| 001 | Provider architecture and the anti-corruption layer |
| 002 | SimHash deduplication, blocking, measured threshold |
| 003 | Curated taxonomy + context-window gating |
| 004 | Untrusted-content trust boundary |
| 005 | LLM as additive enrichment only, never authoritative |
| 006 | SQLite via node:sqlite behind a driver port |
| 007 | Evidence as a required type |
</details>
Testing
106 tests. ~40 cross into real SQLite. Three suites, three jobs:
npm test # everything
npx tsx --test tests/regressions.test.ts # one block per shipped bug
npx tsx --test tests/corpus.test.ts # 111 real postings, pinned baselines
extraction.test.ts— pure logic: German gender markers, company suffixes, SimHash, the essentialism filterregressions.test.ts— onedescribeper critical bug, with the original defect in the comment so nobody "fixes" the test lateringest.test.ts— pipeline → repository → SQLite, error isolation, migration idempotencycorpus.test.ts— 111 real German postings. Pins the measured baselines and the known defects. When you fix one, its test fails — that failure is the proof.
The corpus is committed so every number in the docs is reproducible.
Security
- Prompt injection — job text is attacker-controlled. Nonce-delimited envelope built before the LLM path exists; detection counts, never filters (ADR-004)
- Credential redaction — no query string ever reaches a log line, enforced at the boundary and again centrally
- SQL injection — fully parameterised; verified against
'); DROP TABLE job;-- - Adversarial robustness — 27 hostile inputs (500 KB descriptions, malformed HTML, emoji, RTL, null bytes, FTS syntax), zero crashes
Contributing
Personal project, but the patterns are reusable. If you fork it:
- Read ADR-003 before touching the taxonomy — several constraints look arbitrary and are load-bearing
npm testmust stay green; the false-positive corpus is the guard rail- New decisions get a new ADR — template. ADRs are immutable once Accepted; supersede, don't edit
<div align="center">
MIT · Built as a systems-engineering exercise in evidence-based recommendation. Not affiliated with Bundesagentur für Arbeit or Arbeitnow.
</div>
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。