Create Your Python Project

Objectives

1. Your first milestone: a project, not an agent yet

In about 20 minutes, you will create an isolated Python workspace, run a small function, install two course dependencies, and record what actually installed. You do not need a model, API key, cloud account, or running MCP server for this lesson. Package installation does require access to your configured package index.

A Python interpreter runs Python code. A module is a file you can import. A third-party package adds code installed separately from Python. A virtual environment gives this project its own interpreter entry point and installed packages, so another project's dependencies need not change yours. It is dependency isolation, not a security sandbox. [S1]

Use Python 3.11 or newer as the starting baseline: the supplied MCP installation documentation specifies 3.11+. That is a minimum starting point, not a guarantee that every newer interpreter and every package combination will work. Before proceeding, check the selected release's Python requirements and your actual installation results rather than assuming compatibility from the baseline alone. [S2]

Our design is deliberately small: two Python files for the first program and one helper to record the environment. We will postpone JSON and API requests until the next lesson.

Check yourself: Will creating a virtual environment automatically download a different Python version? No. The interpreter used to run venv determines the environment's Python version. [S1]

2. Create and activate the right environment

Open a terminal. These are terminal commands, not lines to enter at Python's >>> prompt. If you see that prompt, enter exit() first. Choose only the block for your platform.

macOS or Linux, Bash or Zsh:

python3 --version
mkdir agent-course
cd agent-course
python3 -m venv .venv
source .venv/bin/activate

Confirm the first command reports Python 3.11 or newer before creating the environment. If python3 selects an older interpreter, use an installed qualifying interpreter, such as python3.11, for both the version check and environment creation. venv does not upgrade Python. [S1]

Windows, PowerShell: this example deliberately selects an installed Python 3.11 through the Python launcher.

py -3.11 --version
mkdir agent-course
cd agent-course
py -3.11 -m venv .venv
.\.venv\Scripts\Activate.ps1

If you have a different qualifying version installed, use that launcher's version selector instead. If the launcher or selected Python is unavailable, install a supported Python interpreter before continuing; do not treat an unrecognized command as successful setup.

Windows, Command Prompt: use the same version-check, directory, and creation commands as above, but activate with:

.venv\Scripts\activate.bat

Activation selects the environment for the current terminal session; Unix and Windows use different activation scripts. [S1] If PowerShell blocks its script, do not weaken machine-wide execution policy for this lesson. You can use Command Prompt, or explicitly run .\.venv\Scripts\python.exe wherever subsequent commands say python. On macOS/Linux, the equivalent explicit path is .venv/bin/python.

After activation, run:

python --version
python -c "import sys; print(sys.executable); print(sys.prefix != sys.base_prefix)"
python -m pip --version

Expected checks, not a transcript from your machine: Python is 3.11+, the executable path points inside this project's .venv, the comparison prints True, and pip's location is inside .venv. Activation changes command lookup, so checking the interpreter is more reliable than trusting the prompt alone. [S1]

If pip is missing or environment creation reports a missing venv component, stop and repair your Python installation using your platform's installation guidance. Do not switch to a global or administrator pip as a workaround.

Check yourself: Why inspect sys.executable before installing anything? You want evidence of which Python the terminal will actually use.

3. Write a typed function and import it

Use a text editor to create greetings.py and main.py directly inside agent-course, not inside .venv. Copy the corresponding Python code examples below into those files. Save them with a .py extension, not .py.txt.

In greetings.py, def defines a reusable function. The name parameter receives an input, : str documents that the intended input is text, and -> str documents a text result. These are type hints, not automatic runtime input validation. The indented body uses return to send a value back to the caller. Our own explicit empty-input check raises ValueError; the type hints do not perform that check.

In main.py, from greetings import make_greeting imports the function from your neighboring file. import sys accesses Python's built-in system module; it needs no pip installation. Python's standard library includes system interfaces and small testing utilities, so not every import represents an external dependency. [S5]

Run this from the project root:

python main.py

Expected behavior for the supplied code: it prints Hello, builder! followed by your actual interpreter path. The assertion checks the returned string before printing. This checks one small piece of Python behavior; it does not exercise MCP or LangGraph.

Keep all four spaces at the start of each indented function-body line. The if __name__ == "__main__": guard makes main() run when the file is executed as a program, rather than automatically whenever another file imports it.

Try it: change the argument in main.py to " explorer " and also change the assertion to assert greeting == "Hello, explorer!". Predict the greeting, then run python main.py again: the function strips the surrounding spaces, so it prints Hello, explorer! followed by the interpreter path. If you change only the argument, the old assertion raises AssertionError before either print. Next, change the argument to " " and run again to observe the deliberate ValueError; the function raises it before reaching the assertion. Afterward, restore both the argument to " builder " and the assertion to assert greeting == "Hello, builder!", then rerun to confirm the original greeting. If importing greetings fails, check the file name, file extension, and that both files are in the project root.

4. Install course dependencies with this interpreter

Now add dependencies to the environment you just verified. We will request the MCP v2 API family described by the supplied documentation, rather than silently accepting a future major version. That documentation identifies v2 as its stable line and warns that v1 code needs migration. This version range is a lesson selection policy, not an exact tested pin. [S2]

python -m pip install "mcp[cli]>=2,<3" langgraph
python -m pip check
python -m pip show mcp langgraph
python -c "import mcp; import langgraph; print('Dependency imports passed')"

python -m pip runs pip through the interpreter named python; this keeps installation aligned with the program's interpreter. The quoted MCP requirement includes the optional CLI dependencies and limits resolution to the 2.x family. Exact selected versions will be recorded next. [S1][S2]

