Maestro Career Studio
OfficialServer Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": false
} |
| prompts | {
"listChanged": false
} |
| resources | {
"subscribe": false,
"listChanged": false
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| list_base_resumesA | List base resumes (slug, display name, summary). By default only the ones that can be picked; include_archived=true adds the archived ones, each with its archived_at date (archiving hides a resume from scoring and the pickers without deleting it). |
| get_base_resumeB | Get a base resume's full data JSON, render status, and metadata. |
| list_jobsA | List stored jobs as a thin summary array, newest first, paged by limit (default 50, max 500) and offset; a page shorter than limit is the last. Each item is a slim projection (company, title, role_category, level, employment_type, work_mode, location, salary, work_authorization, opt_accepted, best_ats_score: the best stored base-resume score, null when unscored) of roughly 0.8k characters, with no raw_text or extracted_json; use get_job for full detail. Optionally only jobs without an application. Each job carries ownership (owned_here, owner, handover, pending_requests), identifying replicas and jobs being handed over. |
| get_jobB | Get a job with its most recent application. The nested job carries ownership (owned_here, owner, handover, pending_requests), identifying replicas and jobs being handed over. |
| get_applicationB | Get one application's full detail: the tailored resume (customized_json) and the joined job. Use compare_ats for before/after scores. |
| list_referralsA | List referral contacts (company, careers URL, contact name, notes, applications_count). |
| list_qa_entriesB | List an application's screening answers and cover letters, newest first: the ones the app's model generated and the ones a caller stored. |
| run_health_checkA | Run the JD-independent resume health check on a base resume or a tailored
application. |
| get_health_reportA | Fetch the most recent stored health report for a base resume or application
without re-running it. |
| judge_health_bulletsA | Stores evaluations of a resume's pending bullets while Maestro's own model does not
judge them (no model key is set, or the agent app writes), then rebuilds its health
report. |
| waive_health_gateA | Escape hatch for the fatal-health-gate 409 from create_tailoring_session.
Waiving bypasses a safety gate, so the stored waiver records an explicit user
decision: |
| unwaive_health_gateA | Remove a health-gate waiver and restore the gate's protection. Deletes the stored waiver for this gate (a no-op if none exists), so the gate blocks create_tailoring_session with a 409 again. The counterpart waive_health_gate records the user's decision to bypass a failing gate. |
| store_extracted_jdA | Store a job description the caller already extracted into JSON (the backend makes no LLM call). |
| get_setup_statusA | Where this user's Maestro setup stands, for an agent starting fresh. Returns ai_key (whether Maestro has its own AI key), writer (who writes for agent apps: "agent", the default, means the agent app writes, so the tools where Maestro's own AI writes the text refuse (409) and the agent writes it and saves it with the matching store or add tool; "maestro" means Maestro's AI writes it with the user's key, which needs ai_key), demo_bases (slugs of the shipped demo resume, never the user's own), next_step (the first setup step an agent can do that is not done, {step, tool_set, says}; null when none is left), tool_sets (each set's name, use_when and holds, in journey order) and steps (the full setup status: each step's done and detail, form-answer readiness, suggested bases, PDF engines). The AI key, the writer and the default resume design are set by the user in the app and are never a next step. |
| get_tailoring_optionsA | The user's Quick tailor presets, as data an agent can explain or work around. Returns presets ([{key, on, means}]: each switch, whether it is on, and its plain meaning), instruction (an extra instruction the user gave for every tailor, may be empty) and persona (the user's own words on tone, may be empty). create_tailoring_session with use_presets follows these presets; a custom tailor walks the gaps with the user however the agent talks with them and saves the decisions with resolve_gaps. Either way the server keeps its rules: an unproven skill enters only a skills list, resume lines stay in the owner's implied first person, and saved keywords land. The presets are the user's, set in the app; no tool changes them. |
| get_job_search_briefA | Read the server-composed job-search brief: profile constraints (location, relocation, work-auth VERBATIM plus warnings[] when values are contradictory or missing — never guessed), persona text (may be empty, in which case the analytics-derived targets are the stronger signal), active base-resume summaries, role mix, top required skills, build areas (per-skill tier plus category_label, the plain-words label for the raw category key), referral careers pages (company + careers_url + has_contact), and counts of jobs captured in the last 30 days by role category. profile.work_auth_by_country holds those yes/no answers for each market the user applies in, keyed by market ("UK", not "GB"); profile.work_auth is the home market's set, named by work_auth_market and kept for one release; unless the user lives and applies in one country, each warning names its country. last_runs maps each automation to when its newest run that did not fail finished (UTC ISO; a failed run does not count; absent when none has). roles is the role catalog as {key, label}. It is the entry point of the agentic job-search workflow (playbook: docs/agentic-job-search.md), which covers capture, scoring and proposals; applying is outside its scope. sync carries enabled and role (home, remote, or null when sync is off). applying.answer_level is the Screening answers level (facts, history, queued). |
| sync_nowA | Run one sync round on the always-on copy. Returns its outcome (ok, transient, needs_person) and per-step counts, or a skip reason. Forces a round through the backoff window; an attempt within the last 30 seconds is skipped with 'Synced moments ago.'. Sync off returns "Sync isn't set up."; home returns "This is your laptop's copy; your bot runs the sync." |
| get_career_contextA | Read the user's career record from the Career KB, as format="json" (the default) or format="markdown". |
| kb_list_entitiesA | List Career KB entities with their IDs (get_career_context returns prose with no IDs; this tool returns the IDs needed to edit). Optionally filter by kind (experience|project|education|certification|extra) or status (ongoing|completed|archived). Each item carries point_count, draft_count and approved_count (the bullets a resume can use; retired ones are in point_count only, except a bullet combined into another, which is counted nowhere) and used_in_bases, the base resumes it is on (archived included, deleted excluded). |
| kb_get_entityA | Full detail for one Career KB entity: dates, notes, its points (each with id, text and state), attached documents, and the activity timeline; also used_in_bases, the base resumes it is on (archived included, deleted excluded). |
| kb_list_pointsA | List Career KB points across all entities, optionally by state
(draft|approved|retired), oldest first. An unfiltered call returns the first
|
| kb_groom_entityA | Similar bullets on one Career KB entity, clustered, with a proposed keeper. Deterministic and zero LLM calls: the pinned local embedder scores every pair of the entity's active points (drafts included, retired and user_cannot_confirm excluded) and joins pairs at 0.80 cosine and above, minus pairs the user marked "Not the same" at /career. Proposes only; nothing is written. Each cluster carries score, keeper_id, members [{id, text, state, origin, provenance, usage_count, best_score}] and pairs. The keeper rule is approved over draft, then most recently updated, then longest text. compared_wording is false when the embedder could not run; clusters is then empty. Acting on a cluster is kb_combine_points (one claim in other words: the others become the keeper's wordings) or kb_retire_points (a similar but stale one). |
| kb_captureA | Capture free-text career news into DRAFT points. Maestro's AI matches it to an existing entity or proposes a new one, with the user's API key (with none set it returns an error); pass entity_id to force placement. Refused (409) when the user's writer setting (get_setup_status) says the agent app writes; kb_add_points stores bullets the caller already wrote, with no LLM call. Points land as drafts for the user to approve at /career. The text is stored as the user's own words and facts; the server does not verify it. |
| kb_add_pointsA | Add bullets the caller wrote to an existing Career KB entity as DRAFT points, with no LLM call (kb_capture with entity_id, minus the model that splits and places free text). Drafts appear on no resume and in no career export until approved with kb_approve_points (or at /career). The text is stored as the user's own words and facts; the server does not verify it. |
| kb_edit_pointA | Reword a Career KB point or retag it. Changing the text sends the point back to DRAFT, so it leaves the composed resume and career export until it is re-approved (kb_approve_points or /career). Cannot approve, retire or delete a point. New text gets the kb_add_points rules: en and em dashes become a hyphen or a comma, and a third-person line is a 400 naming it. |
| kb_create_entityA | Create a Career KB entity — kind is experience|project|education|
certification|extra. Writes immediately and is not draft-gated: the entity,
with the dates and wording supplied, is stored as the user's own career
record, and no duplicate is blocked (possible_duplicates hints are
returned). |
| kb_edit_entityA | Fix an entity's dates, title, org, notes or lifecycle status (ongoing|completed|archived). Omitted fields are left alone. Writes immediately with no draft gate; each supplied value replaces the stored one. Kind cannot be changed here — that is a rebuild, not a correction. |
| kb_edit_profileA | Update the Career KB profile: contact block, career summary, skill groups, notes. Omitted fields are left alone; a supplied field REPLACES its stored value wholesale (current values are in get_career_context), so changing one list item means sending the full merged field. Writes immediately with no draft gate. |
| kb_ingest_resumeA | Persist one caller-parsed resume into the Career KB. No in-house LLM. |
| kb_approve_pointsA | Batch-approve Career KB points. Approved points are the only ones that appear on composed resumes and the career export; this is the single approval path available over MCP, and kb_retire_points retires points. The approval written is the user's decision about the listed points; the server does not check that the user reviewed them (a convention, not enforcement). |
| kb_combine_pointsA | Fold Career KB points into one: the keeper stays the bullet, and every point in point_ids (the others, not the keeper) is retired and recorded as one of the keeper's wordings. Resumes that use any of them keep their text; combining never edits a resume. Reversible from /career (Make its own bullet). The grouping written is the user's decision about the listed points; the server does not check that the user reviewed them (a convention, not enforcement, the same gate as kb_approve_points). Same entity required (400); a retired or user_cannot_confirm keeper, a user_cannot_confirm loser, and a loser the user stopped (retired but not folded) are refused (400), and so is a keeper that is itself folded into another; a loser already folded elsewhere moves to this keeper. Returns {keeper, repointed}: the keeper with its wordings, and any combined claims the fold moved from a loser onto it. 1-50 ids. |
| kb_merge_entitiesA | Fold one Career KB item into another of the same kind, the cure for a duplicate an import split off: merge_id goes, keep_id stays. Everything on merge_id moves to keep_id: its bullets in every state (approved, drafts, retired, and the wordings folded into a bullet), its evidence documents, and its record of which resumes its bullets were copied into. |
| kb_retire_pointsA | Batch-retire Career KB points: they stop being offered to resumes and the career export; resumes that already use them keep their text. Reversible from /career (Use again). The state written is the user's decision about the listed points; the server does not check that the user reviewed them (a convention, not enforcement, the same gate as kb_approve_points). |
| kb_sync_baseA | Sync one base resume into the Career KB. |
| create_base_resume_from_kbA | Compose a new base resume from selected Career KB entities. LLM-free. |
| get_autofill_profileA | Read the user's application-form autofill data: |
| set_autofill_profileA | Save the user's application-form answers: the profile get_autofill_profile reads and the Companion fills forms from. Only the sections passed change. Inside personal, eligibility, preferences and each country's work-permit answers only the keys passed change, and a null removes one; education and languages replace the stored list whole (get_autofill_profile returns them). Each custom answer replaces the saved one with the same question (case and spacing ignored) and the same only_for and only_for_kind, or is added: for an answer held for one company or city, send its only_for and only_for_kind as get_autofill_profile shows them, or a second scoped answer is added. A saved answer is never removed here, so one the Companion learned meanwhile stays (removing one is the user's, in Profile). A work-permit answer is per country and a form question without one is asked on that form, never defaulted; setup counts the answers of each country in apply_in (set_markets). Diversity (EEO) answers have no field here: they and their consent are the user's, in Profile. An unknown section, key or market key writes nothing. Returns {profile, setup}: the saved profile without eeo, and setup status's form-answer readiness (readiness 0 to 1, groups, blocking, work_auth_by_country counts), null when that read fails. |
| set_job_preferencesA | Save the jobs the user wants, which get_job_search_brief reads and setup counts as done once any is set. Only the fields passed change; [] clears a list and "" clears notes. favored_roles holds at most 25 roles, duplicates dropped. A role the catalog does not know (the refusal names the closest), a currency that is not ISO 4217 or more than 25 roles is a 422 that saves nothing. Returns the stored preferences, with role_categories derived from favored_roles. |
| set_marketsA | Save where the user lives and applies. home (a market key, "UK" not "GB") sets the date format and, while home_currency is unset, the currency pay is shown in. apply_in is every market the user applies in, each asking its own work-permit answers; [] reads as home alone, and a market dropped from it keeps its stored answers. home alone leaves apply_in as stored. home_currency is ISO 4217; "" follows home again. Only the fields passed change, all in one save: a refused field (an unknown currency) writes none of them. No field passed reads the current values. Returns {home, apply_in, home_currency (the resolved one), setup}: setup is setup status's form-answer readiness, null when that read fails. |
| set_personaA | Replace the user's persona: their direction, strengths and working style in their own words. Every writing prompt (tailoring, application answers, cover letters) reads it for tone and emphasis, never as a source of facts. The whole text is replaced; "" clears it, and setup counts the step done while it is not empty. The app's own persona draft needs Maestro's AI key and has no MCP tool. Returns {key, value}. |
| find_job_by_urlA | Answer "is this posting already captured?", so an extraction can be skipped: posting-equality source_url lookup returning {found, job, application_exists, application_id}. Matching ignores query strings and fragments (?utm_*, ?gh_src=, #apply) and treats the posting's own sub-paths (/apply, /application) as the same posting, so tracking parameters need not be stripped from the input. A search-results or employer-index URL still matches nothing. found=False means the posting is new; store_extracted_jd still dedupes authoritatively via already_existed. |
| update_base_resumeA | Replace a base resume's data (the full ResumeData object, required). Optionally also update display_name. PUT replaces the whole resume body. |
| set_base_resume_identityA | Set what a base resume is written for. Only the fields passed change.
|
| edit_base_resumeA | Apply typed edit ops to a base resume. replace_entry changes a field on one
entry (dates, title, company, project name, link, tech) given that entry's
index and the full edited entry; update_base_resume instead replaces the
whole resume body (PUT). ExtraSection shape is on Indices are 0-based into the FULL array (incl. enabled:false), as returned by
get_base_resume, never PDF order. Response
|
| create_base_resumeC | Create a new base resume. Slug must be lowercase alphanumeric/underscores, not 'master'. |
| duplicate_base_resumeC | Clone an existing base resume into a new slug. |
| list_resume_versionsA | List append-only resume snapshots for one target, newest last. |
| get_resume_versionA | Fetch one resume version including its snapshot and the diff vs its parent. |
| restore_resume_versionA | Copy a past version's snapshot into the live resume. |
| archive_base_resumeA | Hide a base resume from pickers without deleting it. |
| unarchive_base_resumeA | Restore an archived base resume to list_base_resumes' default view. |
| edit_applicationA | INCREMENTAL FIX: apply typed edit ops on top of an existing application's CURRENT tailored resume (customized_json), preserving every prior edit. |
| update_applicationA | Update an application's tracking fields (status, applied_at, notes, referral_id). |
| save_referralA | Add a referral contact, or edit one when referral_id is given (list_referrals has the ids). A referral is a company the user can apply to with someone's referral: its careers page, the contact's name and notes. A new referral needs company and careers_url (an http or https address); contact_name and notes are optional, and each call without referral_id adds another referral. An edit changes only the fields passed, and company is optional there: a field left out keeps its saved value, and a field passed replaces it (referrals keep no history of earlier values). An unknown referral_id is a 404 and nothing is written. Returns the saved referral {id, company, careers_url, contact_name, notes, applications_count, created_at, updated_at}. |
| generate_qa_answersA | Maestro's AI writes a batch of screening-question answers for an
application, with the user's API key (with none set it returns an error).
|
| store_qa_answersA | Store screening-question answers the caller wrote for an application, with no LLM call (generate_qa_answers has the app's configured model write them). One entry per question, shown in the job's Apply step history and in list_qa_entries. A question the application already holds (the same words, case and spacing ignored) has its answer replaced and the others are added, so a repeated call changes nothing. Answers are stored as plain form text, as generated ones are: markdown bold, headings, bullet markers and backticks are removed, and en and em dashes become a hyphen in a range ("2020-2023") and a comma elsewhere, line breaks kept. The text is stored as the user's own words; the server does not verify it. An answer that is empty once its formatting is removed is a 400 that stores nothing. Returns the stored entries [{id, kind, prompt, answer, model_used, created_at}]; model_used names the agent. |
| generate_cover_letterA | Maestro's AI writes a cover letter for an application in the requested tone, with the user's API key (with none set it returns an error). This replaces the application's prior generated cover-letter entry. Refused (409) when the user's writer setting (get_setup_status) says the agent app writes; store_cover_letter stores a letter the caller wrote, with no LLM call. |
| store_cover_letterA | Store a cover letter the caller wrote for an application and render its PDF, with no LLM call (generate_cover_letter has the app's configured model write it). It replaces the application's cover letter and that letter's PDF (an application holds one); the new one shows on the job's Apply step and in list_qa_entries. The text is stored as the user's own letter; the server does not verify its facts. As with a generated letter, en and em dashes become a hyphen in a range ("2020-2023") and a comma elsewhere, and one opening a line ("— Jane") is dropped; the rest is stored as written, line breaks and paragraphs included, up to 20000 characters. The PDF puts the base resume's contact block above the letter, in the engine of the application's resume template. |
| render_pdfA | Render a PDF. target_type is 'base_resume' (target_id=slug) or 'application'
(target_id=application id). Optional template_id selects a template (either
engine; must be 'ready'; omit for the default). Response reports
resolved_template_id / resolved_engine (the template actually used);
template_fallback=true only when an explicit template_id was passed and the
resolved id differs — a substitution of the persisted choice is reported
by render_note, never by the flag. render_note is non-null only when the
backend has no TeX and a LaTeX template was rendered through a Typst
template instead; it names both. Returns the render result incl.
pdf_path (an application render also has copy_path: where the user finds the PDF
in their resumes folder, null when off), plus a |
| get_rendered_pdfA | Save a rendered PDF locally and render one PNG per page without inlining images. target_type 'base_resume'(slug)/'application'(id) returns the last rendered resume PDF; 'template'(id) returns the sample preview PDF. Returns {filename, path, size_bytes, mime_type, page_count, page_images, em_dash_found, em_dash_pages, artifact_dir?}. Requires an existing render (render_pdf, or validate_template for a template preview). Paths are local to where the backend/MCP server run; browser uploads use the copy staged by prepare_application_pdf_upload. The PDF and per-page PNGs ({id}.pN.png, ~120 dpi) are written under $MAESTRO_CS_PDF_DIR (or a temp dir). This slim response never includes image base64; get_rendered_pdf_page_image returns the visual bytes of exactly one page. em_dash_found reports whether the PDF contains an em dash (U+2014), which rendered resumes in this product are required not to contain; em_dash_pages lists the page numbers. (page_count is None / page_images empty if the PDF can't be read.) |
| get_rendered_pdf_page_imageA | Return one rendered PDF page as an explicit opt-in visual payload. page_number is 1-based. max_dimension_px (default 1024) is the longest side of the re-rendered PNG; larger values grow the payload, and encoded payloads over ~1MB are rejected (a smaller max_dimension_px fits). Returns target_type, target_id, page_number, page_count, filename/path/ size/mime metadata, and page_image_b64 for only the requested PNG. Out-of-range page numbers are rejected. Does not change get_rendered_pdf's pre-rendered preview PNGs. |
| prepare_application_pdf_uploadA | Stage a disposable copy of an application's canonical rendered PDF for direct Playwright upload: its tailored resume by default, or its cover letter (document). If the PDF has not been rendered, render it and retry once. Writes atomically under $MAESTRO_CS_UPLOAD_DIR/<application_id>/, defaulting to the repository's .playwright-mcp/uploads directory (pair Playwright --output-dir with the parent .playwright-mcp tree). Returns upload_path, an absolute path usable directly by Playwright's file chooser. The staged copy is the only location intended for browser file uploads; the canonical server artifact (under applications/artifact_dir) is preserved and is not an upload source. Also returns filename/path integrity and PDF lint metadata. This preparation tool does not authorize or submit an application. |
| list_templatesA | List resume templates — both engines (id, display_name, status['draft'|'ready'], engine, is_default, last_error). parse_certified false = a 'ready' template that still loses word boundaries under strict extraction — see validate_template. engine_available=false marks a LaTeX template on a backend with no TeX: still pickable, renders through Typst with a render_note. |
| get_templateA | Get a template incl. its full source (Jinja2+LaTeX for engine='latex';
raw .typ text for engine='typst') plus engine, engine_available (see
list_templates), and supported_fmt_keys.
A template of either engine opts into look knobs by referencing |
| create_template_draftA | Create a DRAFT template. |
| update_template_draftA | Edit a draft template; changing the source resets it to 'draft'. Pass validate=True to save + test-compile in one call (returned status/last_error show the result); otherwise re-validate separately. The active default template cannot be edited through these tools (the backend rejects it). |
| validate_templateA | Test-compile a template against a built-in sample resume. On success it becomes 'ready' (usable via render_pdf template_id) and a preview is available via get_rendered_pdf('template', id). On failure returns {ok: false, error} with the engine's compile error (Typst errors carry file:line:col). Returns {ok, error, parse_certified} — parse_certified is true when the template's compiled PDF round-trips through a strict text extractor with word boundaries intact, false when it drops text, null when not assessed. Also returns parse_report: {missing, headers_missing, extra_sections_supported, extra_sections_missing} — missing lists the exact sample phrases the PDF dropped; extra_sections_missing non-empty means the template will hard-fail rendering any resume that has custom sections. |
| explore_trendsA | The saved jobs and their match scores as one chart's rows, one view per call: top_skills,
heatmap, role_mix, fit_distribution, ats_over_time or tailoring_lift (what each returns is on
|
| explore_gap_frequencyA | Skills that recur as gaps across saved JDs (best-scoring base resume vs JD,
before tailoring), ranked by how many jobs want them and their potential match score
gain. This is RECURRING DEMAND, not a "you lack this" or learn-next list:
a skill can recur because the evidence lives on a different base resume, is
stale, or is merely mis-placed on this one. The learn-vs-move
distinction is the per-skill |
| list_applicationsA | List your applications as a thin paginated summary array. Filter by
status/role_category; page with limit/offset. Rows name their base resume
( |
| score_atsA | Deterministic hybrid match score (no LLM): deterministic lexical layers + anchored pinned-model semantic matching + section-level semantic_fit. Omit target to score ALL base resumes (fast). |
| compare_atsA | Before/after match score comparison for an application: composite and per-layer deltas plus a per-skill diff (absent→matched, skills-list-only→dual, decayed→recent). Computes any missing phase row on demand. |
| create_tailoring_sessionA | Start the gap-analysis tailoring workflow for a job + base resume. |
| list_tailoring_sessionsA | List all tailoring sessions for a job, newest first. |
| get_tailoring_sessionA | Fetch a tailoring session to resume a half-done gap walkthrough. |
| close_tailoring_sessionA | Abandon an OPEN tailoring session without tailoring it. |
| resolve_gapsA | Save gap resolutions on a tailoring session (merge by gap_id, idempotent). The batch is validated as ONE unit — one invalid item saves NOTHING (existing saved resolutions untouched). |
| tailor_sessionA | Run the tailor pipeline for a session: saved resolutions -> edit ops -> the job's tailored application, scored and rendered. |
| export_jobsA | Export jobs, newest first, with extracted fields and nested skills, for open-ended analysis. Paged by limit (default 10, max 50) and offset; a page shorter than limit is the last. Rows run a few thousand characters (the skills list is most of it) and omit raw_text and extracted_json, which get_job returns for one posting. since is an ISO date (YYYY-MM-DD); skill is a case-insensitive substring. |
| propose_applicationA | File an agent-hunted application proposal for user review. |
| list_proposalsA | List application proposals, newest first, as {items, total}. |
| get_proposalB | Get proposal detail including job facts, application summary, QA entries, and evidence manifest. Reading expires an overdue pending_review or needs_decision proposal (terminal 'expired'). |
| record_decisionA | Record user decision when proposal is in needs_decision state. Resolves back to pending_review. application_id may be omitted; the backend then links the job's application on fit.chosen_base when there is one, else the job's one application (409 only when the job has no application); an automatic link sets fit.chosen_base to the linked application's resume. |
| request_decisionB | Escalate an unlinked/ambiguous proposal to needs_decision. Idempotent if already needs_decision. |
| resume_proposalA | Return a needs_human proposal to pending_review so preparation can continue. Releases any pre-click daily-cap reservation. Refused for submission_uncertain. |
| get_final_reviewA | Compact final-review bundle: job summary, chosen base/match score delta, PDF readiness,
QA answers, blocked/manual items, evidence manifest. |
| record_filled_answersA | Record what was filled into this job's application form, one call per form page: each
field's question, answer and source. Per question the latest call wins; the job page's
What was submitted tab shows the record. An EEO answer is kept only while the user's
standing EEO consent is recorded, and no MCP read returns its value. Returns {id,
application_id, flag_count, flags}: each flagged field by its index in |
| record_runA | Stores one finished automation run. The Automations page lists the newest run of each automation under Recent runs (its counts, digest and job links) and each card shows when its automation last ran; the Agent inbox says so when the run proposed jobs. automation is the card id (mail-status, job-hunt, referral-pages, tailor-run, apply-session) or a custom automation's own name, 40 characters at most. outcome: ok, partial (some of the work failed) or failed (none of it was done). The newest 200 runs are kept. Returns the stored run. |
| record_consentA | Record the user's explicit approve/reject decision for this proposal as an
append-only audit row ( |
| get_job_site_loginA | The email and password the user saved for job-site accounts, for creating an account or signing in on this proposal's application site. Available only while full automation is on and only for a queued or approved proposal; each call is recorded (never the value). The value passes through the agent's AI provider, which is why it is a password the user keeps for job sites alone. Returns {email, password}. |
| attach_evidenceA | Attach an evidence image to a proposal, from exactly one of image_base64 (the image, base64, supplied inline in the call) or file_path (an image file already on this machine's disk, e.g. a browser tool's saved screenshot; its bytes are read and uploaded server-side, so they never enter the model context). Both or neither is refused, and nothing is uploaded. kind must be step | final_review | submission_receipt. |
| mark_submittedA | Flip an approved proposal to submitted (terminal; links the application to
applied). Requires submission_receipt evidence, or |
| record_triageA | Record the user's TRIAGE decision over one or many proposals — accept queues them for the next apply run; decline drops those unique postings (posting-scoped, never the company). The decision is the user's own, stated directly or as a selection rule they gave. One append-only consent row is written per proposal and the result is a per-id ok/error report (a mixed batch partially succeeds). Accept is NOT submit consent: the per-application approval is recorded separately by record_consent. |
| report_failureA | Report execution failure. From pending_review/accepted/approved, transitions to needs_human (resumable). reason='submission_uncertain' (for a submit click that could not be verified) moves the proposal to the terminal submission_uncertain status: resume_proposal is refused for it. The user's attestation, or the agent's (channel auto) in full automation mode, through mark_submitted moves it on. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 94 tools
The set contains several overlapping clusters: base-resume editing (edit_base_resume/update_base_resume/restore_resume_version), application editing (edit_application/tailor_session ops), proposal state transitions (record_triage/record_decision/request_decision/record_consent/resume_proposal), and KB ingestion (kb_capture/kb_add_points/kb_ingest_resume/kb_sync_base). Individual descriptions are detailed, but at 94 tools an agent must reason through many subtle boundaries and may misselect.
Nearly all tools use snake_case verb_noun phrasing, often with clear domain prefixes such as kb_ and get_/set_/list_/record_. Minor deviations exist around the kb_ namespace and some state-machine verbs, but the conventions remain predictable.
94 tools is an extreme count for any MCP server, even one covering a broad career workflow. The surface should be consolidated into fewer higher-level tools or split into separate servers.
The surface covers a wide lifecycle: jobs, applications, proposals, tailoring, KB, templates, rendering, health checks, referrals, autofill, and export. There are minor gaps such as explicit deletion/update paths for jobs and referrals, but core workflows are well represented.