Install
$ agentstack add mcp-naji-najari-career-copilot ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Career Copilot
A multi-agent CV × JD pipeline on Google ADK v2.
Live demo · Project write-up
Career Copilot reads a CV against a job description and produces a different output depending on who is asking. Recruiters get a fit verdict and a LinkedIn outreach draft that quotes a real achievement from the CV. Candidates get a company brief researched live through Tavily MCP and a tailored interview-prep bundle.
The point of the project is the orchestration. Google ADK v2 shipped its graph workflow API recently, in the same spirit as LangGraph. This is an end-to-end test of it on a real use case.
Why a graph
A single big-prompt agent works until you need any of: deterministic branching, parallel work, typed contracts at every step, or per-step tracing. Once you do, a graph stops being optional.
- Explicit topology: nodes, edges, parallel branches, conditional routing, and sync points are first-class.
- Testable units: each node can be tested, swapped, or traced in isolation.
- Typed state: contracts at every boundary instead of free-form prompt parsing.
- Predictable cost: you know upfront which steps run, in what order, and which model serves each.
How it works
flowchart LR
IN(["CV + JD+ mode"]):::io
IN --> CV[CV Parser]:::agent
IN --> JD[JD Parser]:::agent
CV --> MR(["Mode Router"]):::router
JD --> MR
MR -- recruiter --> FA[Fit Analyzer]:::agent
FA --> VR(["Verdict Router"]):::router
VR -- fit / borderline --> OW["Outreach Writermedium"]:::agent
VR -- no_fit --> GE[Gap Explainer]:::agent
MR -- candidate --> CFORK((·)):::fork
CFORK --> RA["Research Agent+ Tavily MCP"]:::agent
CFORK --> CO[CV Optimizer]:::agent
RA --> IP[Interview Prep]:::agent
CO --> IP
OW --> OUT1(["RecruiterFit"]):::io
GE --> OUT2(["RecruiterNoFit"]):::io
IP --> OUT3(["CandidateResp"]):::io
classDef agent fill:#E3F2FD,stroke:#1565C0,stroke-width:2px,color:#0D47A1
classDef router fill:#E8F5E9,stroke:#2E7D32,stroke-width:1.5px,color:#1B5E20
classDef fork fill:#9E9E9E,stroke:#616161,stroke-width:1px,color:#9E9E9E
classDef io fill:#F5F5F5,stroke:#616161,stroke-width:1px,color:#212121
CV and JD are parsed in parallel. The mode router (a FunctionNode, not an LLM) splits the flow:
- Recruiter branch: linear chain. Fit Analyzer scores fit, Verdict Router routes to Outreach Writer (on fit / borderline) or Gap Explainer (on no_fit).
- Candidate branch: parallel fan-out. Research Agent (Tavily MCP) and CV Optimizer run concurrently, synchronize through a
JoinNode, then feed Interview Prep.
Internal JoinNodes are omitted from the diagram for clarity. See [app/agent/agent.py](backend/app/agent/agent.py) for the full topology.
Architecture decisions
The graph encodes a deliberate separation between deterministic control flow and LLM-driven generation. Routing and synchronization stay in pure Python; only generation crosses an LLM boundary, and every output is constrained by a Pydantic schema before it leaves a node.
- Control flow stays out of the LLMs. Routing lives in
FunctionNodes that decide branches in pure Python on already-typed state.JoinNodes wait for parallel branches to complete before firing downstream. No LLM ever picks which branch fires next, which removes a whole class of failure modes from the critical path. - One schema per agent. Each LLM agent declares its own
output_schema. Invalid output fails the node loud and fast instead of corrupting state in a downstream agent. - Tool-use escape hatch. ADK currently disallows combining
output_schemawith tools ongpt-5.4-mini. The Research Agent uses the Tavily MCP toolset and emitsCompanyIntelligenceas a JSON string, validated withmodel_validate_jsonat the API boundary so the typed-state invariant is preserved at the edge.
Agents
All agents run OpenAI gpt-5.4-mini via LiteLlm. The Outreach Writer is the only one dialled up to medium reasoning effort.
| Agent | Role | Output schema | | ------------------ | --------- | ------------------------- | | CV Parser | Parser | ParsedCV | | JD Parser | Parser | ParsedJD | | Fit Analyzer | Recruiter | FitVerdict | | Outreach Writer | Recruiter | OutreachDraft | | Gap Explainer | Recruiter | GapReport | | Research Agent | Candidate | CompanyIntelligence | | CV Optimizer | Candidate | CVOptimizationBundle | | Interview Prep | Candidate | InterviewPrepBundle |
Tracing
Every /v1/analyze run is traced end-to-end with Langfuse. Sub-agent calls, tool calls, latency, and token counts nest under a parent agent observation. The filterable trace attributes propagated to Langfuse carry only metadata and sizes (mode, model, version, cv_chars, jd_chars); raw CV and JD content is kept out of the trace tags so traces stay free of PII.
Stack
| Layer | Stack | | --------- | ------------------------------------------------------------------ | | Backend | Python 3.12, FastAPI, Google ADK v2, uv | | Models | OpenAI gpt-5.4-mini via LiteLlm | | Research | Tavily via MCP (McpToolset) | | Frontend | Next.js 15, React 19, Tailwind v4, shadcn/ui, TanStack Query, Zod | | Tracing | Langfuse | | Deploy | Docker (Cloud Run / Fly.io / HuggingFace Spaces) |
Run it
Prerequisites
- Python 3.12 with uv
- An OpenAI API key for
gpt-5.4-mini - A Tavily API key for the candidate-mode Research Agent
Install and configure
cd backend
uv sync
cp .env.example .env # fill in OPENAI_API_KEY and TAVILY_API_KEY
Start the backend
uv run uvicorn app.main:app --reload --port 8080
API docs at . Try POST /v1/analyze with {"cv_text": "...", "jd_text": "...", "mode": "recruiter" | "candidate"}.
Run the tests
uv run pytest tests/ -q
License
[MIT](./LICENSE)
Source & license
This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Naji-Najari
- Source: Naji-Najari/career-copilot
- License: MIT
- Homepage: https://career-copilot.najinajari.com
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.