Skip to main content
The Alex MCP server is read-only and exposes 16 tools. All tools accept an optional companyId argument; if omitted, the call is scoped to the user’s primary company. See Multi-company access for when to pass it.

Discovery

listCompanies

List the Alex companies this token has access to. The LLM should call this first whenever a user has multiple companies, or whenever it needs to translate a company name into a companyId. Inputs
  • query (string, optional) — substring to filter company names. Only used by Alex platform admins (who can list across all companies).
Returns human-readable list of companies with their IDs. Example prompt: “Which Alex tenants do I have access to?”

searchCandidates

Natural-language candidate search across the talent pool. Backed by the same search infrastructure as Talent Match in app.alex.com. Inputs
  • query (string, required) — natural-language description: role, skills, location, experience, education, prior employers, work preferences.
  • limit (number, default 10) — max results.
  • radius (number, optional) — location radius in miles when a location is in the query.
  • recency (number, optional) — only return candidates updated within this many days.
  • status (string[], optional) + statusOperator ("includes" | "excludes") — filter by candidate status.
  • companyId (string, optional).
Example prompt: “Find React developers in Atlanta with 3+ years of experience, updated in the last 30 days.”

searchDocumentation

Searches Alex’s product documentation and returns a synthesized answer with citation links. Useful when users ask “how do I…?” rather than asking about their data. Inputs
  • query (string, required) — the user’s question, in natural language.
Example prompt: “How do I integrate with Bullhorn?”

Jobs and pipeline

getJobDetails

Fetch a single job’s configuration plus current pipeline counts and recent sessions. Inputs (any one of)
  • jobId (string) — internal Alex position UUID.
  • externalId (string) — ATS job ID.
  • jobName (string) — fuzzy match on role title.
  • sessionLimit (number, default 10) — recent sessions to include.
  • sessionStatus (string, optional) — filter sessions by status (e.g. "COMPLETED").
  • companyId (string, optional).
Example prompt: “Tell me about job 73605, including the interview link.”

getJobsSummary

Summary of all jobs (or one user’s jobs) with completion rates, average scores, and decision breakdowns. Inputs
  • userId (string, optional) — scope to one user’s created jobs.
  • userName (string, optional) — scope by recruiter name (resolved server-side).
  • sinceDate / untilDate (ISO date strings, optional) — filter by job creation date.
  • includeClosedJobs (boolean, default true).
  • status (string, optional) — filter by job status (e.g. "ACTIVE", "PAUSED").
  • limit (number, default 50, max 200) — max jobs returned per call.
  • offset (number, default 0) — pagination offset; combine with limit to page through large companies.
  • companyId (string, optional).
The aggregate counts (totalJobs, openJobs, closedJobs, activeJobs, pausedJobs, etc.) are always over the full filtered set regardless of limit/offset. The jobs array is the page, and hasMore indicates whether more pages exist. Each job in the jobs array now carries both isOpen (open vs closed for applications) and status ("ACTIVE", "PAUSED", etc.). ATS coverage — output also includes an atsCoverage block aggregated from ats_job_coverage_weekly and filtered by the same date window:
  • supportedtrue only when the company’s ATS has weekly coverage rows. Bullhorn-only today; non-Bullhorn customers get supported: false and null fields.
  • atsType — the ATS type for the rolled-up rows.
  • atsJobsCreated — total active jobs created in the customer’s ATS during the window.
  • alexJobsImported — how many of those landed in Alex.
  • alexJobsWithInterviews — Alex jobs that actually ran at least one interview.
  • coveragePercentalexJobsImported / atsJobsCreated.
  • interviewPercentalexJobsWithInterviews / atsJobsCreated.
  • weeksCovered — number of weekly rows the rollup spans.
Example prompts
  • “Give me a summary of Sarah’s jobs from this quarter.”
  • “Show me the next 50 active jobs.” (paginates with limit: 50, status: "ACTIVE")
  • “How many of Acme’s ATS jobs are running through Alex this year?”

