Career Agent Workbench MCP Server
This server provides MCP tools for searching, retrieving, and locally matching public LinkedIn job postings, supporting job research and application tracking.
search_linkedin_jobs – Search guest-accessible public LinkedIn jobs using keywords and location, with filters for date posted, job type, workplace type, experience level, distance, sort order, pagination, and exclusion by job IDs.
get_linkedin_job_details – Retrieve normalized, structured details for a single public LinkedIn job by job ID or supported public URL.
get_linkedin_job_raw_payload – Fetch the raw public LinkedIn detail payload for a job (by ID or URL) for debugging the data ingestion layer.
find_matching_linkedin_jobs – Run a local matching workflow against the operator’s master resume and private workspace, with controls for location, date posted, query limits, and result limits. Uses a configured query-planner model to find aligned jobs.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Career Agent Workbench MCP ServerFind me remote Python developer jobs posted this week."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Career Agent Workbench
Career Agent Workbench is a local, human-governed portfolio application for researching public job postings, maintaining application state, and producing reviewable resume variants. It combines a provider-neutral Python core, SQLite-backed workflow state, deterministic rendering and ATS diagnostics, a Flask tracker, command-line and Make workflows, and a proportional FastMCP server.
The public repository contains reusable software, synthetic tests, fictional examples, and documentation. A separate operator-owned workspace contains real profile evidence, tracker data, generated documents, credentials, and model configuration. The application does not authenticate to LinkedIn, access private-member data, submit applications, contact employers, or make a hiring decision. Any model-produced resume or cover-letter content remains a draft until a person reviews it.
Version 2.1.0 expands the public documentation around one coherent fictional case: Tessa Rowan is a platform and reliability engineer considering the Senior Platform Automation Engineer opening at Rivermark Platform Services. The tracked posting is intentionally a credible but imperfect match. It overlaps with Tessa's Python, Ansible, AWX, cloud, OpenSearch, Grafana, Prometheus, testing, and operational-support evidence while naming preferred gaps such as Argo CD, Backstage, Crossplane, and OpenTelemetry. All people, organizations, locations, dates, credentials, and opportunities in that case are fictional, and every public URL uses a reserved domain.
Safety posture
The workbench is deliberately conservative about authority and data placement:
Real resume text, the Master Resume Object, job rows, application artifacts, credentials, logs, and model settings belong outside the public checkout.
Public-job access is limited to guest-accessible pages and bounded generic public URLs. Authenticated sessions and private profile data are out of scope.
Generated changes are evidence-grounded, stored as variants, and marked for human review. Creating a variant does not approve or submit it.
Database writes use the application-state layer, transactional operations, revision checks, and explicit selection rules. Filesystem exports are restricted to the configured workspace.
Errors crossing provider, model, process, parser, or filesystem boundaries are bounded and content-hidden. Tests use injected responses and synthetic temporary workspaces.
No workflow sends a resume, cover letter, message, or application to a third party. The final application decision and every external action remain with the operator.
That boundary matters more than feature count: this is a review workbench, not an autonomous application agent.
Related MCP server: witness-mcp
Key terms and stored objects
The repository uses a small vocabulary consistently across Python, SQLite, Make, the Flask tracker, and the examples.
Master resume text is the concise, human-maintained factual source. It is the readable evidence record from which structured resume claims are reconciled.
MRO — Master Resume Object is
MASTER-RESUME.yml, the structured and reusable representation of that source. It contains the header, summary, skills taxonomy, experience, education, certifications, portfolio, source evidence, and cross-links used by later workflows.Job description is the full stored public posting. The source text is retained separately from the prompt-ready form.
JOD — Job Opening Description is the normalized, bounded job context used by matching and resume workflows. In an ARO it also contains ordered requirement targets.
ARO — Application Resume Object is a job-specific resume mapping derived from the MRO. It can be rendered to HTML and PDF and stored as an isolated variant.
CLO — Cover Letter Object is a separately stored, human-owned cover-letter mapping. It has its own editing and rendering path; it is not implied by an ARO.
ATS diagnostics are deterministic local signals for a rendered resume and JOD pair: parsing, keyword, semantic, and formatting components plus bounded matched and missing terms. They are diagnostics, not a prediction of employer behavior.
Variant is one of
v1,v2, ormanual. Each variant retains its own ARO, rendered artifacts, diagnostics, lineage, validation, evidence packet, and model metadata where applicable.Selection identifies which stored variant drives the application-level resume projection. Automatic selection prefers
manual, thenv2, thenv1; an explicit human selection remains pinned until it is changed or reset.
The distinction between source, object, rendered artifact, and selected state prevents a generated file from silently becoming the authoritative resume.
Architecture and workspace boundary
The package is Python-first. Domain modules accept explicit paths, services, model runners, transports, or stores, while CLI and Make layers compose those capabilities after parsing. The same application-state contract supports the command line, archived Flask interface, and MCP matching tool.
public checkout external operator workspace
------------------------------- -------------------------------
src/career_agent_workbench/ ---> profile/MP-MASTER-RESUME.txt
scripts/ and Makefile profile/MASTER-RESUME.yml
packaged templates and skills output/tracking/applications.sqlite3
fictional examples and tests output/ and downloads/
README and release metadata tmp/, .blacklist, and private .envThe ignored root .env is bootstrap-only. It selects the external workspace
and the private dotenv; it should not hold tokens or personal settings:
CAREER_AGENT_WORKBENCH_WORKSPACE=../career-agent-workbench-ops
CAREER_AGENT_WORKBENCH_PRIVATE_ENV_FILE=../career-agent-workbench-ops/.envThe private dotenv is required to have user-only mode 0600 on platforms that
support POSIX permissions. Conventional members resolve beneath the workspace,
and individual members may be overridden there. Explicit CLI values have the
highest precedence, followed by process values, the private dotenv, and public
defaults. Configuration loading returns immutable settings and paths without
mutating the process environment. Invalid, escaping, or unsafe paths fail with
sanitized errors.
See .env.example for the complete public configuration
surface. Do not copy real values into documentation, issue reports, fixtures,
or committed test output.
The existing MRO and application-workflow diagrams are tracked explanatory assets. They remain unchanged during the 2.1.0 text-parity phase and will be reviewed separately for final visual parity.
Installation and first setup
Use the repository-root Make targets. The supported public environment is the
ignored .venv; a legacy environment is not part of the operator contract.
make install
make console-scripts
cp .env.example .env
make skill-link
make test
make lintmake install creates .venv, installs the editable .[dev,browser] package,
and installs Playwright Chromium. It does not install Ollama, configure an API
key, call a model, search for jobs, or initialize a private workspace.
make console-scripts verifies the six packaged executables. make skill-link
idempotently links exactly the five repository skills—Career Agent Workbench,
master-resume YAML, manual resume passthrough, workflow initialization, and
workflow control—into the configured Codex skills directory. It refuses to
overwrite a non-symlink.
After copying .env.example, edit only the ignored .env selectors and the
external private dotenv. A conventional private workspace looks like this:
career-agent-workbench-ops/
├── .env # mode 0600; credentials and runtime settings
├── .blacklist
├── profile/
│ ├── MP-MASTER-RESUME.txt
│ └── MASTER-RESUME.yml
├── output/
│ └── tracking/
│ └── applications.sqlite3
├── tmp/
└── downloads/The application creates or updates operational state only when a selected
command needs it. Help and version commands load no workspace. Use
career-agent-workbench --help and the specific executable's --help before
running a stateful workflow.
The fictional offline demo
The tracked demo workspace source is the
safe starting point for exploration. Its full-depth resume has 14 technical
skill categories, seven ordered roles, 60 experience items, education, three
certifications, and a portfolio. Its target is the full-size, nine-section
Rivermark posting under the stable job ID demo-platform-001.
The public identity is internally consistent: tessa.rowan@example.test and
https://tessa-rowan.example.test belong to the fictional candidate, while
https://jobs.example.test/rivermark-platform-automation-engineer is the
fictional posting. The reserved .test endpoints are documentation values, not
live services.
Materialize the source into ignored disposable state:
make demoTo choose a different disposable location, pass it explicitly:
make demo DEMO_WORKSPACE=tmp/my-demo-workspaceThe materializer copies the public text and MRO, initializes SQLite, and
creates exactly one application row, one automatically selected v1 ARO, one
human-review-gated CLO, and three readable examples:
tmp/demo-workspace/
├── profile/MP-MASTER-RESUME.txt
├── profile/MASTER-RESUME.yml
└── output/
├── tracking/applications.sqlite3
└── demo-examples/
├── application-resume-v1.yml
├── cover-letter.json
└── resume-v1.htmlRerunning make demo against the same explicit workspace is idempotent. The
demo factory performs no network, provider, model, browser, or child-process
work. It does not produce v2 or manual variants, does not invent ATS scores,
and does not create a PDF. The readable files demonstrate object shape and
rendering; they are not application-ready artifacts.
Building and refining MASTER-RESUME.yml
Treat the human resume text as the factual authority and the MRO as its
structured projection. The repository's master-resume-yaml skill describes
the guided reconciliation process, but the trust rules apply whether the YAML
is edited manually or with an assistant.
Maintain the source text in
profile/MP-MASTER-RESUME.txt. Keep it concise enough to review but complete enough to support every later claim.Reconcile the header, professional summary, skill taxonomy, experience, education, certifications, and portfolio into
profile/MASTER-RESUME.yml.Separate primary and additional skills without duplication. Preserve useful compound phrases and list neutral match aliases only when they resolve to a declared display skill.
Give each experience item stable order and source evidence. Link it only to categories and skills that exist in the public taxonomy. A link explains evidence ownership; it does not assert a job match.
Keep job-specific fields neutral in the master object: empty
jod_matched_items, empty matched-category lists, and zerojod_match_countandbullet_point_total_match_countvalues.Render and inspect the result, reconcile it back to the source text, and run focused structural tests before using it for matching or tailoring.
The public Tessa MRO illustrates the expected shape:
schema_version: career_agent_workbench.master_resume.v1
source:
text_path: profile/MP-MASTER-RESUME.txt
extraction_method: g01_approved_public_text
section_order:
- header_top
- professional_summary
- core_technical_skills
- professional_experience
- education
- certifications
- portfolio
basics:
name: Tessa Rowan
label: Senior Platform & Reliability EngineerThe full example also contains source and paragraph evidence, category assignments, and skill links. Those fields make later changes traceable. They must never be used as a place to add unsupported tools, metrics, responsibilities, credentials, or employer facts.
End-to-end application workflow
A normal configured workflow has explicit stages. An operator can stop after any stage and review the state before continuing.
human resume text
↓ reconcile and validate
Master Resume Object
↓ plan bounded public queries
public posting → stored source description → prompt JOD
↓ generate and review
v1 ARO → optional v2 ARO → optional manual ARO
↓ optional exact highlighting
selected variant + ATS diagnostics + separately reviewed CLO
↓
human-controlled external application action (outside this product)1. Find or add a public job
make seed-jobs resolves the configured MRO and blacklist, asks the configured
planner for bounded public search queries, searches guest-accessible job pages,
fetches public details, filters results, and atomically seeds eligible rows. It
records content-free query outcomes so later query ranking can learn from
previous result counts without storing model traffic.
The default CLI bounds searches to remote or hybrid full-time/contract roles
at associate, mid-senior, or director level. LOCATION, DATE_POSTED,
LIMIT_PER_QUERY, MAX_QUERIES, and MAX_JOBS provide explicit Make
overrides. The Flask Add page can also seed a bounded batch, add LinkedIn guest
URLs, or parse other supported public job URLs through the generic ingestion
seam.
This stage is not offline when using the default composition: public-job search/detail calls use the network, and query planning uses the configured API-compatible or Ollama model client. Unit and integration tests inject both capabilities.
2. Preserve the source description and build the prompt JOD
The state layer stores the complete usable public description separately from the prompt-ready text. The deterministic cleaner normalizes whitespace, removes predictable low-signal or trailing compensation/application boilerplate, retains role-bearing sections, and caps prompt context at a line boundary. The source remains available for review and correction.
The Rivermark fixture makes the distinction concrete. Its complete source description is 7,798 characters and includes nine sections. The current prompt-cleaning result is 6,255 characters: it retains company and role context, impact, responsibilities, required and preferred qualifications, and working-model expectations, while omitting the trailing compensation and application sections from prompt context.
For example, this source responsibility:
- Design and maintain Python services, command-line tools, and REST interfaces
that coordinate inventory, configuration, and deployment workflows without
hiding approval boundaries.becomes the same claim as a normalized prompt sentence without the list marker. The deterministic demo then attaches five ordered requirement targets to the ARO, including tested Python and REST automation, Ansible/AWX/Terraform, observability evidence, support and review duties, and the preferred Kubernetes/GitOps gap. It does not pretend the preferred gap is present in Tessa's resume.
Run a read-only audit before rewriting existing prompt JODs:
make audit-jodsThe installed audit command accepts --apply. Apply mode uses the governed
state API, creates a SQLite backup by default, and refreshes ATS diagnostics for
changed rows with rendered resumes unless --skip-ats is specified. The
--no-backup flag is available but removes that default recovery point and
should be an explicit operator decision.
3. Generate the first-pass v1 resume
make generate-draft-resumes creates v1 only for eligible rows that do not
already have it. make regenerate-draft-resumes FIRST_DRAFT_FORCE=1 explicitly
allows replacement. JOB_IDS can scope either command to selected job IDs;
the Make default JOB_IDS=all emits no job flags and lets the script use its
eligible-row behavior.
For each candidate the first-pass workflow:
initializes an ARO from the exact configured MRO;
asks the configured core-skill model for bounded job-match suggestions;
asks the configured JOD model for ordered requirement targets;
rewrites renderable experience bullets by role against that JOD;
renders HTML and PDF, calculates deterministic ATS diagnostics, and stores the
v1variant withrequires_human_review=true; anduses the application's revision to prevent stale work from overwriting newer state.
The model output is not trusted as evidence. Skill matches must resolve to the MRO taxonomy, responses are parsed into expected shapes, and later governed passes validate edits against canonical source evidence. A failed candidate is not silently selected or submitted.
The deterministic public demo is intentionally different from this configured
first-pass command. It builds a neutral ARO locally, attaches fixed public
requirement targets, renders HTML, and stores one v1 without making a model
call. That difference keeps make demo offline and reproducible.
4. Produce the second-pass v2 candidate
make refine-draft-resumes runs the evidence-grounded refinement workflow.
With JOB_IDS=all it selects active rows; otherwise it accepts explicit IDs.
make regenerate-resumes composes first-pass regeneration and v2 refinement,
and make second-pass-refinement is an alias for the refinement target.
The v2 workflow starts from stored v1, snapshots the exact MRO and resume
source text, and builds a bounded evidence packet. The configured model must
return a strict patch set targeting allowlisted summary, bullet, or matched
skill fields. Every proposed change includes the current text, proposed text,
rationale, and evidence references. Validation rejects unsupported claims,
unapproved metrics, stale evidence digests, malformed targets, oversized
responses, or a changed application revision.
After validation, the workbench renders and scores the candidate and stores it
as v2 with v1 lineage. An optional external critique can be supplied
through the CLI, but it is context rather than factual authority. The v2 write
does not override an explicit selection, even when the compatibility --apply
flag is present. A row still in automatic mode may advance to v2 under the
normal preference rule.
5. Run a manual pass and optional highlighting
The manual-pass workflow is a model-assisted review candidate, not an
unstructured rewrite. It requires both v1 and a coherent v2, derives
manual from v2, applies the same evidence snapshots and strict patch
validation, preserves the MRO skill inventory, stores lineage to v2, and
preserves any explicit selection. A row in automatic mode may advance to the
new manual candidate under the same preference rule.
Manual Make execution requires explicit IDs:
make manual-pass-resumes JOB_IDS="demo-platform-001"JOB_IDS=all is intentionally rejected for this target. The default logical
profile is regular; economy and premium are also allowlisted. Workflow-
specific model and reasoning-effort overrides take precedence over shared
Codex settings. Regardless of profile, the result remains marked
awaiting_user_review.
Highlighting is a separate exact-target operation:
make highlight-draft-resumes \
JOB_IDS="demo-platform-001" \
HIGHLIGHT_RESUME_VARIANT=manual \
HIGHLIGHT_MAX_STRONG_SPANS_PER_BULLET=2The highlighter can be scoped by company or experience-job order. It may only
insert validated <strong> wrappers around unchanged text; removing,
reordering, or inventing claim text fails validation. It updates the selected
or explicitly named existing variant rather than creating a fourth variant,
and it never changes selection. The Flask action workflow can chain
highlighting after supported v1, v1+v2, or manual sequences.
6. Compare, select, and review artifacts
Automatic selection chooses the best available variant in the order manual,
v2, v1. Once a person explicitly chooses a variant in the tracker, new or
updated candidates do not move that selection. Resetting selection returns the
row to automatic preference. Variant lineage, validation, evidence and model
metadata, ATS diagnostics, and rendered artifacts remain attached to their
exact variant.
The CLO is reviewed separately in the cover-letter editor. Safe rich text is sanitized before deterministic PDF rendering; unsupported markup and unsafe links are not carried into the artifact. The demo CLO says only that Tessa is interested in the Rivermark role and that the public resume supports the named overlap. It explicitly requires human review.
Recorded public-synthetic refinement
The 2.1.0 documentation pass also exercised the fictional
demo-platform-001 row through the configured workflows in a retained
public-synthetic workspace. This is validation evidence, not a tracked fixture:
the complete sanitized log and generated YAML, HTML, and PDF artifacts remain
outside Git.
Stage | Configuration | Persisted result |
v1 | Existing retained public-synthetic variant; not regenerated | ATS diagnostic score 92 |
v2 |
| Attempt 1; ATS diagnostic score 92 |
Manual |
| Attempt 1; ATS diagnostic score 92 |
Highlight |
| Attempt 1; updated |
The final selected variant is manual under automatic selection. Persisted
prompt and response fields are absent. The unchanged score across variants is
reported as observed; it is not evidence that the variants are identical or
that an employer would score them the same way.
ATS diagnostics and evidence lineage
ATS diagnostics run locally over caller-provided resume PDF bytes and the prompt JOD. The implementation extracts bounded text, checks recognizable resume structure and contact patterns, compares weighted technical terms, calculates semantic-cluster coverage, and reports formatting risk. It stores component scores and bounded matched/unmatched-term evidence with the exact variant.
These numbers help compare drafts under one deterministic implementation. They are not an employer's ATS score, do not validate truth, and must not justify an unsupported rewrite. A higher score is not permission to add a missing tool or claim. Factual lineage remains:
master resume text → MRO evidence item → validated ARO target → variant record
job source text → prompt JOD → requirement target → ATS diagnosticsThe governed v2, manual, and highlighting workflows snapshot evidence-file digests before a model call, recheck the current files immediately before a conditional write, and fail if evidence or application state drifted. Raw model traffic is not persisted as the resume's factual authority.
Flask tracker and application lifecycle
The local website activates the mature archived 31-route Flask application through a thin public runtime adapter. The adapter resolves the current external workspace, database, output, downloads, packaged resume template, and project root. It initializes only an uninitialized database; ordinary app creation and read-only presentation leave an initialized database unchanged.
Start it with the PID-owned operator targets:
make start-website
make start-website OPEN_BROWSER=1
make restart-website
make stop-websiteThe browser stays closed unless OPEN_BROWSER=1 is explicit. PID state and a
bounded log live under ignored tmp/website/. Stop uses only the exact recorded
child PID; stale state does not trigger port scanning or broad process
termination. make launch-website remains the foreground alternative.
The tracker supports:
active, archived, status, and text filtering with lifecycle updates;
Add/Seed, guest LinkedIn URL ingestion, and bounded generic public-URL ingestion;
selected-row execution of allowlisted Make workflows, sequential progress, bounded restart-safe background history, and timeout-retry presentation;
exact selected or named-variant resume HTML/PDF views, download, configured copy, comparison, explicit selection, and automatic-selection reset;
revision-guarded structured resume editing, save, revert, ARO sync, and advanced YAML review;
full source/prompt JOD comparison and governed description updates;
CLO editing, sanitized preview, deterministic PDF, download, and configured copy; and
archive, unarchive, and referentially coherent deletion.
The compact status panel renders the latest eight messages from the default
20-message status response. The existing route also exposes older retained
events: use /actions/status?limit=20&offset=20 for the preceding page or
/actions/status?detail=full for the complete bounded run history. Status
records contain only closed configuration, attempt, timing, timeout, decision,
and completion/failure events. They are stored beneath the configured external
workspace TMP_DIR with an eight-run/160-event retention bound; the response
reports availability and event counts, never a filesystem path. Raw Make
commands, child output, prompts, responses, job IDs, and exception text are not
persisted.
GET presentation routes read application state without mutating it, while
state changes use POST routes and the canonical state layer where adapted. One
explicit legacy helper, /linkedin/<job_id>, opens the row's stored public job
URL in local Chromium; it is a browser-launch action rather than a state write.
The UI does not submit an external application or contact an employer.
The tracked PNGs below are existing synthetic documentation illustrations, not
screenshots produced by make demo and not evidence of private operator state.
They remain intentionally generic and are not presented as captures of the
recorded public-synthetic refinement.
Add jobs and review tracker state
![]()
![]()
![]()

