Skip to main content
Glama
mohitakki

Germany Intelligence MCP

by mohitakki

πŸ‡©πŸ‡ͺ Germany Intelligence MCP

An evidence-based career intelligence layer for the German software job market, exposed to Claude as an MCP server.

CI Node TypeScript Tests License Status

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?”


⚠️ 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.


Related MCP server: LinkedIn Profile MCP Server

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.

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."


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.

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.


How a recommendation earns its place

Five gates, all of which must pass:

  1. Sample floor β€” β‰₯ 20 deduplicated jobs analysed

  2. Evidence floor β€” the skill appears in β‰₯ 5 postings

  3. Relevance floor β€” β‰₯ 10% of jobs request it

  4. Deficit β€” your self-assessed level is below 4/5

  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

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


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 filter

  • regressions.test.ts β€” one describe per critical bug, with the original defect in the comment so nobody "fixes" the test later

  • ingest.test.ts β€” pipeline β†’ repository β†’ SQLite, error isolation, migration idempotency

  • corpus.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:

  1. Read ADR-003 before touching the taxonomy β€” several constraints look arbitrary and are load-bearing

  2. npm test must stay green; the false-positive corpus is the guard rail

  3. New decisions get a new ADR β€” template. ADRs are immutable once Accepted; supersede, don't edit


MIT Β· Built as a systems-engineering exercise in evidence-based recommendation. Not affiliated with Bundesagentur fΓΌr Arbeit or Arbeitnow.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

–Maintainers
–Response time
–Release cycle
–Releases (12mo)
Commit activity

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

  • A
    license
    -
    quality
    C
    maintenance
    US + EU salary benchmarking, pay transparency compliance, and semantic endpoints. 1,400+ US occupations, 28 EU countries. MCP server for AI agents.
    Last updated
    MIT
  • A
    license
    -
    quality
    D
    maintenance
    An intelligent bridge between Claude and LinkedIn. This MCP server empowers Claude to act as your high-level career consultant, capable of auditing your profile, optimizing your identity, and managing your professional content with real-time API access.
    Last updated
    1
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    An MCP server that exposes a perpetual, honest job-application pipeline as typed tools an LLM agent can call, with fit scoring, verified resume building, and a submission planner enforced by code, not prompts.
    Last updated
    16
    MIT
  • A
    license
    -
    quality
    A
    maintenance
    A local-first, open-source MCP server that analyzes jobs, matches your CV, tailors documents, and tracks applications β€” all on your machine with no data uploaded.
    Last updated
    AGPL 3.0

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

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/mohitakki/germany-intelligence-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server