getCandidatePipelineStats

Candidate counts grouped by interview status, decision, or as a funnel view. Inputs
  • metric ("byStatus" | "byDecision" | "funnel", required).
  • positionId (string, optional) — scope to one job.
  • sinceDate / untilDate (optional).
  • companyId (string, optional).
Example prompt: “Show me the candidate funnel for the Senior PM role.”

getPositionStats

Position-level metrics: open positions count, total positions, candidates per position. Inputs
  • metric ("openPositions" | "totalPositions" | "candidatesPerPosition", required).
  • sinceDate / untilDate (optional).
  • status (string, optional) — filter by position status (e.g. "ACTIVE").
  • companyId (string, optional).
Example prompt: “How many open positions do we have right now?”

Interviews

getInterviewGuideDetails

The interview guide (questions, criteria, intro/outro, learning objectives) for a position. Useful for asking “how should I improve my questions?” or “what’s in my guide?” Inputs — none required (uses the current job context if available, otherwise pass companyId and the LLM may need a position context first). Example prompt: “Are there gaps in our interview questions for backend engineers?”

getInterviewStats

Interview-volume metrics: completed interviews, completed candidates, average duration, breakdowns by interviewer, by status, or by medium type (video vs phone vs SMS), top candidates. Inputs
  • metric ("completedCandidates" | "completedInterviews" | "averageDuration" | "byInterviewer" | "byStatus" | "byMediumType" | "topCandidates", required).
  • positionId (string, optional) — auto-scopes to current job context if available.
  • companyWide (boolean, optional) — explicitly query across all jobs.
  • sinceDate / untilDate (optional).
  • limit (number, optional, max 100) — for topCandidates.
  • decision (("NONE" | "ACCEPTED" | "REJECTED" | "SUBMITTED" | "PLACED" | "HIRED")[], optional) — for topCandidates.
  • mediumType (("VIDEO" | "PHONE" | "EXTERNAL" | "BOTH" | "WHATSAPP" | "PHONE_SMS" | "SMS" | "NONE")[], optional) — filter any metric by interview medium. For a full breakdown, use metric: "byMediumType" instead.
  • companyId (string, optional).
Example prompts:
  • “What’s the top 10 highest-scoring candidates for the Atlanta Sales Lead role?”
  • “How many phone vs video interviews did we run last quarter?” (metric: "byMediumType")
  • “How many phone interviews has AlexAI completed this month?” (metric: "completedInterviews", mediumType: ["PHONE"])

getInterviewTimeOfDayBreakdown

Hour-of-day distribution of completed interviews plus a headline after-hours coverage % — answers “what share of interviews happen outside business hours?”. Inputs
  • sinceDate / untilDate (ISO date, optional) — defaults to the last 30 days.
  • timezone (IANA name, optional, default America/New_York) — buckets are computed in this zone; Postgres handles DST automatically.
  • businessHoursStart / businessHoursEnd (0–23 / 1–24, optional, defaults 9 and 18, end-exclusive).
  • positionId (string, optional) — auto-scopes to current job context if available.
  • companyWide (boolean, optional).
  • companyId (string, optional).
Returns 24 hour buckets plus inHoursCount / afterHoursCount / afterHoursPercent. Example prompt: “What share of Acme’s interviews happened after 6pm last quarter?”

getCandidateReport

Look up a candidate’s interview report by name, email, or position. Returns one or more candidate records with deep links into Alex, plus per-session ratings, reviews, decision, integrity flags, and invite-to-completion timing. Inputs (one of name or email required)
  • name (string, optional) — partial match, order-agnostic ("liu chao" matches "Chao Liu").
  • email (string, optional) — exact match.
  • jobName (string, optional) — narrow to a specific job title.
  • limit (number, default 5) — max sessions per candidate.
  • status (string, optional) — filter sessions by status.
  • companyId (string, optional).