Inspect descriptions, resume variants, and the cover letter





Make and command-line reference
The Makefile is the normal composition layer. Empty path variables do not emit repository-relative private defaults; configuration resolves centrally after argument parsing.
Target | Purpose and important behavior |
| Create the public |
| Verify all six installed public executables. |
| Link exactly five public skills without replacing unrelated files. |
| Materialize the deterministic Tessa/Rivermark demo offline. |
| Plan, search, filter, and atomically seed public job rows; uses configured network/model boundaries. |
| Audit source descriptions against deterministic prompt JODs without applying changes. |
| Create missing v1 candidates for eligible or explicitly selected jobs. |
| Re-run v1; replacement still requires |
| Reconstruct stored ARO objects from the current MRO without a model call. |
| Synchronize selected stored drafts into application-level ARO projection. |
| Produce evidence-grounded v2 candidates. |
| Compose v1 regeneration followed by v2 refinement. |
| Add validated exact-text emphasis to an existing variant. |
| Produce manual-from-v2 candidates for explicit job IDs. |
| Run the local Flask tracker in the foreground. |
| Use PID-owned background lifecycle. |
| Run the complete pytest suite. |
| Run Ruff checks over source, tests, and scripts. |
| Verify Ruff formatting without rewriting files. |
Common workflow examples:
make seed-jobs LOCATION="United States" DATE_POSTED=past_week MAX_JOBS=5
make generate-draft-resumes JOB_IDS="demo-platform-001"
make refine-draft-resumes JOB_IDS="demo-platform-001"
make manual-pass-resumes JOB_IDS="demo-platform-001" MANUAL_PASS_PROFILE=regularThe installed console scripts are:
career-agent-workbench
career-agent-workbench-audit-jods
career-agent-workbench-refine-resume
career-agent-workbench-seed-jobs
career-agent-workbench-webapp
career-agent-workbench-mcpcareer-agent-workbench itself provides help and version. The other commands
expose their workflow-specific options. Stateful scripts additionally accept
explicit path overrides, but portable configuration through the external
workspace is preferred. Use --dry-run where supported. Artifact directories
are validated beneath the private workspace.
MCP server
career-agent-workbench-mcp runs a FastMCP stdio server named “Career Agent
Workbench.” It exposes four business-level tools:
search_linkedin_jobsperforms one bounded guest public search.get_linkedin_job_detailsreturns normalized public details for a job ID or supported public URL.get_linkedin_job_raw_payloadreturns a bounded public payload plus parsed details for debugging the public seam.find_matching_linkedin_jobslazily resolves the configured MRO, output, and database and runs the bounded matching workflow.
The first three tools can start without a workspace. The matching tool requires configured private state and, under default composition, uses both the public provider and configured query-planner model. MCP schemas contain business arguments rather than workspace paths, tokens, or internal configuration.
Register the canonical executable by absolute path. Replace this clearly marked placeholder with the absolute path to your public checkout:
{
"mcpServers": {
"career-agent-workbench": {
"command": "/absolute/path/to/career-agent-workbench/.venv/bin/career-agent-workbench-mcp"
}
}
}Do not pass private workspace paths or tokens as MCP arguments. The process uses the same root bootstrap and external private-dotenv resolution as the CLI.
Configuration and model routing
All optional values shown below belong in the external private dotenv, not the
public bootstrap .env.
Workspace members include PROFILE_DIR, MASTER_RESUME,
MASTER_RESUME_TEXT, OUTPUT_DIR, DATABASE, BLACKLIST, TMP_DIR, and
DOWNLOAD_DIR, each under the CAREER_AGENT_WORKBENCH_ prefix. Network
settings include the public user agent, request timeout, and maximum results.
The provider-neutral model client supports two explicit modes:
CAREER_AGENT_WORKBENCH_LLM_PROVIDER=apiuses the configured OpenAI- compatible base URL, API key, model, planner model, and timeout. The public default base is OpenRouter-compatible; an API key is required before a request.CAREER_AGENT_WORKBENCH_LLM_PROVIDER=ollamauses the configured local Ollama base URL, model, and timeout. The workbench does not install or start Ollama.
JOD extraction, core-skill matching, and second-pass refinement have
workflow-specific model settings. Manual pass and highlighting use the
governed Codex subprocess boundary and have independent model/reasoning-effort
settings. Presence-aware workflow overrides take precedence over shared Codex
settings. Manual profiles are logical public policies: economy uses Terra at
high reasoning, regular uses Sol at high reasoning, and premium uses Sol at
extra-high reasoning unless explicit private overrides are supplied.
Effective workflow execution settings
The model-assisted resume workflows use these public effective defaults:
Workflow | Model policy | Timeout per call/process | Retry count | Total attempts |
v1 core/JOD/experience | Provider-specific configured models | 300 seconds | 1 | 2 |
v2 critique | Provider-specific second-pass model | 600 seconds | 1 | 2 |
Manual pass |
| 900 seconds | 1 | 2 |
Highlighting | Luna/high | 900 seconds | 1 | 2 |
A retry count is the number of retries after the initial attempt, so a retry
count of 2 allows three total attempts. API-backed v1 and v2 retry only typed
timeouts and the closed transient allowlist: HTTP 408, 409, 425, 429, 500, 502,
503, or 504; remote connect, read, or protocol interruption; and a valid empty
completion. They also recover from validated transient provider errors embedded
in an HTTP-200 top-level or choice envelope and from unusable missing, null, or
empty completion shapes when no permanent signal is present. Permanent or
contradictory embedded errors and malformed provider envelopes do not retry.
API-backed syntactically malformed generated JSON retries within the same
workflow-owned budget. The v2 client accepts bare JSON and the shared
extractor's supported Markdown-fenced or prose-wrapped JSON, then passes a
canonical JSON string into the strict resume-patch parser. Ollama-backed v1 and
v2 remain timeout-only. Manual pass and highlighting remain Codex-subprocess-
timeout-only. Codex nonzero exits and missing or invalid output do not retry.
Valid JSON that fails schema, application, policy, or evidence validation
remains nonretryable; rendering, ATS, state, artifact, and configuration
failures likewise fail that row without consuming another workflow attempt.
Batch commands keep later rows isolated and preserve earlier successful writes.
The configured v1/v2 timeout is a wall-clock deadline for each logical model
attempt, not only an HTTP transport-inactivity timeout. Each retry receives a
fresh deadline, backoff happens outside it, and the workflow-created API client
uses one internal request attempt so retry_count + 1 is also the maximum
provider-boundary count for that logical operation. Cancellation closes the
in-flight request before the retry decision; the transport timeout remains a
defense-in-depth boundary.
Each workflow has a configuration-only preflight that resolves normal CLI, Make, process-environment, private-dotenv, compatibility, and default layers, then exits before opening state, creating a model client, or starting Codex:
make generate-draft-resumes CONFIG_ONLY=1
make refine-draft-resumes CONFIG_ONLY=1
make manual-pass-resumes CONFIG_ONLY=1
make highlight-draft-resumes CONFIG_ONLY=1The final aggregate JSON remains on stdout; sanitized configuration and attempt
events go to stderr. Configuration events report only the stage, approved model
label, reasoning effort or inherit, timeout, retry and total-attempt counts,
source layer (cli, make, process, private_dotenv, or default), and a
workspace-configured boolean.
Attempt events retain the stable broad failure category and may carry a closed,
optional failure_subtype; legacy events omit that additive field. Relevant
retry-decision and terminal-failure events may also carry a closed response
summary containing only validated status, error-location/type/code,
finish-reason, choice-count, and content-state labels. It never retains raw
provider text, content, headers, identifiers, endpoints, prompts, or responses.
Make passes the six unprefixed workflow knobs
FIRST_DRAFT_LLM_TIMEOUT_SECONDS, FIRST_DRAFT_LLM_RETRIES,
SECOND_PASS_TIMEOUT_SECONDS, SECOND_PASS_RETRIES, CODEX_TIMEOUT_SECONDS,
and CODEX_RETRIES as explicit command flags, including for Flask-launched
nested Make runs. Consequently, a prefixed compatibility timeout/retry value
does not override those explicit Make flags. Workflow-specific model and effort
values precede shared Codex values. An explicitly present empty reasoning
effort means inherit the Codex CLI setting; an empty model does not create a
model override.
Direct manual-pass and highlighting invocations resolve timeout and retry
fallbacks from CAREER_AGENT_WORKBENCH_CODEX_TIMEOUT_SECONDS and
CAREER_AGENT_WORKBENCH_CODEX_RETRIES; the legacy
LINKEDIN_CAREER_MCP_... forms remain compatibility fallbacks. Explicit CLI
flags are stronger, and Make always supplies those flags from its unprefixed
CODEX_TIMEOUT_SECONDS and CODEX_RETRIES values. The v1/v2 scripts retain
their established direct interfaces: provider timeout settings may come from
the existing API/Ollama environment fields, while their workflow retry count
is an explicit flag or the public default.
Configuration never makes a request merely by loading. The following actions
are offline: help/version parsing, MRO loading, deterministic JOD cleanup,
state reads, ARO regeneration, ATS diagnostics, rendering, public safety
checks, and make demo. Public job search/detail, configured query planning,
v1 generation, v2 refinement, manual pass, and highlighting cross explicit
network or model/process boundaries under their respective commands.
Never assume a model label proves a provider, deployment, privacy policy, or data-retention policy. Operators are responsible for configuring an acceptable endpoint and deciding what private evidence may be sent to it.
Operational limits
LinkedIn support is guest-only and public. Site markup, availability, and rate limits can change; authenticated browsing and private-member data are intentionally unsupported.
Generic URL ingestion accepts bounded public HTTP responses and normalizes supported job pages. It is not a general browser or credentialed scraper.
Job search can miss postings, public detail pages can be incomplete, and model-planned queries can be poor. Counts and ranked results are aids, not a comprehensive labor-market dataset.
JOD cleanup is heuristic. Review the source/prompt comparison before applying an audit rewrite, especially when a posting uses unusual headings.
ATS diagnostics are deterministic local proxies. They do not reproduce a particular employer's parser or predict interview outcomes.
Evidence validation reduces unsupported resume changes but cannot replace a person's factual review. The operator owns every final claim.
PDF rendering depends on supported local rendering capabilities and bounded inputs. The packaged HTML template is autoescaped, and browser-based tests remain offline.
SQLite and the Flask process are designed for a local single-user portfolio workflow, not a multi-tenant service, hosted production deployment, or concurrent recruiting team.
Background actions are local and bounded. Closing the UI does not transform them into a remote job service.
No feature submits applications, sends email, messages recruiters, schedules interviews, accepts terms, or changes an employer's systems.
Development and testing
The codebase favors narrow domain boundaries, caller-injected capabilities, synthetic evidence, and focused regression tests. Start with:
make test
make lint
make format-check
.venv/bin/python scripts/check_public_safety.py tree --root .
git diff --checkFocused test groups map directly to the public behavior:
configuration and CLI tests cover two-phase dotenv resolution, path containment, precedence, help/version behavior, and Make composition;
provider, parsing, ranking, and matching tests inject public responses and prove bounds, filtering, deduplication, and atomic seed behavior;
application-state tests cover JOD/ARO/CLO storage, variant lineage, selection, conditional writes, lifecycle, and artifact targeting;
resume, refinement, manual, highlighting, rendering, and ATS tests cover evidence grounding, safe transformations, exact targets, failure atomicity, HTML/PDF behavior, and human-review metadata;
archived Flask tests bind the inline frontend, route activation, canonical state adapters, editing/revert behavior, and configured downloads;
MCP tests use injected services and an offline stdio handshake to verify the exact tool list and workspace-free public startup;
demo tests prove public source coherence, full posting persistence, one row/one v1/one CLO, idempotence, and Flask presentation; and
release and safety tests verify version alignment, explicit package members, local README links, six console scripts, public-tree exclusions, and isolated installed-package behavior.
See Representative Behavior Coverage for the current traceability matrix, repository guidance for contribution rules, and the MIT license for licensing terms.
When changing public behavior, keep edits scoped, add focused tests, update the version/changelog/release note as required by the active workflow, and use only fictional reserved-domain evidence. Never solve a failing public-safety check by weakening the checker or moving private material into an ignored repository path.
Packaging and release closeout
The project uses pyproject.toml metadata and Hatch build configuration. The
source distribution explicitly includes the README, release documentation,
public demo source and factory, behavior coverage, scripts, tests, skills,
packaged templates/static resources, archived Flask runtime, and tracked visual
assets. Wheel tests verify recursive package resources and all six console
entry points.
Documentation completeness is not release completion. A release candidate must
pass the focused tests for its changes plus the complete synthetic suite, Ruff
check and format verification, git diff --check, and public-tree safety.
Release readiness additionally requires wheel and source-distribution builds
and scans, an isolated wheel install/smoke test, offline Chromium rendering,
the browser workbench, and an injected MCP stdio check.
The current release status is recorded in the 2.1.0 release note and changelog. Release commits remain normal reviewed commits. A release PR must stay unmerged and untagged until its final gate and explicit user approval; merge, version tag, publication, external cutover, and any application activity are separate authorized actions.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceEnables users to search for jobs, prefill applications using AI, and automate submissions across major platforms like Lever and Ashby directly from Claude or Cursor. It provides a full suite of tools for managing job queues, profile data, and resumes within a chat interface.34MIT
- AlicenseNot gradedqualityCmaintenanceEnables to interact with job application workflows through MCP, allowing users to find jobs, generate non-trivial applications with proof-maps, and build offline dashboards, all without auto-submitting.Apache 2.0
- AlicenseNot gradedqualityCmaintenanceAn MCP server that exposes job-search and application-management capabilities to compatible AI clients, enabling discovery of vacancies, drafting of tailored application materials, and coordinated human-approved submissions.MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to discover, filter, and track job openings based on the user's local resume, without uploading data to the cloud.11MIT
Related MCP Connectors
Semantic search over 3.5M+ live US job postings pulled straight from company ATSes. Read-only.
Securely search and manage workspace context files for AI agents and teams.
Cross-agent artifact workspace with provenance across Claude Code, Codex, Cursor, LangGraph.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mperkhou/career-agent-workbench'
If you have feedback or need assistance with the MCP directory API, please join our Discord server