Skip to main content
Glama
iwsen

Arbetsförmedlingen MCP

by iwsen

Arbetsförmedlingen MCP

An MCP server for the open APIs of Arbetsförmedlingen (the Swedish Public Employment Service, JobTech). It lets an AI assistant search Platsbanken job ads, read full ad texts, get labour-market statistics and look up taxonomy codes - using plain words, no API keys.


🚀 Features

  • Job search: free text, locations, occupations, skills, employment type, remote work, traineeships, radius around coordinates.

  • Names instead of codes: "Göteborg", "Systemutvecklare", "Python" are automatically mapped to taxonomy concept ids (you can also pass the ids directly).

  • Full job ad: description, requirements, conditions, contacts, application link.

  • Statistics: number of ads by region, municipality, occupation, skill.

  • Autocomplete and taxonomy: suggestions like in Platsbanken and lookup of official names.

  • Compact responses: only the needed fields, so the model context isn't filled with raw JSON.

  • No keys or registration: uses the open JobSearch and Taxonomy APIs.


Related MCP server: Apiverket MCP Server

📂 Project structure

.
├── README.md
├── package.json
├── src/
│   ├── index.js        # Entry point (stdio transport)
│   ├── server.js       # MCP tool definitions
│   ├── api.js          # JobSearch and Taxonomy client, taxonomy cache
│   └── format.js       # Ad and statistics compaction
└── test/
    └── format.test.js

📦 Installation

cd arbetsformedlingen-mcp
npm install

Requires Node.js 20+.


🔌 Connecting

Claude Code

claude mcp add arbetsformedlingen -- node /path/to/arbetsformedlingen-mcp/src/index.js

Claude Desktop / Codex / any MCP client

{
  "mcpServers": {
    "arbetsformedlingen": {
      "command": "node",
      "args": ["/path/to/arbetsformedlingen-mcp/src/index.js"]
    }
  }
}

Debugging with MCP Inspector

npm run inspect

Environment variables (optional)

Variable

Default

Purpose

JOBSEARCH_BASE_URL

https://jobsearch.api.jobtechdev.se

JobSearch API address

TAXONOMY_BASE_URL

https://taxonomy.api.jobtechdev.se/v1/taxonomy/main

Taxonomy API address

AF_TIMEOUT_MS

20000

Request timeout


🛠 Tools

All tools are read-only.

search_jobs - job search

Parameter

Description

q

Free text (job title, technology, employer)

municipality, region

Locations: names or concept ids

occupation, occupation_field

Occupations and occupational fields

skill

Skills

employment_type, worktime_extent, workplace_model

Employment type, extent (Heltid/Deltid), workplace model

employer

Employer name or organization number

remote, trainee, larling, open_for_all

Flags: remote work, traineeship, apprenticeship, "open to all"

experience_not_required

Only ads with no experience requirement

driving_license_required

Whether a driving licence is required

published_after, published_before

Publication period

latitude, longitude, radius_km

Radius search

limit, offset

Pagination (limit up to 2100 - pages of 100 are fetched sequentially; offset up to 2000)

sort

relevance, pubdate-desc, pubdate-asc, applydate-desc, applydate-asc, updated

description_chars

How many characters of the description to include in the card

get_job - job ad by ID

Full description, requirements (must_have / nice_to_have), salary, address, contacts and how to apply. Parameter: id.

job_stats - statistics

Accepts the same filters as search_jobs, plus group_by (region, municipality, occupation-name, occupation-group, occupation-field, country, employment-type, worktime-extent, skill, language) and top.

suggest - autocomplete

Suggestions by word prefix: q, limit.

search_taxonomy - taxonomy lookup

Finds concept ids and official names: type, query, limit. Rarely needed - search_jobs maps names itself - but handy for ambiguous cases.


⚠️ Limitations

  • Data comes from public APIs in real time; an internet connection is required.

  • JobSearch itself caps a request at 100 ads and offset at 2000, so one search yields at most 2100 ads - refine filters (e.g. by date) for larger result sets.

  • The remote, trainee and larling filters rely on analysis of ad texts and may be inaccurate.

  • Ad texts are mostly in Swedish.


🧪 Tech stack

Available Tools

