MCP server for jd-intel. Your AI assistant fetches and reads full job descriptions across seven ATS platforms, no copy-paste.
Three tools with strong naming (verb_noun pattern), comprehensive descriptions (200-400 chars each), and detailed parameter documentation. All tools have explicit Zod schemas with types and descriptions. fetch_jobs includes extensive usage guidance (USE WHEN/DON'T USE), error codes, and argument guides. However, output schemas are not formally documented in the tool definitions, responses are inferred from code (envelope.js wrapping). Error handling is present but recovery guidance is embedded in descriptions rather than structured error responses. No tool annotations (readOnlyHint, destructiveHint, idempotentHint) despite all three being read-only operations.
Detect which ATS platform (Greenhouse, Lever, Ashby, SmartRecruiters, Teamtailor, Recruitee) a company uses by probing. Workday is registry-only and never returned here; find Workday-hosted companies via search_registry or fetch_jobs (which auto-detects from the registry). USE WHEN: user asks about the ATS platform explicitly ("What ATS does Stripe use?") or for debugging. DON'T USE WHEN: you want to fetch jobs. Call fetch_jobs directly (it auto-detects internally). ARGUMENT GUIDE: company: company name or slug. Hyphens and spaces stripped automatically ("Cockroach Labs" → "cockroachlabs"). RESPONSE: { status, data: "greenhouse" | "lever" | "ashby" | "smartrecruiters" | "teamtailor" | "recruitee" | null, metadata }. data === null means none of the probeable ATS host this company (Workday is registry-only and never detected here). "partial" status means some probes failed. Result may be incomplete. ERROR CODES: - invalid_args: company arg missing - partial_failure: some probes failed
Fetch open job postings from a specific company's ATS (Greenhouse, Lever, Ashby, SmartRecruiters, Teamtailor, Recruitee, Workday). USE WHEN: the user asks about roles at a known company ("Is Stripe hiring?", "What's open at Figma?"). DON'T USE WHEN: - User doesn't know the company → read the registry Resource or call search_registry - User only asks which ATS a company uses → call detect_ats ARGUMENT GUIDE: company: lowercase slug, no spaces (e.g. "stripe", "cockroachlabs"). Hyphens and spaces auto-stripped. title_filter: JavaScript-compatible regex matched against TITLE ONLY. Case-insensitive by default. Do NOT use inline flags like (?i) (not supported by V8). Use for role identity ("product manager", "staff engineer"). Does NOT match description text. That's the distinction from filter. filter: JavaScript-compatible regex matched across title + department + description. Case-insensitive by default. Do NOT use inline flags like (?i). Use for topic/scope ("integrations", "growth"). AND'd with title_filter. posted_within_days: number. "recent" or "new" → 30. "this week" → 7. "today" → 1. location_includes: array of keywords. Case-insensitive substring match; short codes (US, UK) use word-boundary matching automatically. For US queries prefer ["United States", "US", "Remote - US"]. Avoid bare "Remote". It matches Remote-EMEA, Remote-LatAm. location_excludes: array. Drop jobs whose location contains any keyword. Use as refinement on top of includes. limit: default 100. Reduce for high-volume companies. workday: optional { tenant, env, site }. Use ONLY for a Workday company not in the registry when the user gives a careers URL. Derive from https://{tenant}.{env}.myworkdayjobs.com/{site}: tenant is the first label, env is the part like wd108, site is the path segment. Overrides the registry for that fetch. Never guess or fabricate these values; use only what the user supplied or what is literally in the careers URL. Omit this argument entirely if you do not have a real URL. RESPONSE: { status, data: [jobs], metadata: { attempted, succeeded, failed, notes } }. Check status first. "partial" means some adapters failed. Tell the user results may be incomplete. ERROR CODES: - company_not_found: slug not in registry, not detected - ats_unreachable: known ATS failed, or a supplied workday {tenant,env,site} was rejected by Workday - invalid_args: missing/malformed args, including an incomplete workday triple - rate_limited: upstream 429
Output schemas not formally documented. Response structure (status, data, metadata) is inferred from envelope.js code, not declared in tool definitions. LLMs cannot see what fields to expect without reading implementation.
No tool annotations despite all three tools being read-only. Adding readOnlyHint=true would signal to agents that these calls are safe to retry and have no side effects.
Error responses lack structured recovery guidance. Error codes (company_not_found, ats_unreachable, invalid_args, rate_limited) are defined but error response format is not documented. LLMs need to know: is this retryable? Should I ask the user?
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 68 | 2026-07-28+ | v2 |
Find companies in the indexed registry by name or sector. USE WHEN: targeted lookups ("Is Stripe in your index?", "Show me fintech companies"). DON'T USE WHEN: - User wants a broad survey of the catalog → read the registry://jd-intel/all Resource instead (one fetch vs repeated tool calls) - User asks about a specific company's jobs → call fetch_jobs directly ARGUMENT GUIDE: query: optional. Substring match (case-insensitive) against company name. sector: optional. Match against sector field. Examples: "fintech", "developer tools", "marketing tech". At least one argument required. Returns companies matching either. RESPONSE: { status, data: [{ slug, name, sector, ats }], metadata }. Each result includes the ATS platform for that company. ERROR CODES: - invalid_args: both query and sector missing
fetch_jobs accepts workday override but validation is defensive (checks for empty strings after trim). If user provides incomplete {tenant, env, site}, error message is helpful but parameter descriptions could be clearer about mutual requirement.
search_registry requires at least one of {query, sector} but this constraint is only documented in the description, not enforced by Zod schema. Schema allows both to be undefined, relying on runtime validation.