LangGraph's base package is installed separately. You do not need to install all of LangChain just to begin using LangGraph; provider integrations are separate packages and will be added when needed. [S9]

Interpret the evidence narrowly:

If pip reports no matching distribution, a Python-version mismatch, or conflicting requirements, keep the error. Check the selected Python version and release-specific package requirements. Do not remove the major-version constraint merely to get a successful install, and do not substitute older v1 imports into a v2 lesson. If the requested release family cannot be resolved, this dependency milestone is blocked; the standard-library function exercise can still run. [S2]

Check yourself: Does importing both packages prove a complete agent works? No: you have not created an MCP connection, run a graph, or called a model.

5. Record what installed and keep a reproducible layout

A dependency request expresses what you want; a snapshot records what you got. pip freeze produces requirements-format entries for installed dependencies, and pip install -r can request those recorded versions in another environment. [S1]

Create record_environment.py using the final code example, then run:

python record_environment.py

The helper uses sys.executable to run this same interpreter's pip. It writes requirements.txt in UTF-8 only after freeze succeeds, plus an environment.txt recording Python, platform, and the limited validation status. This avoids relying on different shells' output-file encoding behavior. Inspect the two generated files in your editor: use the actual package versions, never numbers copied from an illustrative tutorial. A freeze snapshot is not a cross-platform lockfile or proof of combined-stack compatibility. [S1][S9]

Your layout should now look like this; generated files appear only after the helper succeeds:

agent-course/
  .venv/
  greetings.py
  main.py
  record_environment.py
  requirements.txt
  environment.txt
  README.md
  .gitignore

Create README.md with your platform, Python version, chosen MCP major line, setup commands, and the checks you actually ran and their results. Explicitly record MCP + LangGraph + local model: not integration-tested yet. No local model was configured in this lesson. Do not change that status based only on installation or imports. A course release needs dependencies resolved together and compatibility tests before it can present a tested stack. [S9]

Create .gitignore with these lines as a project convention:

.venv/
__pycache__/
.env

Keep source and environment records in version control if you use it; do not commit the environment directory. Treat .env as a reserved local configuration file, not as another name for .venv. No secrets are needed here.

For a later recreation, make and select a fresh environment using the recorded interpreter version, then run:

python -m pip install -r requirements.txt
python -m pip check
python main.py

The requirements file does not install Python itself. Different operating systems or missing package artifacts may still prevent recreation, so record and investigate failures rather than claiming universal reproducibility. [S1][S9]

Check yourself: Which file records package versions, and which records Python and validation status? requirements.txt and environment.txt, respectively.

6. Exit check and next-session routine

Before moving on, verify these outcomes on your own computer:

  1. Environment: the interpreter check points inside .venv and reports Python 3.11+.
  2. Function and import: python main.py prints the greeting and your environment's interpreter path; the empty-input experiment raises the intended error.
  3. Dependencies: installation, dependency checking, and the import smoke check completed. If not, mark this item blocked and retain the real error.
  4. Record: requirements.txt contains the versions that actually installed; environment.txt and README.md distinguish basic checks from unperformed integration tests.

For a new terminal session, enter the project directory and repeat only the activation command for your shell, then check sys.executable. You do not recreate .venv on every visit. When finished, enter deactivate to undo activation in the current shell. Activation and deactivation change the shell's interpreter selection, not your source files. [S1]

Final self-test: Someone says, “I froze the dependencies, so the agent stack is tested.” Explain the missing step. A version snapshot records installation state; an integration test must actually exercise the components together. Neither your greeting assertion nor a successful import has done that. [S8][S9]

You now have the workspace for the next lesson, where ordinary Python values will become structured JSON data.

def make_greeting(name: str) -> str:
    cleaned = name.strip()
    if not cleaned:
        raise ValueError("name must contain non-space text")
    return f"Hello, {cleaned}!"

Save as greetings.py in the project root. This original function uses type hints and an explicit empty-input check.

import sys

from greetings import make_greeting


def main() -> None:
    greeting = make_greeting("  builder  ")
    assert greeting == "Hello, builder!"
    print(greeting)
    print(sys.executable)


if __name__ == "__main__":
    main()

Save as main.py beside greetings.py; run with python main.py from the project root.

from pathlib import Path
import platform
import subprocess
import sys


def main() -> None:
    project = Path(__file__).resolve().parent
    snapshot = subprocess.run(
        [sys.executable, "-m", "pip", "freeze"],
        check=True,
        capture_output=True,
        text=True,
        encoding="utf-8",
    )
    (project / "requirements.txt").write_text(
        snapshot.stdout, encoding="utf-8"
    )
    details = (
        f"Python: {platform.python_version()}\n"
        f"Platform: {platform.system()} {platform.machine()}\n"
        "MCP API family requested: 2.x\n"
        "Validation: record actual command outcomes in README.md\n"
        "MCP + LangGraph + local model: not integration-tested yet\n"
    )
    (project / "environment.txt").write_text(details, encoding="utf-8")
    print("Wrote requirements.txt and environment.txt")


if __name__ == "__main__":
    main()

Save as record_environment.py. Run only after successful dependency installation; this records installed versions, not compatibility.

Key takeaways

Common mistakes

References

  1. Virtual Environments and Packages, Python Software Foundation
  2. MCP Python SDK installation, Model Context Protocol
  3. Brief Tour of the Standard Library, Python Software Foundation
  4. Install LangGraph, LangChain
  5. Agent Evals, LangChain

See the full course: Building AI Agents with MCP and LangGraph: Local LLMs to Production