5 tools
get_jobJob by IDA
Read-onlyIdempotent

Full ad details: description, requirements, conditions, contacts and how to apply. The ID comes from search_jobs.

NameRequiredDescriptionDefault
idYesAd ID, e.g. "31579964"

TDQS

A3.9/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent and openWorld, so safety and freshness are covered. The description contributes the useful distinction that this returns the full ad body versus a search teaser, but says nothing about behavior on an invalid or expired ID.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences with no filler. The returned-content list comes first and the ID provenance second, which is the order an agent needs it in.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read tool with no output schema, enumerating the returned fields compensates well for the missing return definition, and annotations cover the safety profile. Only edge-case behavior for a bad ID is unaddressed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% and already documents the single parameter's format with an example. The description adds genuine meaning on top by specifying provenance — the ID must come from search_jobs — which the schema does not say.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the resource and enumerates the payload it returns (description, requirements, conditions, contacts, application method), which makes clear this is a full-detail fetch rather than a listing. It names search_jobs as the source of the ID, implicitly differentiating it from the search sibling, though it never uses an explicit verb like 'retrieve' or says 'single job ad'.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"The ID comes from search_jobs" gives an explicit prerequisite and routes the agent to the correct sibling before calling this one. It stops short of stating when not to use it (e.g., when only summaries are needed) or what happens with a stale/unknown ID.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

job_statsJob statisticsA
Read-onlyIdempotent

Number of jobs broken down by region, municipality, occupation, field, skill, etc. For example: "how many Python jobs per region". Accepts the same filters as search_jobs.

NameRequiredDescriptionDefault
qNoFree text: job title, technology, employer, e.g. "python" or "sjuksköterska"
topNoRows per field
skillNoSkills. Names ("Python") or concept ids from the taxonomy
regionNoRegions (län). Names ("Stockholms län") or concept ids from the taxonomy
remoteNotrue — jobs where remote work is likely
larlingNotrue — apprenticeships (lärling)
traineeNotrue — traineeships (trainee)
employerNoEmployer name or organization number (digits only)
group_byNoFields to group by
latitudeNoLatitude for radius search
longitudeNoLongitude for radius search
radius_kmNoRadius in km (requires latitude and longitude)
occupationNoOccupations. Names ("Systemutvecklare") or concept ids from the taxonomy
municipalityNoMunicipalities. Names ("Göteborg") or concept ids from the taxonomy
open_for_allNotrue — "Öppen för alla" ads
employment_typeNoEmployment type. Names ("Vanlig anställning") or concept ids from the taxonomy
published_afterNoISO date or datetime (YYYY-MM-DD / YYYY-MM-DDTHH:MM:SS), or a number of minutes ago
workplace_modelNoWorkplace model. Names ("Distans") or concept ids from the taxonomy
worktime_extentNoWorking hours. Names ("Heltid") or concept ids from the taxonomy
occupation_fieldNoOccupation fields. Names ("Data/IT") or concept ids from the taxonomy
published_beforeNoISO date or datetime
experience_not_requiredNotrue — only jobs with no experience requirement
driving_license_requiredNo

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and openWorldHint, so the safety profile is covered. The description adds the key behavioral fact not in annotations: the result is aggregated counts per grouping dimension, not job records. It does not discuss result shape or how large the breakdown can get, leaving a modest gap.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two tight sentences: the core purpose with its grouping dimensions is front-loaded, followed by a concrete example and the sibling relationship. Nothing is redundant.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 23-parameter tool with no output schema, the description should clarify the return structure (how counts are keyed per group_by field) and any limits on the breakdown. The example query hints at this but does not describe the response, which is a noticeable gap given the tool's complexity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 96%, so nearly every parameter is documented in the schema itself, including top, group_by and the taxonomy-name-or-id convention. The description adds only the inherited-from-search_jobs framing and no syntax or behavior detail beyond the schema, so the baseline of 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific resource and operation: counts of jobs broken down by region, municipality, occupation, field, skill. The example ("how many Python jobs per region") makes the aggregation nature unambiguous and separates it from search_jobs, which returns listings rather than counts.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"Accepts the same filters as search_jobs" establishes this as the aggregation counterpart to the sibling search tool, giving the agent a clear basis for choosing between them. It stops short of an explicit when-to-use/when-not statement or naming the alternative as the tool for raw listings.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_jobsSearch jobsA
Read-onlyIdempotent

