Arbetsförmedlingen MCP
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., "@Arbetsförmedlingen MCPVisa mig jobb som systemutvecklare i Göteborg"
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.
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 installRequires Node.js 20+.
🔌 Connecting
Claude Code
claude mcp add arbetsformedlingen -- node /path/to/arbetsformedlingen-mcp/src/index.jsClaude Desktop / Codex / any MCP client
{
"mcpServers": {
"arbetsformedlingen": {
"command": "node",
"args": ["/path/to/arbetsformedlingen-mcp/src/index.js"]
}
}
}Debugging with MCP Inspector
npm run inspectEnvironment variables (optional)
Variable | Default | Purpose |
|
| JobSearch API address |
|
| Taxonomy API address |
|
| Request timeout |
🛠 Tools
All tools are read-only.
search_jobs - job search
Parameter | Description |
| Free text (job title, technology, employer) |
| Locations: names or concept ids |
| Occupations and occupational fields |
| Skills |
| Employment type, extent (Heltid/Deltid), workplace model |
| Employer name or organization number |
| Flags: remote work, traineeship, apprenticeship, "open to all" |
| Only ads with no experience requirement |
| Whether a driving licence is required |
| Publication period |
| Radius search |
| Pagination ( |
|
|
| 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,traineeandlarlingfilters rely on analysis of ad texts and may be inaccurate.Ad texts are mostly in Swedish.
🧪 Tech stack
Node.js ESM, no build step
@modelcontextprotocol/sdkandzodfor tool schemasJobSearch API and Taxonomy API from JobTech
Tests:
npm test(node --test)
Available Tools
5 toolsget_jobJob by IDARead-onlyIdempotent
Full ad details: description, requirements, conditions, contacts and how to apply. The ID comes from search_jobs.
| Name | Required | Description | Default |
|---|---|---|---|
| id | Yes | Ad ID, e.g. "31579964" |
TDQS
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.
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.
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.
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.
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.
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 statisticsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Free text: job title, technology, employer, e.g. "python" or "sjuksköterska" | |
| top | No | Rows per field | |
| skill | No | Skills. Names ("Python") or concept ids from the taxonomy | |
| region | No | Regions (län). Names ("Stockholms län") or concept ids from the taxonomy | |
| remote | No | true — jobs where remote work is likely | |
| larling | No | true — apprenticeships (lärling) | |
| trainee | No | true — traineeships (trainee) | |
| employer | No | Employer name or organization number (digits only) | |
| group_by | No | Fields to group by | |
| latitude | No | Latitude for radius search | |
| longitude | No | Longitude for radius search | |
| radius_km | No | Radius in km (requires latitude and longitude) | |
| occupation | No | Occupations. Names ("Systemutvecklare") or concept ids from the taxonomy | |
| municipality | No | Municipalities. Names ("Göteborg") or concept ids from the taxonomy | |
| open_for_all | No | true — "Öppen för alla" ads | |
| employment_type | No | Employment type. Names ("Vanlig anställning") or concept ids from the taxonomy | |
| published_after | No | ISO date or datetime (YYYY-MM-DD / YYYY-MM-DDTHH:MM:SS), or a number of minutes ago | |
| workplace_model | No | Workplace model. Names ("Distans") or concept ids from the taxonomy | |
| worktime_extent | No | Working hours. Names ("Heltid") or concept ids from the taxonomy | |
| occupation_field | No | Occupation fields. Names ("Data/IT") or concept ids from the taxonomy | |
| published_before | No | ISO date or datetime | |
| experience_not_required | No | true — only jobs with no experience requirement | |
| driving_license_required | No |
TDQS
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.
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.
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.
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.
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.
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 jobsARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| q | No | Free text: job title, technology, employer, e.g. "python" or "sjuksköterska" | |
| sort | No | Sort order: relevance, publication date or application deadline | relevance |
| limit | No | How many jobs to return (1–2100; more than 100 takes several consecutive API requests) | |
| skill | No | Skills. Names ("Python") or concept ids from the taxonomy | |
| offset | No | Pagination offset (0–2000, API ceiling) | |
| region | No | Regions (län). Names ("Stockholms län") or concept ids from the taxonomy | |
| remote | No | true — jobs where remote work is likely | |
| larling | No | true — apprenticeships (lärling) | |
| trainee | No | true — traineeships (trainee) | |
| employer | No | Employer name or organization number (digits only) | |
| latitude | No | Latitude for radius search | |
| longitude | No | Longitude for radius search | |
| radius_km | No | Radius in km (requires latitude and longitude) | |
| occupation | No | Occupations. Names ("Systemutvecklare") or concept ids from the taxonomy | |
| municipality | No | Municipalities. Names ("Göteborg") or concept ids from the taxonomy | |
| open_for_all | No | true — "Öppen för alla" ads | |
| employment_type | No | Employment type. Names ("Vanlig anställning") or concept ids from the taxonomy | |
| published_after | No | ISO date or datetime (YYYY-MM-DD / YYYY-MM-DDTHH:MM:SS), or a number of minutes ago | |
| workplace_model | No | Workplace model. Names ("Distans") or concept ids from the taxonomy | |
| worktime_extent | No | Working hours. Names ("Heltid") or concept ids from the taxonomy | |
| occupation_field | No | Occupation fields. Names ("Data/IT") or concept ids from the taxonomy | |
| published_before | No | ISO date or datetime | |
| description_chars | No | How many characters of the description to include in the card (0 — no description) | |
| experience_not_required | No | true — only jobs with no experience requirement | |
| driving_license_required | No |
TDQS
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.
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.
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.
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.
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.
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 searchARead-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.
| Name | Required | Description | Default |
|---|---|---|---|
| type | Yes | Concept type | |
| limit | No | ||
| query | No | Name substring; without it the start of the list is returned |
TDQS
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.
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.
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.
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.
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.
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 autocompleteARead-onlyIdempotent
Free-text suggestions (skills, occupations, places, employers) from the start of a word — as in Platsbanken.
| Name | Required | Description | Default |
|---|---|---|---|
| q | Yes | Start of a word or phrase, e.g. "utv" | |
| limit | No |
TDQS
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.
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.
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.
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.
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.
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.
5 tool updates
v1.0.0- First observed
get_job - First observed
job_stats - First observed
search_jobs - First observed
search_taxonomy - First observed
suggest
TDQS
Scored across 5 tools
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.
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.
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.
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
Related MCP Connectors
Jobs, ads, SEO, keywords, YouTube and AliExpress data for AI agents. Runs on Apify.
LinkedIn jobs, companies and people for AI agents, plus messages and invites via your own account.
HR and recruiting: score a job description, or generate a structured role profile. No API key.
Real-time Meta, Google, LinkedIn and TikTok ad library access for AI assistants. No API keys.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables 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.9MIT
- AlicenseAqualityFmaintenanceQuery Swedish public data from AI tools. Includes company data, SCB statistics, weather, transport, public agencies, and more through the Apiverket API.229 npm2MIT
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to query Swedish Parliament open data through natural language.12 npmMIT
- FlicenseNot gradedqualityAmaintenanceEnables 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.-