Skip to content

Repository files navigation

Interview Practice MCP Server

A local MCP server that gives Claude persistent memory of your DSA / HLD / LLD / Behavioral interview practice — backed by plain files on your disk that you can open and review anytime.

It knows a built-in catalog of ~230 problems (149 DSA, 30 HLD, 25 LLD, 29 Behavioral), tracks what you've attempted and when, and can recommend what to practice next: new problems, or ones due for revision, biased toward your flagged weak areas.

What it stores

Session/tracking data, under ~/interview-prep/ by default:

  • revision.md — human-readable chronological log, one section per session. This is the file you review.
  • index.json — compact machine index: sessions, aggregated weak areas, competency scores (Behavioral), and a per-problem tracker (times practiced, last verdict, last attempt date, absolute path to the linked solution/doc). Keeps Claude's context small.
  • custom_catalog.json — problems you've added yourself (e.g. from a mock interview), merged with the built-in catalog.json shipped next to server.py.

Per-problem solutions/docs live in four independent, separately configurable directories — each defaults to a folder under ~/interview-prep/docs/ but is normally pointed at wherever your real practice work lives:

  • DSA_SOLUTIONS_DIR (e.g. ~/code/DSA_mock) — organized as <Topic_Folder>/<problem>.py, one real Python file per problem grouped by topic, matching a typical personal LeetCode-practice layout. DSA is code, not markdown — see save_dsa_solution below.
  • HLD_SOLUTIONS_DIR (e.g. ~/code/HLD) — one markdown file per HLD problem (e.g. design-rate-limiter.md).
  • LLD_SOLUTIONS_DIR (e.g. ~/code/LLD) — holds two separate things: the reference corpus of your own designs, one self-contained .py per problem grouped into category folders (1_state_machine/vending_machine.py), plus flat design-parking-lot.md write-ups from save_practice_doc; and Mock Solutions/, the mock-interview loop where you attempt a problem and Claude grades it. See LLD mock interviews below.
  • BEHAVIORAL_SOLUTIONS_DIR (e.g. ~/code/Behavioral) — holds a single candidate_context.md: your reusable background + STAR story bank, with a "Current Focus" section you refresh per company/role. See save_candidate_context below.

Tools exposed to Claude

ToolWhen Claude uses it
get_progress_summaryStart of every session — session table, weak areas, competency scores, catalog coverage per type
get_catalogBrowse the full problem pool for DSA/HLD/LLD/Behavioral, filterable by topic/difficulty/status, to find a problem's exact id
suggest_next_problems"What should I practice next?" — ranks due-for-revision and weak-area-matching problems above fresh new ones
add_custom_problemAdd a problem not in the built-in catalog (company-specific, from a mock interview, a real behavioral question you were asked, etc.)
log_sessionEnd of every session, all types — appends to revision.md, updates weak areas and the per-problem tracker; optionally records per-competency scores (1-5) and improvement suggestions
save_practice_docHLD/LLD only — after a design discussion, writes its own markdown file for quick revision
get_practice_docFetch one saved HLD/LLD doc in full, e.g. to revise a specific past solution
list_practice_docsList all saved HLD/LLD docs (title, topic, last updated) as a revision menu
save_candidate_contextBehavioral only — persist your reusable candidate profile + STAR story bank as one markdown file, overwriting the previous version
get_candidate_contextFetch the saved candidate context in full, e.g. at the start of a behavioral mock session
scan_dsa_directoryRead DSA_SOLUTIONS_DIR — folder/filename/problem-statement-snippet/last-modified for every .py file, flagging which are already tracked
import_solved_dsa_problem(s)Backfill the tracker from an existing file on disk (matched to a catalog id) without touching the file — uses its mtime as "last practiced"
save_dsa_solutionAfter solving a NEW DSA problem — writes a .py file into the right topic folder in DSA_SOLUTIONS_DIR; refuses to overwrite an existing file unless told to
scan_lld_directoryRead LLD_SOLUTIONS_DIR — folder/filename/kind/snippet/last-modified for every file in the reference corpus, flagging which are already tracked. Only kind=solution rows are per-problem designs; Mock Solutions/ is excluded
read_lld_solutionOpen ONE LLD file in full — a past design, a corpus doc, or a mock attempt you're about to grade
import_solved_lld_problem(s)Backfill the tracker from an LLD design already on disk, without touching the file. Refuses anything that isn't kind=solution
save_lld_solutionWrite a NEW design into the reference corpus, in the right category folder; refuses to overwrite unless told to
start_mock_attemptPose an LLD problem — creates Mock Solutions/<id>/ with problem.md and an attempt.py stub for you to fill in. Never overwrites an existing attempt; re-posing opens the next round
list_mock_attemptsEvery mock problem and round: which files exist, what's still awaiting grading, scores and verdicts
save_mock_evaluationGrade an attempt against the fixed 7-dimension rubric — writes evaluation.md, logs the session, and regenerates feedback.md
save_ideal_solutionWrite the interviewer's reference design as ideal.py, beside your attempt. Can only ever write ideal*.py, so it cannot touch your own file
save_simple_solutionWrite a pared-back version of that design as simple.py — what a strong candidate could realistically finish in the time box. Can only ever write simple*.py
get_lld_feedbackRubric averages, weakest dimensions, and attempt history — call this beforesuggest_next_problems("LLD") to aim the next question
get_session_detailRevisit the full log entry for one past session
resolve_weak_areaRemove a weak area once you've demonstrably improved at it