Search jobs in Platsbanken (Arbetsförmedlingen). All filters are optional and combined with AND. Places, occupations and skills can be given as plain names — they are matched to the taxonomy automatically. Returns short cards; use get_job for the full ad text.

NameRequiredDescriptionDefault
qNoFree text: job title, technology, employer, e.g. "python" or "sjuksköterska"
sortNoSort order: relevance, publication date or application deadlinerelevance
limitNoHow many jobs to return (1–2100; more than 100 takes several consecutive API requests)
skillNoSkills. Names ("Python") or concept ids from the taxonomy
offsetNoPagination offset (0–2000, API ceiling)
regionNoRegions (län). Names ("Stockholms län") or concept ids from the taxonomy
remoteNotrue — jobs where remote work is likely
larlingNotrue — apprenticeships (lärling)
traineeNotrue — traineeships (trainee)
employerNoEmployer name or organization number (digits only)
latitudeNoLatitude for radius search
longitudeNoLongitude for radius search
radius_kmNoRadius in km (requires latitude and longitude)
occupationNoOccupations. Names ("Systemutvecklare") or concept ids from the taxonomy
municipalityNoMunicipalities. Names ("Göteborg") or concept ids from the taxonomy
open_for_allNotrue — "Öppen för alla" ads
employment_typeNoEmployment type. Names ("Vanlig anställning") or concept ids from the taxonomy
published_afterNoISO date or datetime (YYYY-MM-DD / YYYY-MM-DDTHH:MM:SS), or a number of minutes ago
workplace_modelNoWorkplace model. Names ("Distans") or concept ids from the taxonomy
worktime_extentNoWorking hours. Names ("Heltid") or concept ids from the taxonomy
occupation_fieldNoOccupation fields. Names ("Data/IT") or concept ids from the taxonomy
published_beforeNoISO date or datetime
description_charsNoHow many characters of the description to include in the card (0 — no description)
experience_not_requiredNotrue — only jobs with no experience requirement
driving_license_requiredNo

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover read-only/idempotent/open-world safety, so the bar is lower; the description still adds useful behavior: results are short cards rather than full ads, plain names are auto-resolved against the taxonomy, and it flags that a limit above 100 triggers several consecutive API requests. It does not describe pagination interplay or result shape in detail, but that is largely covered elsewhere.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Four compact sentences, front-loaded with what the tool is and what it returns, with no filler. Every sentence carries a distinct piece of information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a 25-parameter tool with no output schema, the description covers the essentials: source, filter combination semantics, return format ('short cards'), and where to get full detail. The only gap is a slightly vague notion of what a 'card' contains, which an agent must learn by calling it.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 96%, so the schema already documents nearly every parameter, establishing a baseline of 3. The description adds the AND-combination rule for filters and the name-to-taxonomy matching behavior, which is genuinely beyond the schema, but nothing else about the 25 parameters is elaborated.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Search jobs in Platsbanken (Arbetsförmedlingen)') and explicitly separates itself from the sibling get_job ('use get_job for the full ad text'). An agent can distinguish it from search_taxonomy, suggest, and job_stats from the description alone.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explains that all filters are optional and AND-combined, and names the alternative (get_job) with the condition that selects it (needing full ad text). Lacks explicit 'when not to use' guidance for the other siblings, but the usage context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_taxonomyJobTech taxonomy searchA
Read-onlyIdempotent

Finds concept ids and official names for occupations, municipalities, regions, skills, languages, etc. Usually only needed when a name is ambiguous: search_jobs matches names itself.

NameRequiredDescriptionDefault
typeYesConcept type
limitNo
queryNoName substring; without it the start of the list is returned

TDQS

