MCP server for Swedish occupation classification and income statistics
The server defines 2 tools with HTTP transport via fastmcp. Both tools have descriptions and basic input schemas. Tool names follow verb_noun convention ('classify_occupation', 'get_income_statistics'). However, there are significant gaps: parameter descriptions are minimal (under 20 chars in most cases), output schemas are not documented in the code, error handling guidance is absent, and no tool annotations (readOnlyHint/destructiveHint) are present despite both tools being READ_ONLY. The SearchEngine.search() and SCBClient.get_income_statistics() implementations are not visible, making it impossible to verify actual return structure or error handling behavior. Input validation exists (title non-empty, description length capped at 4000 chars) but error messages are generic. Overall: solid naming and basic structure, but descriptions lack LLM-optimization detail and output contracts are undefined.
Classifies an occupation based on title and (optional) description. Returns a list of matching SSYK codes with titles and similarity scores.
Fetches income statistics (median, 25th/75th percentile) for a given SSYK code from Statistics Sweden (SCB).
Parameter descriptions are generic and under 20 characters. 'The job title (e.g., "Software Engineer")' provides context but 'The 4-digit SSYK code (e.g., "2512")' lacks explanation of what SSYK is or why it matters. LLMs cannot infer domain meaning from sparse descriptions.
Output schemas are not documented in the tool definitions. classify_occupation returns 'List[Dict[str, Any]]' with fields 'ssyk_code', 'title', 'score' inferred from implementation, but this is not declared in the tool schema. get_income_statistics returns 'Dict[str, Any]' from an external API with unknown structure. LLMs cannot plan downstream calls without knowing the response structure.
No error handling guidance in tool descriptions. Calls to search_engine.search() and scb_client.get_income_statistics() may fail (network, invalid code, rate limits) but there is no documented recovery path. Generic exceptions propagate without actionable next steps for the LLM.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 69 | 2025-06-18+ | v2 |
| 2026-03-09 | F | 48 | - | v1 |
Tool annotations (readOnlyHint, destructiveHint) are absent. Both tools are READ_ONLY but this is not declared in the tool metadata. Agents cannot distinguish read-only from destructive tools without explicit annotations.
Parameter 'description' in classify_occupation is optional but has no default value semantics documented. Does an empty or None description degrade search quality? Will it fail? LLMs need to understand the trade-off when omitting it.
No pagination or result limiting documented for classify_occupation. If search_engine.search() returns many matches, the response could grow unbounded. The description should state 'Returns top 10 matches' or similar and document the limit.