Per-session output fields
  • decisionDecision enum (NONE | ACCEPTED | REJECTED | SUBMITTED | PLACED | HIRED).
  • recruiterRating, alexRatingSession.feedbackScore and Session.alex_feedback_score (numeric, nullable).
  • recruiterReview, alexReviewSession.feedback and Session.alex_feedback text (nullable).
  • notes — up to 10 most-recent recruiter notes attached to the report (report_note.content, ordered desc).
  • inviteToCompletionSeconds — time between invite (Session.dateCreated) and completion (Session.time_completed); null if the session isn’t completed.
  • fraudHighRisktrue when the candidate’s overall_verification_score is below 50 (mirrors getFraudVerificationStats); null when fraud_detection_enabled is off.
  • fraudVerificationScore — raw overall_verification_score (0–100); null when fraud detection is off.
  • cheatingFlaggedtrue when base_evaluation.cheating_score >= Company.cheat_threshold; null when gemini_cheat_detection_enabled is off.
  • cheatingScore — raw cheating_score; null when cheating detection is off.
Example prompts
  • “Show me Anthony’s interview reports.”
  • “Pull last quarter’s hires for Acme — include their ratings and reviews so I can highlight a few in the renewal recap.”

Integrity (cheating, fraud, identity)

Three distinct concepts in Alex, each with their own tool. Don’t confuse them: For a single candidate, getCandidateVerification returns all three at once.

getCheatingDetectionStats

Cheating-detection metrics — flagged candidates, flagged interviews, percentages, per-job rates, and individual flagged sessions with full sub-score breakdown (eye/head tracking, voice analysis, transcript analysis, multi-person detection). Inputs
  • metric ("flaggedCandidates" | "flaggedInterviews" | "flaggedPercentage" | "byJob" | "flaggedDetails", required).
  • positionId (string, optional).
  • companyWide (boolean, optional).
  • sortByJobMetric ("rate" | "count", optional, for byJob).
  • sinceDate / untilDate (optional).
  • companyId (string, optional).
Requires fraud_detection_enabled or gemini_cheat_detection_enabled on the company. Example prompt: “Which jobs have the highest cheating rate this month?”

getFraudVerificationStats

Identity-signal metrics from SEON — disposable emails, VOIP phones, VPN/proxy IPs, thin LinkedIn, digital-footprint anomalies. Aggregates over candidate_verification. Inputs
  • metric ("flaggedCandidates" | "riskBreakdown" | "flaggedDetails", required).
  • threshold (number, optional, default 50)overall_verification_score below this counts as flagged.
  • sinceDate / untilDate (optional).
  • limit (number, optional, max 50) — for flaggedDetails. Default 20.
  • companyId (string, optional).
Requires fraud_detection_enabled on the company. Example prompt: “How many candidates this month had VPN-flagged IPs or disposable emails?”

getIdentityVerificationStats

Stripe document + selfie verification metrics. Aggregates over identity_verification. Inputs
  • metric ("statusBreakdown" | "verificationRate" | "failureReasons" | "failedDetails", required).
  • sinceDate / untilDate (optional).
  • limit (number, optional, max 100) — for failedDetails. Default 20.
  • companyId (string, optional).
Requires enable_identity_verification on the relevant jobs. Example prompt: “What’s our ID-verification completion rate this month, and why are candidates failing?”

getCandidateVerification

The full integrity picture for ONE candidate (or session): cheating sub-scores, fraud signals, identity-verification status. The right tool for “is this candidate legit?” or “why was X flagged?” Inputs (any one of)
  • sessionId (string) — most precise.
  • candidateId (string) — returns the most recent session’s verification.
  • email (string) — exact match.
  • name (string) — partial / order-agnostic.
  • companyId (string, optional).
Returns nullable blocks for each integrity surface depending on what data exists for that candidate (e.g. cheating block is null if no base_evaluation row exists; identity block is null if Stripe ID verification wasn’t run for that job). Example prompt: “Show me everything we know about jane@acme.com’s identity.”