A4.2/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and openWorldHint, so the safety profile is covered. The description adds the kind of value returned (ids plus official names) but says nothing about result ordering, pagination, or how partial matches are ranked beyond what the schema states. Adequate but not rich.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences with no waste, and the sibling comparison is front-loaded right after the purpose statement. Nothing needs trimming or reordering.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description does state what comes back (concept ids and official names), which covers the main return-value gap. For a 3-parameter read tool with solid annotations this is nearly complete, missing only ordering/pagination expectations for the list-style result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 67%: the `type` enum and `query` are already documented in the schema, while `limit` carries only default/min/max with no description. The description's list of concept domains loosely maps to the enum but adds no matching semantics or guidance on `limit`, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a concrete verb (finds) and the exact resources returned (concept ids and official names) across the taxonomy domains, and explicitly distinguishes itself from the sibling search_jobs. An agent can tell what this tool produces and why it exists without opening the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit usage condition ('usually only needed when a name is ambiguous') and names the alternative that handles the common case ('search_jobs matches names itself'). This is exactly the when-to-use/when-to-use-something-else guidance an agent needs for tool selection.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

suggestQuery autocompleteA
Read-onlyIdempotent

Free-text suggestions (skills, occupations, places, employers) from the start of a word — as in Platsbanken.

NameRequiredDescriptionDefault
qYesStart of a word or phrase, e.g. "utv"
limitNo

TDQS

A3.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint and openWorldHint, so safety is covered. The description still adds real behavioral value by specifying prefix-matching semantics ('from the start of a word') and the domains matched, though it says nothing about result ordering, caps, or locale of the underlying data.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

A single dense sentence, front-loaded with the core action and followed by the entity domains; every clause carries information and nothing is repeated from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter read-only tool with annotations covering safety, most of what an agent needs is present. However, there is no output schema and the description never states what a suggestion response looks like or how many items come back, leaving a small but real gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 50%: q is documented with an example and limit is self-explanatory via its constraints. The description adds meaning by indicating q matches the start of words across several entity types, but gives no further detail on how results are limited or ranked, so it only marginally compensates for the undocumented limit parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb+resource: free-text autocomplete suggestions, and enumerates the concept domains (skills, occupations, places, employers). It does not explicitly distinguish itself from the similar-sounding sibling search_taxonomy, so an agent must infer the boundary between autocomplete and taxonomy search.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

"From the start of a word" implies prefix-style lookup and thus hints at usage, but there is no explicit when-to-use, when-not-to-use, or pointer to an alternative such as search_taxonomy for full-term search. Context is implied rather than stated.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 5 tool updatesv1.0.0
    • First observedget_job
    • First observedjob_stats
    • First observedsearch_jobs
    • First observedsearch_taxonomy
    • First observedsuggest

TDQS

A4/5.0

Scored across 5 tools

Disambiguation4/5

Each tool targets a distinct layer: search_jobs for ads, get_job for a single ad's detail, job_stats for aggregates, search_taxonomy for concept IDs, and suggest for autocomplete. The only soft overlap is search_taxonomy vs suggest, since both resolve names to taxonomy concepts, but the descriptions clearly frame one as a lookup/ID resolver and the other as start-of-word autocomplete.

Naming Consistency4/5

All names are lowercase snake_case, which is consistent at the style level. The verb pattern is mostly verb_noun (search_jobs, search_taxonomy, get_job), with minor deviations: job_stats is noun-only and suggest is a bare verb without an object, though both remain unambiguous.

Tool Count5/5

Five tools is tightly scoped to a job-search domain: search, detail, statistics, taxonomy lookup, and autocomplete. Nothing feels redundant and there is no obvious operation left out that would need its own tool.

Completeness4/5

The surface covers the full read path of a job board: discovery via search_jobs, drill-down via get_job, aggregation via job_stats, plus taxonomy resolution and autocomplete. Minor gaps exist—no bulk taxonomy enumeration or employer/application lookup—but agents can work around these using search_taxonomy and suggest.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables LLMs to search, access, and retrieve official Swedish statistics from Statistics Sweden (SCB), providing access to 1,200+ tables covering demographics, economy, environment, labor market, and education with 75+ years of historical data.
    9
    MIT
  • A
    license
    A
    quality
    F
    maintenance
    Query Swedish public data from AI tools. Includes company data, SCB statistics, weather, transport, public agencies, and more through the Apiverket API.
    2
    29 npm
    2
    MIT
  • F
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to search job listings and retrieve job details, as well as view and manage career-profile information such as work history, job summaries, career aspirations, and technical skills.
    -