A keyless-by-default Job Search MCP server + web app, deployable on Vercel. Ranks jobs, generates cover letters, and answers application questions.
Four tools with complete input schemas and descriptions. Naming follows verb_noun pattern (profile_upsert, jobs_search, letter_generate, qa_reply). Descriptions are substantive (100-250 chars), explaining WHAT and WHEN. All parameters have types and descriptions. Output structure documented via ok() helper returning both text and structuredContent. Key gaps: no error handling guidance (e.g., what to do if live search fails), no output schema formally documented, no parameter constraints beyond Zod validation (e.g., limit 1-50 is enforced but not visible in description), and no idempotency hints for stateless operations.
Rank jobs against a query and profile. Returns jobs with a fit_score (0-100), match_reasons and a source label. Set live=true to pull keyless live listings from five sources (Remotive, The Muse, Arbeitnow, RemoteOK, Jobicy), filtered by the profile's role/location; live results have their links reachability-checked before being returned. Set validateLinks=false to skip that check.
Generate a cover letter for a job in a chosen tone. Uses a template in demo mode, or a real LLM when an API key is configured.
Validate and normalize a candidate profile. Stateless: returns the normalized profile to pass back into later calls.
Answer an interview or application question in the candidate's voice. Heuristic in demo mode, real LLM when a key is configured.
No error recovery guidance in tool descriptions. If live=true search fails or link validation times out, LLM has no hint to retry with live=false or validateLinks=false.
Output schema not formally documented. The ok() helper returns {content, structuredContent}, but LLMs cannot see this structure in tool definitions, only inferred from description text.
Parameter constraints (limit 1-50, enum tones) enforced in Zod but not visible in descriptions. LLMs cannot read JSON Schema, they rely on description text to understand constraints.
No idempotency or statelessness hints. profile_upsert description says 'Stateless: returns the normalized profile to pass back into later calls', good, but other tools lack this clarity.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | A | 82 | 2026-07-28+ | v2 |
No pagination or result-limiting guidance. jobs_search accepts limit (1-50) but description does not state default or explain what happens if results exceed limit.