Account

getCompanyFeatures

Which Alex features are enabled for the current company (fraud detection, resume fit, talent match, coordinator, WhatsApp, custom logo, etc.). Useful for the LLM to know whether to suggest a feature. Inputs
  • featureName (string, optional) — check one specific feature; otherwise returns all.
  • companyId (string, optional).
Example prompt: “Is fraud detection enabled for our company?”

getAtsConnection

Inspect how the company’s ATS integration is configured: which ATS is set, whether credentials are populated (field-presence only — values are never returned), Kombo aggregator status, the auto job-import flag, and whether decision-stage sync mappings are configured. Use this for “is this customer connected to X?”, “what ATS does Y use?”, “why isn’t sync working?”, “is auto job import on?”. Inputs
  • companyId (string, optional).
Returns structured fields:
  • atsType + atsLabel (e.g. "BULLHORN" / "Bullhorn").
  • connected (boolean) — at least one expected credential field for the configured ATS is populated.
  • expectedCredentialFields / configuredCredentialFields / missingCredentialFields — credential field names only.
  • kombo{ integrationId, status, configured } for the Kombo aggregator (used for Avature, SAP SuccessFactors, SmartRecruiters, and as an alt path for some other ATSes).
  • autoJobImportEnabled (boolean).
  • syncFieldsConfigured (boolean) — at least one decision-stage sync field+value pair (hired/placed/rejected/shortlisted/submitted/no-decision) is set.
The tool is read-only and never returns API keys, tokens, passwords, or any other secret values. Example prompt: “Is Acme connected to Bullhorn, and is auto job import on?”

listTemplates

Lists templates the company has defined and reports adoption per type — ”% of active jobs using a template”. Covers two template surfaces: resume-fit (drives resume scoring) and interview-guide (structured guides). Each entry carries an adoptionMethod field describing how the number was computed, and the two differ in how much they can be trusted:
  • resumeFit is exact. The template id survives on the job (Interviewer.resume_fit_template_id), so both the adoption rate and the per-template job counts are real.
  • interviewGuide is a proxy. A guide template copies its content into the job and drops the template id, leaving only a source='template' mark on the copied questions and grading criteria — so adoption is counted as “the job’s guide has at least one template-marked question or grading criterion”. The bucket-level rate is meaningful; per-template jobCount is always 0 and is not usage. The mark is also copied when a guide is duplicated, so it can be present on a job that nobody created from a template. Treat it as company-level adoption, not as a per-job fact.
This previously reported two separate interview-guide numbers — one for grading criteria, one for question text. They have been merged into a single interviewGuide figure: the split implied a precision the underlying data doesn’t have, the two barely overlapped, and each half alone undercut real adoption. The merged number reads higher than the old criteria-only one; that is the same population counted correctly, not growth.
Inputs
  • templateType ("resumeFit" | "interviewGuide", optional) — drill into a single type; otherwise returns both.
  • topN (number, optional, default 10, max 50) — top templates per type to include.
  • includeUnused (boolean, optional, default false) — only meaningful for resumeFit; interviewGuide always lists templates alphabetically since per-template usage is unattributable.
  • companyId (string, optional).
Returns per type: totalTemplates, jobsUsingType, adoptionPercent, adoptionMethod, and a topTemplates array. Active job = Interviewer.status='ACTIVE' AND is_deleted=false (matches getJobsSummary and getPositionStats). Example prompts
  • “What % of Acme’s active jobs use a resume-fit template?”
  • “Which interview-guide templates does Acme have set up?”

Calling a tool directly

If you’re building a non-LLM client and want to call a tool over JSON-RPC:
The response is a JSON-RPC envelope; tool output is in result.content[0].text as a human-readable string. (Internally each tool returns structured data; the server formats it for LLM and human consumption.)