Setup (macOS)

  1. Keep this folder wherever you cloned it, e.g. /Users/YOUR_USERNAME/code/file_mcp/. server.py and catalog.json must stay next to each other — the catalog is loaded relative to the script.

  2. Install the MCP SDK (any Python ≥3.10):

    pip3 install mcp

    (If you use uv: uv pip install mcp, and adjust the command below to your venv's python.)

  3. Open Claude Desktop → Settings → Developer → Edit Config. This opens ~/Library/Application Support/Claude/claude_desktop_config.json. Add:

    {
    "mcpServers": {
    "interview-memory": {
    "command": "python3",
    "args": ["/Users/YOUR_USERNAME/code/file_mcp/server.py"],
    "env": {
    "INTERVIEW_PREP_DIR": "/Users/YOUR_USERNAME/interview-prep",
    "DSA_SOLUTIONS_DIR": "/Users/YOUR_USERNAME/code/DSA_mock",
    "HLD_SOLUTIONS_DIR": "/Users/YOUR_USERNAME/code/HLD",
    "LLD_SOLUTIONS_DIR": "/Users/YOUR_USERNAME/code/LLD",
    "BEHAVIORAL_SOLUTIONS_DIR": "/Users/YOUR_USERNAME/code/Behavioral"
    }
    }
    }
    }

    Use absolute paths. If python3 on your PATH isn't the one with mcp installed, use the full path (check with which python3). Omit any of the four *_SOLUTIONS_DIR vars you don't need — each independently falls back to a folder under INTERVIEW_PREP_DIR.

  4. Fully quit Claude Desktop (Cmd+Q) and reopen it. You should see the server's tools under the connectors/tools indicator in the chat input.

Note:~/code/HLD only ever receives what this server writes, so there's nothing to import there. ~/code/LLD is different — it already holds a real corpus of hand-written .py designs in category folders, so start with the first-time LLD import below. Older LLD work outside LLD_SOLUTIONS_DIR (e.g. ~/code/Practice LLD/chess/main.py) is not reachable by these tools; move it under ~/code/LLD if you want it tracked.

First-time DSA import

If DSA_SOLUTIONS_DIR already has solutions in it (like an existing Arrays_and_Hashing/, Graphs/, etc. layout), the tracker starts out blank until you import — the files existing on disk isn't the same as the MCP knowing about them. Ask Claude:

Scan my DSA directory and import everything you can confidently match to the catalog.

Claude will call scan_dsa_directory(), read each file's problem-statement snippet, match filenames like island.pynumber-of-islands or top_k.pytop-k-frequent-elements using its own judgment (filenames rarely match catalog ids exactly), and call import_solved_dsa_problems in bulk. Anything it can't confidently match (scratch files, company-specific problems) it should flag for you to decide — add those via add_custom_problem first if you want them tracked. This step only reads files and writes to index.json; it never modifies anything under DSA_SOLUTIONS_DIR.

First-time LLD import

LLD_SOLUTIONS_DIR (~/code/LLD) already holds ~31 hand-written designs in numbered category folders, none of which the tracker knows about. Ask Claude:

Scan my LLD directory and import everything you can confidently match to the catalog.

scan_lld_directory() returns every file with a Kind column — solution (a per-problem design), category-doc (a folder README), aggregate-doc (INDEX.md, QUICK_REFERENCE.md and friends, which span many problems), practice-doc (a markdown write-up this server wrote via save_practice_doc), or other. Only solution rows are importable; import_solved_lld_problem(s) refuses the rest, so an index file can never get linked to a single problem. Claude matches filenames to catalog ids by judgment (8_lru_cache.pydesign-lru-cache-oop), and roughly half the corpus isn't in the 25-entry built-in LLD catalog at all (order_lifecycle.py, whatsapp_messaging.py, audit_trail.py, …) — those need add_custom_problem first. Nothing under ~/code/LLD is modified; only index.json is written.

LLD mock interviews

The loop that turns practice into targeted practice. You write the design yourself; Claude grades it as the interviewer and remembers where you're weak.

~/code/LLD/Mock Solutions/
feedback.md # running scorecard, regenerated after each grading
design-parking-lot/
problem.md # the prompt, as Claude posed it
attempt.py # YOUR work — no tool in this server ever writes over it
evaluation.md # Claude's scored critique
ideal.py # Claude's reference design
simple.py # the same design pared back to interview scope

Re-attempting a problem later opens the next round (attempt_2.py, evaluation_2.md, ideal_2.py, simple_2.py), so you keep the earlier one to diff against.

The flow. Say:

Let's do an LLD mock interview.

  1. Claude calls get_lld_feedback() and suggest_next_problems("LLD") to pick a problem aimed at your weakest dimensions, then start_mock_attempt(...) — which writes the prompt and an empty attempt.py.
  2. You write your design in attempt.py, on your own. Say when you're done.
  3. Claude calls read_lld_solution("Mock Solutions/design-parking-lot/attempt.py"), then save_mock_evaluation(...) with rubric scores and a written critique, then save_ideal_solution(...) and optionally save_simple_solution(...).
  4. Read evaluation.md and diff attempt.py against ideal.py — and against simple.py for the version that actually fits the clock.

The rubric is a fixed seven-dimension vocabulary, scored 1-5. It's fixed on purpose: free-form labels would never aggregate, and the aggregate is exactly what makes later sessions pick different problems.

DimensionWhat it measures
requirements-and-scopeClarifying questions, scoping, stated assumptions
class-decompositionEntity/responsibility split, cohesion
design-patternsPattern choice, and whether it was justified
solid-and-extensibilitySOLID, open/closed, how the design absorbs change
concurrency-and-edge-casesThread safety, races, boundary conditions
code-qualityNaming, structure, idiomatic Python
tradeoff-communicationArticulating and defending alternatives

Scores accumulate in index.json under lld:-prefixed keys (kept apart from Behavioral competency scores, which share that store), and surface in three places: feedback.md for you to read, get_lld_feedback() and get_progress_summary() for Claude, and a weakest-dimensions note appended to suggest_next_problems("LLD"). That last one matters — the generic weak-area ranking matches against catalog topics, and every LLD topic starts with "OOP Design", so without the rubric the feedback would never actually change what gets asked.

Safety.attempt.py is yours: start_mock_attempt refuses to overwrite one, save_ideal_solution can only ever write a file named ideal*.py, and save_simple_solution only simple*.py. Scans and imports exclude Mock Solutions/ entirely, so Claude's own output can never be backfilled into the tracker as your finished work.

Wiring it into your practice routine

Say this at the start of a chat (or bake it into a Claude Desktop project/skill):

At the start of every practice session, call get_progress_summary, then suggest_next_problems for the type we're practicing (DSA/HLD/LLD) and let me pick from the top few. For LLD also call get_lld_feedback first and aim the pick at my weakest rubric dimensions. After we solve/discuss it, call log_session (with problem_id set). Then for HLD/LLD call save_practice_doc with the full write-up (HLD: requirements, capacity estimate, architecture, API/data model, trade-offs; LLD: class design, patterns used, key decisions). For DSA, if the problem isn't already on disk, call save_dsa_solution with the final code and a short explanation.

If I say I want to attempt an LLD problem myself rather than discuss it, run the mock loop instead: start_mock_attempt, wait for me to write attempt.py, then read_lld_solution, save_mock_evaluation (score honestly — inflated scores break the feedback loop), save_ideal_solution, and save_simple_solution for the cut-down version.

Example flow (HLD):

  1. "What should I practice today?" → Claude calls get_progress_summary, then suggest_next_problems("HLD"), and proposes 2-3 options (mixing anything due for revision with new ones matching your weak areas).
  2. You work through the problem together.
  3. Claude calls log_session(..., problem_id="design-rate-limiter") to record the verdict/gaps, then save_practice_doc("HLD", "Design a Rate Limiter", <full writeup>, problem_id="design-rate-limiter"), creating HLD_SOLUTIONS_DIR/design-rate-limiter.md.
  4. Next time you ask to revise, list_practice_docs() shows everything documented so far, and get_practice_doc pulls up any specific one in full.

Example flow (DSA):

  1. suggest_next_problems("DSA") → you pick "Course Schedule".
  2. You solve it together.
  3. log_session(..., problem_id="course-schedule"), then save_dsa_solution("course-schedule", "Course Schedule", "Graphs", <code>, explanation="topological sort via Kahn's algorithm") → creates DSA_SOLUTIONS_DIR/Graphs/course_schedule.py.

Example flow (Behavioral):

  1. Before your first mock session, tell Claude about your background and stories; it calls save_candidate_context(...) to write BEHAVIORAL_SOLUTIONS_DIR/candidate_context.md (a "Core Profile & Story Bank" section, plus a "Current Focus" section for whichever company/role you're currently prepping for).
  2. "Let's do a behavioral mock for CompanyX." → Claude calls get_candidate_context() to see your stories, then suggest_next_problems("Behavioral") and proposes a few questions (built-in catalog of 29 common questions across competencies like Leadership, Conflict & Disagreement, Ownership — see catalog.json), or you can just ask about a real question you were asked and it'll use add_custom_problem("Behavioral", ...) to track it.
  3. You answer with a STAR story together.
  4. Claude calls log_session(..., interview_type="Behavioral", problem_id=..., competency_scores={"leadership": 4, "communication": 3}, improvements="Add a concrete metric to the result step.") — this appends the verdict, scores, and improvement suggestions to revision.md, and updates the per-competency score averages shown in get_progress_summary.
  5. Prepping for a different company next week? Call save_candidate_context(...) again with an updated "Current Focus" section — the Core Profile & Story Bank stays intact since you write the whole file each time.

Tuning revision timing

By default a problem is considered "due for revision" 21 days after its last attempt (or immediately, regardless of days, if it matches a currently flagged weak area). Change it via the INTERVIEW_PREP_STALE_DAYS env var in the Claude Desktop config's env block.

Adding more problems

catalog.json (next to server.py) is the shared, built-in catalog — feel free to hand-edit it, or just ask Claude to call add_custom_problem, which writes to custom_catalog.json inside your INTERVIEW_PREP_DIR instead (keeps your personal additions separate from the shipped list).

Testing / autostart

A stdio-transport MCP server (what this is) isn't a background daemon you start once and leave running — Claude Desktop spawns it fresh as a subprocess every time it needs it, using whatever command/args/env is in mcpServers.interview-memory in claude_desktop_config.json. So there's no separate "autostart" toggle: being registered in that config is autostart — Desktop manages the process lifecycle automatically from then on. That's already wired up (see Setup above); you just need to fully quit (Cmd+Q, not just close the window) and reopen Claude Desktop for it to pick up the config.

Four ways to check it's actually working, in increasing order of realism:

  1. Does it even boot?python3 server.py from this folder — should start and hang silently (that's correct; it's waiting for a client on stdin). Ctrl+C to stop.
  2. Does it speak MCP correctly?python3 test_server.py — spawns the server as a real MCP client would, does the protocol handshake, lists all 26 tools, and calls get_progress_summary for real. Read-only, safe to run anytime. A clean "All checks passed" means the server itself is solid, independent of Claude Desktop.
  3. Do the LLD tools actually behave?python3 test_lld_tools.py — builds a synthetic design repo in a temp directory and exercises every LLD tool against it: path guards, kind classification, rubric validation, the full mock loop, and above all that attempt.py comes out byte-identical to what was written. Hermetic — it never touches ~/code/LLD or your real index.json.
  4. Is Claude Desktop actually using it?
    • Open a chat and look at the tools/connectors icon near the input box — interview-memory should be listed with its tool count.
    • ps aux | grep server.py — while Desktop is open, you should see a live python3 .../server.py process (Desktop spawns it once you open a chat that uses it, or at startup depending on version).
    • Logs: ~/Library/Logs/Claude/mcp-server-interview-memory.log.
    • Interactive debugging (optional): mcp dev server.py launches the MCP Inspector, a local web UI where you can call any tool by hand and see raw responses — useful for poking at edge cases outside a real chat.

Security

This server only ever runs over stdio (mcp.run(transport="stdio")), the same as the original. Stdio means Claude Desktop spawns it as a local subprocess and talks to it over stdin/stdout pipes — it opens no network port, so there is nothing for another machine, process, or website to connect to. Don't change the transport to sse/http unless you also add authentication and bind to localhost; as shipped, "outside access" isn't a meaningful attack surface because there's no listener at all.

Within the local filesystem, a few guardrails are worth knowing about since Claude drives these tools somewhat autonomously:

  • Path sanitization. Every value used to build a filename (problem_id, problem titles) is passed through a slugifier that strips everything except a-z 0-9 - before it ever touches a path. A value like ../../etc/passwd becomes etc-passwd, not a traversal — verified with an adversarial test during development.
  • Directory containment.save_practice_doc can only write inside HLD_SOLUTIONS_DIR or LLD_SOLUTIONS_DIR (whichever matches the call); save_dsa_solution can only write inside DSA_SOLUTIONS_DIR; save_candidate_context/get_candidate_context are confined to BEHAVIORAL_SOLUTIONS_DIR; import_solved_dsa_problem(s) can only link to files that already live inside DSA_SOLUTIONS_DIR (it refuses anything outside it, e.g. ~/.ssh/, /etc/); get_practice_doc re-checks that whatever index.json points at is still inside the allowed directory for that problem type before reading it.
  • No code execution. The server only reads and writes text files. It never execs, evals, or runs the DSA solutions it stores — code/content_markdown arguments are treated purely as bytes to persist.
  • Overwrite protection.save_dsa_solution refuses to replace an existing file unless overwrite=True is passed explicitly, since DSA_SOLUTIONS_DIR is assumed to hold real, hand-written work. (save_practice_doc for HLD/LLD, and save_candidate_context for Behavioral, do overwrite by design — they hold Claude's latest write-up/profile, and those directories are managed entirely by this server.)
  • Env-var-scoped roots.INTERVIEW_PREP_DIR, DSA_SOLUTIONS_DIR, HLD_SOLUTIONS_DIR, LLD_SOLUTIONS_DIR, and BEHAVIORAL_SOLUTIONS_DIR are only ever set by you, in your own Claude Desktop config — they aren't something a chat message can override.

Troubleshooting

  • Server not appearing: check JSON syntax in the Claude Desktop config (one bad comma disables everything), confirm paths are absolute, then look at logs in ~/Library/Logs/Claude/mcp-server-interview-memory.log.
  • Test the server manually: python3 server.py should start and wait silently (Ctrl+C to exit).
  • ModuleNotFoundError: No module named 'mcp': run pip3 install mcp with the same python3 referenced in the config.
  • save_dsa_solution refusing to write: it never overwrites an existing file without overwrite=True — this is deliberate, since DSA_SOLUTIONS_DIR is treated as containing real, hand-written solutions.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages