Difference between revisions of "Module 6"
| (31 intermediate revisions by 5 users not shown) | |||
| Line 1: | Line 1: | ||
| − | __NOTOC__ | + | __NOTOC__ |
| − | Module 6 | + | In Module 6, you will learn the fundamentals of Python, a widely used scripting language, and Regular Expressions, which provide powerful tools for text processing. You will also explore the Model Context Protocol (MCP), a framework that allows software tools to communicate with large language models (LLMs). |
| + | |||
| + | This article contains your assignments for Module 6. | ||
| + | |||
| + | === Learning Goals === | ||
| + | * Regular Expressions | ||
| + | * Python | ||
| + | * MCP Fundamentals | ||
| + | |||
| + | == Using the Wiki == | ||
| + | {{RequiredInstructions|content= | ||
| + | Text enclosed by <syntaxhighlight lang="bash" inline><</syntaxhighlight> and <syntaxhighlight lang="bash" inline>></syntaxhighlight> should be replaced by content unique to you. | ||
| + | |||
| + | '''Example''' | ||
| + | |||
| + | <source lang="bash"> | ||
| + | $ sudo useradd -r -m -c "<My Full Name>" <usernameHere> | ||
| + | </source> | ||
| + | becomes | ||
| + | <source lang="bash"> | ||
| + | $ sudo useradd -r -m -c "Zach Cohn" zcohn | ||
| + | </source> | ||
| + | }} | ||
| + | |||
== Reading == | == Reading == | ||
| Line 6: | Line 29: | ||
The following articles on the online class wiki textbook contain information that will help you complete the assignments. | The following articles on the online class wiki textbook contain information that will help you complete the assignments. | ||
| − | * [[ | + | * [[Python]] |
| − | * [[ | + | * [[Regular Expressions]] |
| + | * [[MCP Fundamentals]] | ||
| + | * [[FAQ - Mod 6]] | ||
== Individual Assignments == | == Individual Assignments == | ||
| − | === | + | === Learn Python and Regular Expressions === |
| + | |||
| + | In Module 6, you will learn a new language: Python. Before starting this assignment, you should look over the [[Python|online textbook article about Python]]. You should be using version 3 (or greater) of Python. Additionally, you may find it helpful to look over the [[Regular Expressions|online textbook article about Regular Expressions]]. | ||
| + | |||
| + | === Write some Regular Expressions === | ||
| + | |||
| + | Please write regular expressions that do each of the following: | ||
| + | |||
| + | # Match the substring "hello world" in a string. | ||
| + | # Match all character strings in an input string that contain three or more consecutive vowels. The regex should match strings containing three or more consecutive vowels, regardless of whether the letters are uppercase, lowercase, or a mixture of both. | ||
| + | # Match an input string that is '''entirely''' a flight code, of the format AA####, where '''AA''' is a two-letter uppercase airline code, and '''####''' is a three- or four-digit flight number. | ||
| + | Examples: | ||
| + | Note: text in ''[[red]]'' is matched by the regular expression. Please note that each line is a '''separate''' test input string. For Regex 3, '''six''' separate test cases are given. | ||
| + | Regex 1: Programmers will often write ''[[hello world]]'' as their first project with a programming language. | ||
| + | Regex 2: The ''[[gooey]]'' peanut butter and jelly sandwich was a ''[[beauty]]''. | ||
| + | Regex 3: ''[[AA312]]'' | ||
| + | ''[[AA1298]]'' | ||
| + | ''[[NW1234]]'' | ||
| + | ''[[US443]]'' | ||
| + | US31344 | ||
| + | AA123 extratext | ||
| + | |||
| + | |||
| + | |||
| + | Save your regexes to text files – one each. | ||
| + | |||
| + | === Baseball Stats Counter === | ||
| + | |||
| + | The St. Louis Cardinals are the most legendary baseball team in the national league. In this exercise, you will be creating a Python script that reads box scores from a file and computes the Cardinals' players' batting averages in a particular season. | ||
| + | |||
| + | ==== Tips and Instructions ==== | ||
| + | |||
| + | * You should write a Python script file to solve this problem. | ||
| + | * You should use a regular expression to parse players' names, at-bats, hits, and runs from the input file. | ||
| + | * You may want to create a class to hold and compute information about each player. | ||
| + | * Your file should take one command-line argument: the path to an input file. If no path is given, your program should print a usage message. | ||
| + | |||
| + | ==== Input ==== | ||
| + | |||
| + | Sample input files may be found here: http://classes.engineering.wustl.edu/cse330/content/cardinals/ | ||
| + | |||
| + | The top of the file contains a citation, and what follows is an entire season of Cardinals Baseball box scores. Each game starts with a title; for example: | ||
| + | |||
| + | === St.Louis Cardinals vs. Chicago Cubs, 1940-04-19 === | ||
| + | |||
| + | What follows are the performance of each player in that game. The format of each line is as follows: | ||
| + | |||
| + | XXX batted # times with # hits and # runs | ||
| + | |||
| + | where '''XXX''' is the player's name (you may assume that the name is unique), and each '''#''' are integers representing the number of "at bats", the number of "hits", and the number of "runs", respectively. | ||
| + | |||
| + | ==== Output ==== | ||
| + | |||
| + | Your script needs to output all players' batting average across the season. A player's batting average is that player's total hits divided by that player's total at-bats throughout the entire season. For example, Johnny Hopp was at bat 149 times in 1940, and he had 41 hits, so his batting average was 0.275. | ||
| + | |||
| + | Each line in the output should have the format: | ||
| + | |||
| + | XXX: #.### | ||
| + | |||
| + | where '''XXX''' is the player's name, and '''#.###''' is the player's batting average, '''rounded''' (not truncated) to three decimal places. | ||
| + | |||
| + | The players should be sorted by batting average, with the highest batting average on top. If two players have the exact same batting average (before rounding occurs), the order between those two players is unimportant. | ||
| + | |||
| + | For example, the correct output for the 1940 season is: | ||
| + | <!-- Jimmy Brown was 0.280 and Ira Hutchinson was 0.125 --> | ||
| + | <source lang="text"> | ||
| + | Pepper Martin: 0.316 | ||
| + | Walker Cooper: 0.316 | ||
| + | Johnny Mize: 0.314 | ||
| + | Ernie Koy: 0.310 | ||
| + | Enos Slaughter: 0.306 | ||
| + | Joe Medwick: 0.304 | ||
| + | Terry Moore: 0.304 | ||
| + | Joe Orengo: 0.286 | ||
| + | Jimmy Brown: 0.280 | ||
| + | Marty Marion: 0.279 | ||
| + | Don Gutteridge: 0.276 | ||
| + | Johnny Hopp: 0.275 | ||
| + | Creepy Crespi: 0.273 | ||
| + | Mickey Owen: 0.265 | ||
| + | Bill DeLancey: 0.250 | ||
| + | Don Padgett: 0.242 | ||
| + | Stu Martin: 0.238 | ||
| + | Eddie Lake: 0.222 | ||
| + | Hal Epps: 0.214 | ||
| + | Lon Warneke: 0.209 | ||
| + | Harry Walker: 0.185 | ||
| + | Max Lanier: 0.179 | ||
| + | Bill McGee: 0.178 | ||
| + | Carl Doyle: 0.174 | ||
| + | Mort Cooper: 0.157 | ||
| + | Clyde Shoun: 0.145 | ||
| + | Carden Gillenwater: 0.130 | ||
| + | Bob Bowman: 0.067 | ||
| + | </source> | ||
| + | |||
| + | === Baseball Stats MCP Extension === | ||
| + | |||
| + | ====Cardinals MCP Server ==== | ||
| + | |||
| + | This project builds a '''custom MCP server''' that lets you explore ''St. Louis Cardinals'' batting data from the '''1930''' and '''1940''' seasons using the text files you made in the Baseball Stats Counter section. | ||
| + | |||
| + | The MCP server reads player batting averages from text files, sorts them, and returns the results through '''Claude Desktop'''. | ||
| + | |||
| + | For all of the assignment, feel free to use your favorite AI tool to help. | ||
| + | |||
| + | ==== What Is an MCP Server? ==== | ||
| + | |||
| + | An '''MCP (Model Context Protocol)''' server is a local tool that '''Claude Desktop''' can talk to directly. | ||
| + | It runs safely inside '''Docker''', allowing Claude to use your Python scripts, such as this Cardinals data processor, through its chat interface. | ||
| + | |||
| + | Learn more: | ||
| + | [https://modelcontextprotocol.io/docs/getting-started/intro Model Context Protocol: Getting Started] | ||
| + | |||
| + | Servers provide the fundamental building blocks for adding context to language models via MCP. These primitives enable interactions between clients, servers, and language models: | ||
| + | |||
| + | * '''Prompts''': Predefined templates or instructions that guide language model interactions | ||
| + | * '''Resources''': Structured data or content that provides additional context to the model | ||
| + | * '''Tools''': Executable functions that allow models to perform actions or retrieve information | ||
| + | |||
| + | Each primitive can be summarized in the following control hierarchy: | ||
| + | |||
| + | ====== Where Prompts, Resources, and Tools Fit in the MCP Server ====== | ||
| + | |||
| + | * '''Prompts''': In the context of this project, prompts are what the user will provide to interact with the MCP server. For example, if you want to fetch the top hitters from a particular season, you would issue a prompt like: | ||
| + | <pre>"Show Johnny Mize’s batting averages for 1930 and 1940."</pre> | ||
| + | |||
| + | The prompt is user-controlled, and the system will interpret it to execute the appropriate action. | ||
| + | |||
| + | * '''Resources''': These are the data files, such as <code>cardinals-1930.txt</code> and <code>cardinals-1940.txt</code>, that your MCP server uses to provide additional context. These text files are '''resources''' that store structured data (player stats like batting averages) which the server can access to provide meaningful results when the user queries it. | ||
| + | |||
| + | * '''Tools''': The server exposes various '''tools''' that perform specific actions, like: | ||
| + | ** <code>get_player_stats</code>: Retrieve and show batting averages for a player. | ||
| + | ** <code>get_team_roster</code>: List all players from a given season. | ||
| + | ** <code>get_top_players</code>: Sort and show the top hitters. | ||
| + | ** <code>search_players</code>: Search for players by name. | ||
| + | |||
| + | Each of these functions is a '''tool''' in the MCP system that the language model can call when responding to prompts from the user. | ||
| + | |||
| + | ==== What Is Docker? ==== | ||
| + | |||
| + | '''Docker''' lets you run software in isolated, self-contained environments called '''containers'''. | ||
| + | A container is like a tiny computer that holds everything your app needs (code, libraries, and dependencies) so it runs the same way on every machine. | ||
| + | |||
| + | Claude uses Docker to safely run MCP tools '''without touching your main system'''. | ||
| + | When you say: | ||
| + | |||
| + | <pre>“Use CardinalsMCP to calculate batting averages.”</pre> | ||
| + | |||
| + | Claude does not execute code directly. It asks Docker to launch your MCP container, which runs the script in isolation. | ||
| + | |||
| + | ==== Goal ==== | ||
| + | |||
| + | You will build a '''Python-based MCP server''' that: | ||
| + | |||
| + | * Reads Cardinals player batting averages from '''1930''' and '''1940''' text files | ||
| + | * Lists team rosters and top hitters | ||
| + | * Lets you search players or compare across seasons | ||
| + | * Runs inside Docker and connects to Claude Desktop | ||
| + | |||
| + | After setup, you will be able to say things like: | ||
| + | |||
| + | <pre>“Use CardinalsMCP to show the top 10 hitters from 1940.”</pre> | ||
| + | <pre>“Get batting averages for Johnny Mize in both 1930 and 1940.”</pre> | ||
| + | |||
| + | Claude will automatically run your tool and show the results. | ||
| + | |||
| + | ==== Project Structure ==== | ||
| + | |||
| + | Your final folder will look like this: | ||
| + | |||
| + | <source lang="text"> | ||
| + | cardinals-mcp/ | ||
| + | ├── Dockerfile | ||
| + | ├── cardinals_server.py | ||
| + | ├── requirements.txt | ||
| + | └── data/ | ||
| + | ├── cardinals-1930.txt | ||
| + | └── cardinals-1940.txt | ||
| + | </source> | ||
| + | |||
| + | The <code>/data</code> directory contains the <code>.txt</code> files you created from the Baseball Stats Counter section, listing player names and batting averages. | ||
| + | If you only printed the results, copy and paste them into <code>.txt</code> files. | ||
| + | |||
| + | ==== Step 1: Prerequisites ==== | ||
| + | |||
| + | You will need: | ||
| + | |||
| + | * [https://www.docker.com/products/docker-desktop/ Docker Desktop] | ||
| + | * [https://claude.ai/download Claude Desktop] (''not the browser version'') | ||
| + | |||
| + | ==== Enable MCP in Docker ==== | ||
| + | |||
| + | # Open '''Docker Desktop'''. | ||
| + | # Go to '''Settings → Beta Features'''. | ||
| + | # Enable '''“Docker MCP Toolkit”'''. | ||
| + | # Click '''Apply & Restart'''. | ||
| + | |||
| + | ===== Connect Claude to Docker ===== | ||
| + | |||
| + | In Docker Desktop, go to '''MCP Toolkit''' → '''Clients → Claude Desktop → Connect'''. | ||
| + | If it says '''“Disconnect”''', you are good to go. | ||
| + | |||
| + | ==== Step 2: Create the Files ==== | ||
| + | |||
| + | ===== <code>cardinals_server.py</code> ===== | ||
| + | |||
| + | This is your main MCP script. It should: | ||
| + | |||
| + | * Load player data from <code>/data/cardinals-1930.txt</code> and <code>/data/cardinals-1940.txt</code> | ||
| + | * Implement at least '''two tools''', such as: | ||
| + | ** <code>get_player_stats</code>: Show batting averages for one player | ||
| + | ** <code>get_team_roster</code>: List all players for a given season (1930 or 1940) | ||
| + | ** <code>get_top_players</code>: Show the top hitters for a given season (by batting average) | ||
| + | ** <code>search_players</code>: Find players by partial name and show their batting averages | ||
| + | |||
| + | Each tool should be implemented as an '''asynchronous function''' decorated with <code>@mcp.tool()</code>. For example, the provided <code>get_player_stats</code> function uses the <code>@mcp.tool()</code> decorator to indicate that it is a tool that can be invoked by Claude. | ||
| + | |||
| + | In this script, you need to: | ||
| + | |||
| + | '''Create MCP Tools:''' | ||
| + | * Implement at least two MCP tools (<code>get_player_stats</code>, <code>get_team_roster</code>, <code>get_top_players</code>, <code>search_players</code>). | ||
| + | * Use the <code>@mcp.tool()</code> decorator to register each function as a tool that Claude can use. | ||
| + | * Your functions should: | ||
| + | ** Retrieve data from the loaded player stats. | ||
| + | ** Perform calculations or sorting (for example, finding the top hitters or listing all players for a season). | ||
| + | ** Return well-formatted strings with the results. | ||
| + | |||
| + | ===== Starter Code ===== | ||
| + | |||
| + | <source lang="python"> | ||
| + | import os | ||
| + | import sys | ||
| + | import logging | ||
| + | import re | ||
| + | from datetime import datetime | ||
| + | from mcp.server.fastmcp import FastMCP | ||
| + | |||
| + | # Configure logging to stderr | ||
| + | logging.basicConfig( | ||
| + | level=logging.INFO, | ||
| + | format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", | ||
| + | stream=sys.stderr, | ||
| + | ) | ||
| + | logger = logging.getLogger("CardinalsMCP-server") | ||
| + | |||
| + | |||
| + | # Initialize MCP server | ||
| + | mcp = FastMCP("CardinalsMCP") | ||
| + | |||
| + | # Data file paths | ||
| + | DATA_DIR = os.path.join(os.path.dirname(__file__), "data") | ||
| + | CARDINALS_1930_FILE = os.path.join(DATA_DIR, "cardinals-1930.txt") | ||
| + | CARDINALS_1940_FILE = os.path.join(DATA_DIR, "cardinals-1940.txt") | ||
| + | |||
| + | # ==== Helper Functions (You DO NOT need to use these) ==== | ||
| + | |||
| + | def load_cardinals_data(): | ||
| + | """Load Cardinals player data from both 1930 and 1940 files.""" | ||
| + | data = {"1930": [], "1940": []} | ||
| + | |||
| + | for year, file_path in [("1930", CARDINALS_1930_FILE), ("1940", CARDINALS_1940_FILE)]: | ||
| + | try: | ||
| + | with open(file_path, 'r', encoding='utf-8') as f: | ||
| + | content = f.read() | ||
| + | data[year] = parse_player_data(content) | ||
| + | except FileNotFoundError: | ||
| + | logger.warning(f"Data file not found: {file_path}") | ||
| + | except Exception as e: | ||
| + | logger.error(f"Error loading {year} data: {e}") | ||
| + | |||
| + | return data | ||
| + | |||
| + | def parse_player_data(content): | ||
| + | """Parse player data from the simplified Cardinals text files.""" | ||
| + | players = [] | ||
| + | |||
| + | for line in content.split('\n'): | ||
| + | line = line.strip() | ||
| + | if not line or line.startswith('#'): | ||
| + | continue | ||
| + | |||
| + | # Parse format: "Player Name: 0.XXX" | ||
| + | if ':' in line: | ||
| + | parts = line.split(':') | ||
| + | if len(parts) == 2: | ||
| + | name = parts[0].strip() | ||
| + | try: | ||
| + | batting_avg = float(parts[1].strip()) | ||
| + | players.append({ | ||
| + | 'name': name, | ||
| + | 'batting_average': batting_avg | ||
| + | }) | ||
| + | except ValueError: | ||
| + | continue | ||
| + | |||
| + | return players | ||
| + | |||
| + | def get_all_players(data): | ||
| + | """Get all unique players from the data.""" | ||
| + | players = set() | ||
| + | for year_data in data.values(): | ||
| + | for player in year_data: | ||
| + | players.add(player['name']) | ||
| + | return sorted(list(players)) | ||
| + | |||
| + | def get_player_by_name(data, player_name): | ||
| + | """Get player data for a specific player.""" | ||
| + | player_data = [] | ||
| + | for year, year_data in data.items(): | ||
| + | for player in year_data: | ||
| + | if player['name'].lower() == player_name.lower(): | ||
| + | player_data.append({ | ||
| + | 'year': year, | ||
| + | 'name': player['name'], | ||
| + | 'batting_average': player['batting_average'] | ||
| + | }) | ||
| + | return player_data | ||
| + | |||
| + | # ==== MCP Tools ==== | ||
| + | |||
| + | # This tool works - use to test your MCP connection. | ||
| + | @mcp.tool() | ||
| + | async def hello(name: str = "world") -> str: | ||
| + | """Return a simple greeting to verify the server is working.""" | ||
| + | return f"Hello CSE 3300 Student, {name}!" | ||
| + | |||
| + | # Only two tools are required here | ||
| + | |||
| + | @mcp.tool() | ||
| + | async def get_player_stats(player: str = "") -> str: | ||
| + | """Get a player's batting averages for both 1930 and 1940 seasons.""" | ||
| + | # TODO: Implement the function to fetch player data using the 'load_cardinals_data' | ||
| + | # and 'get_player_by_name' functions | ||
| + | |||
| + | |||
| + | @mcp.tool() | ||
| + | async def get_team_roster(year: str = "1930") -> str: | ||
| + | """Get the roster of the Cardinals for the given year, listing player names and batting averages.""" | ||
| + | # TODO: Implement the function to fetch and list the team roster for the specified year (1930 or 1940) | ||
| + | |||
| + | |||
| + | @mcp.tool() | ||
| + | async def get_top_players(year: str = "1930", limit: int = 10) -> str: | ||
| + | """Get the top Cardinals players for a specific year, sorted by batting average.""" | ||
| + | # TODO: Implement this function to return the top 'limit' players sorted by their batting averages | ||
| + | |||
| + | |||
| + | @mcp.tool() | ||
| + | async def search_players(search_term: str = "") -> str: | ||
| + | """Search for Cardinals players by name and show their batting averages.""" | ||
| + | # TODO: Implement this function to search for players by name and return their batting averages | ||
| + | |||
| + | # ==== Server Startup ==== | ||
| + | |||
| + | if __name__ == "__main__": | ||
| + | logger.info("Starting Cardinals Historical Data MCP server...") | ||
| + | try: | ||
| + | mcp.run(transport="stdio") | ||
| + | except Exception as e: | ||
| + | logger.error(f"Server error: {e}", exc_info=True) | ||
| + | sys.exit(1) | ||
| + | </source> | ||
| + | |||
| + | You can find an explanation of the hello function in the [[MCP_Fundamentals#Example MCP Tool: hello|MCP Fundamentals]] | ||
| + | |||
| + | ===== <code>Dockerfile</code> ===== | ||
| + | |||
| + | <source lang="dockerfile"> | ||
| + | FROM python:3.11-slim | ||
| + | |||
| + | LABEL maintainer="cardinals_server" | ||
| + | ENV PYTHONUNBUFFERED=1 | ||
| + | |||
| + | WORKDIR /app | ||
| + | |||
| + | # Copy dependencies | ||
| + | COPY requirements.txt /app/requirements.txt | ||
| + | RUN pip install --no-cache-dir -r requirements.txt | ||
| + | |||
| + | # Copy application files | ||
| + | COPY cardinals_server.py /app/cardinals_server.py | ||
| + | COPY data/ /app/data/ | ||
| + | |||
| + | # Create non-root user | ||
| + | RUN useradd -m -u 1000 mcpuser && chown -R mcpuser:mcpuser /app | ||
| + | USER mcpuser | ||
| + | |||
| + | CMD ["python", "/app/cardinals_server.py"] | ||
| + | </source> | ||
| + | |||
| + | ===== <code>requirements.txt</code> ===== | ||
| + | |||
| + | <source lang="text"> | ||
| + | mcp | ||
| + | </source> | ||
| + | |||
| + | That is all you need unless you use extra libraries. | ||
| + | |||
| + | ==== Step 3: Build and Test the Container ==== | ||
| + | |||
| + | Build your Docker image: | ||
| + | |||
| + | <source lang="bash"> | ||
| + | $ docker build -t cardinals-mcp-server:latest . | ||
| + | </source> | ||
| + | |||
| + | Run it: | ||
| + | |||
| + | <source lang="bash"> | ||
| + | $ docker run --rm -it cardinals-mcp-server | ||
| + | </source> | ||
| + | |||
| + | If it launches correctly, you will see something like: | ||
| + | |||
| + | <source lang="text"> | ||
| + | 2025-10-19 20:00:00,000 - CardinalsMCP-server - INFO - Starting Cardinals Historical Data MCP server... | ||
| + | </source> | ||
| + | |||
| + | ==== Step 4: Connect in Claude Desktop ==== | ||
| + | |||
| + | ===== Open Claude Desktop Settings ===== | ||
| + | |||
| + | On macOS, click '''Claude → Settings…''' from the top menu bar. | ||
| + | (''Do not use the settings inside the Claude chat window; you need the app-level settings.'') | ||
| + | |||
| + | This opens the '''Claude Desktop Settings''' panel, separate from your Claude account settings. | ||
| + | |||
| + | ===== Go to Developer Settings ===== | ||
| + | |||
| + | In the Settings window, select the '''Desktop app''' tab in the left sidebar. | ||
| + | Then scroll all the way down to Extensions and select '''Manage'''. | ||
| + | This section manages MCP servers and other developer tools. | ||
| + | |||
| + | Click '''Edit Config''' to open the configuration file. | ||
| + | If you do not have one yet, Claude will create it automatically. | ||
| + | |||
| + | '''Configuration file locations:''' | ||
| + | |||
| + | * macOS: <code>~/Library/Application Support/Claude/claude_desktop_config.json</code> | ||
| + | * Windows: <code>%APPDATA%\Claude\claude_desktop_config.json</code> | ||
| + | |||
| + | ===== Add the MCP Gateway Configuration ===== | ||
| + | |||
| + | Replace (or merge) the contents of the file with the JSON below. | ||
| + | This tells Claude to launch the '''Docker MCP Gateway''', which connects to your custom Cardinals MCP server. | ||
| + | |||
| + | <source lang="json"> | ||
| + | { | ||
| + | "mcpServers": { | ||
| + | "cardinals-mcp-server": { | ||
| + | "command": "docker", | ||
| + | "args": [ | ||
| + | "run", | ||
| + | "--rm", | ||
| + | "-i", | ||
| + | "cardinals-mcp-server:latest" | ||
| + | ] | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | </source> | ||
| + | |||
| + | ===== Restart Claude Desktop ===== | ||
| + | |||
| + | After saving the file: | ||
| + | * Completely quit Claude Desktop (not just close the window). | ||
| + | * Reopen it. | ||
| + | |||
| + | Claude will now start the MCP gateway automatically. | ||
| + | |||
| + | If everything is configured correctly, you will see a small MCP indicator in the lower-right corner of the chat input box. Click it to view all available tools, including your '''Cardinals MCP Server'''. | ||
| − | + | Once connected, you can run the hello tool in the chat of Claude Desktop: | |
| − | + | <pre>"Use CardinalsMCP to say hello"</pre> | |
| − | + | If everything is working correctly, you should see a "Hello CSE 3300 Student" response. | |
| − | + | After you have implemented the MCP tools, you can run commands like this: | |
| − | + | <pre>“Use CardinalsMCP to list the top 10 hitters from 1940.”</pre> | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | == | + | ==== Step 5: Test It Out ==== |
| − | + | Open a new chat in Claude and try: | |
| − | + | <pre>“Use CardinalsMCP to list the 1940 Cardinals roster sorted by batting average.”</pre> | |
| + | <pre>“Show Johnny Mize’s batting averages for 1930 and 1940.”</pre> | ||
| + | <pre>“Who were the top 5 hitters on the 1930 Cardinals?”</pre> | ||
| + | <pre>“Search for players with ‘Martin’ in their name.”</pre> | ||
| − | + | Claude will call your MCP tool, execute your Python script inside Docker, and display formatted results. | |
== Grading == | == Grading == | ||
| − | |||
| − | + | We will be grading the following aspects of your work. There are 70 points total. | |
| + | '''Assignments (including code) must be committed to Github by the end of class on the due date (commit early and often). Failing to commit by the end of class on the due date will result in a 0. ''' | ||
| − | + | Upload your baseball code and a file with your Regular expressions. | |
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | ''' | + | # '''Regular Expressions (15 Points):''' |
| + | #* '''Your regex files should contain your regex (with no delimiters or flags) and nothing else.''' | ||
| + | #* Regular Expression 1 Correct and saved in a file named <code>regex1.txt</code> (5 points) | ||
| + | #* Regular Expression 2 Correct and saved in a file named <code>regex2.txt</code> (5 points) | ||
| + | #* Regular Expression 3 Correct and saved in a file named <code>regex3.txt</code> (5 points) | ||
| − | + | # '''Baseball Stats Counter (25 Points):''' | |
| + | #* Solution is written entirely in Python and saved in a file named <code>baseball.py</code> (5 points) | ||
| + | #*: ''Note: Failing to write your code in Python 3 will result in losing, at a minimum, points for this category.'' | ||
| + | #* Correct usage of one or more regular expressions to parse and extract data from each line of the input file (5 points) | ||
| + | #*: ''Note: You should not be using <code>str.split</code> to extract data.'' | ||
| + | #* Script prints a usage message if a command line argument is not present (3 points) | ||
| + | #*: ''Note: For an example of a usage message, see [https://en.wikipedia.org/wiki/Usage_message#Examples this link.]'' | ||
| + | #* Output is correct for all test cases (12 points) | ||
| + | #*: ''This includes sorting and rounding.'' | ||
| − | # ''' | + | # '''Baseball Stats MCP Extension (30 Points):''' |
| − | #* | + | #* Docker image builds successfully without errors (5 points) |
| − | #* | + | #*: ''MCP server (`cardinals_server.py`) loads player data and returns correct results.'' |
| − | #* | + | #* Includes at least 2 MCP tools (14 points) |
| − | #* | + | #*: ''Each tool must function correctly and return expected output through Claude Desktop.'' |
| − | + | #* README includes 3 different example prompts used in Claude (6 points) | |
| − | + | #*: ''Each example should show the prompt you asked and the MCP’s response.'' | |
| − | #* | + | #* Completion of the AI Reflections for the assignment following the [[AI Best Practices]] (5 points) |
| − | |||
| − | |||
| − | |||
| − | #* | ||
| − | |||
| − | |||
| − | |||
| − | #* | ||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
| − | |||
[[Category:Module 6]] | [[Category:Module 6]] | ||
[[Category:Modules]] | [[Category:Modules]] | ||
Latest revision as of 14:59, 3 November 2026
In Module 6, you will learn the fundamentals of Python, a widely used scripting language, and Regular Expressions, which provide powerful tools for text processing. You will also explore the Model Context Protocol (MCP), a framework that allows software tools to communicate with large language models (LLMs).
This article contains your assignments for Module 6.
Learning Goals
- Regular Expressions
- Python
- MCP Fundamentals
Using the Wiki
Text enclosed by < and > should be replaced by content unique to you.
Example
$ sudo useradd -r -m -c "<My Full Name>" <usernameHere>
becomes
$ sudo useradd -r -m -c "Zach Cohn" zcohn
Reading
The following articles on the online class wiki textbook contain information that will help you complete the assignments.
Individual Assignments
Learn Python and Regular Expressions
In Module 6, you will learn a new language: Python. Before starting this assignment, you should look over the online textbook article about Python. You should be using version 3 (or greater) of Python. Additionally, you may find it helpful to look over the online textbook article about Regular Expressions.
Write some Regular Expressions
Please write regular expressions that do each of the following:
- Match the substring "hello world" in a string.
- Match all character strings in an input string that contain three or more consecutive vowels. The regex should match strings containing three or more consecutive vowels, regardless of whether the letters are uppercase, lowercase, or a mixture of both.
- Match an input string that is entirely a flight code, of the format AA####, where AA is a two-letter uppercase airline code, and #### is a three- or four-digit flight number.
Examples:
Note: text in red is matched by the regular expression. Please note that each line is a separate test input string. For Regex 3, six separate test cases are given. Regex 1: Programmers will often write hello world as their first project with a programming language. Regex 2: The gooey peanut butter and jelly sandwich was a beauty. Regex 3: AA312 AA1298 NW1234 US443 US31344 AA123 extratext
Save your regexes to text files – one each.
Baseball Stats Counter
The St. Louis Cardinals are the most legendary baseball team in the national league. In this exercise, you will be creating a Python script that reads box scores from a file and computes the Cardinals' players' batting averages in a particular season.
Tips and Instructions
- You should write a Python script file to solve this problem.
- You should use a regular expression to parse players' names, at-bats, hits, and runs from the input file.
- You may want to create a class to hold and compute information about each player.
- Your file should take one command-line argument: the path to an input file. If no path is given, your program should print a usage message.
Input
Sample input files may be found here: http://classes.engineering.wustl.edu/cse330/content/cardinals/
The top of the file contains a citation, and what follows is an entire season of Cardinals Baseball box scores. Each game starts with a title; for example:
=== St.Louis Cardinals vs. Chicago Cubs, 1940-04-19 ===
What follows are the performance of each player in that game. The format of each line is as follows:
XXX batted # times with # hits and # runs
where XXX is the player's name (you may assume that the name is unique), and each # are integers representing the number of "at bats", the number of "hits", and the number of "runs", respectively.
Output
Your script needs to output all players' batting average across the season. A player's batting average is that player's total hits divided by that player's total at-bats throughout the entire season. For example, Johnny Hopp was at bat 149 times in 1940, and he had 41 hits, so his batting average was 0.275.
Each line in the output should have the format:
XXX: #.###
where XXX is the player's name, and #.### is the player's batting average, rounded (not truncated) to three decimal places.
The players should be sorted by batting average, with the highest batting average on top. If two players have the exact same batting average (before rounding occurs), the order between those two players is unimportant.
For example, the correct output for the 1940 season is:
Pepper Martin: 0.316
Walker Cooper: 0.316
Johnny Mize: 0.314
Ernie Koy: 0.310
Enos Slaughter: 0.306
Joe Medwick: 0.304
Terry Moore: 0.304
Joe Orengo: 0.286
Jimmy Brown: 0.280
Marty Marion: 0.279
Don Gutteridge: 0.276
Johnny Hopp: 0.275
Creepy Crespi: 0.273
Mickey Owen: 0.265
Bill DeLancey: 0.250
Don Padgett: 0.242
Stu Martin: 0.238
Eddie Lake: 0.222
Hal Epps: 0.214
Lon Warneke: 0.209
Harry Walker: 0.185
Max Lanier: 0.179
Bill McGee: 0.178
Carl Doyle: 0.174
Mort Cooper: 0.157
Clyde Shoun: 0.145
Carden Gillenwater: 0.130
Bob Bowman: 0.067
Baseball Stats MCP Extension
Cardinals MCP Server
This project builds a custom MCP server that lets you explore St. Louis Cardinals batting data from the 1930 and 1940 seasons using the text files you made in the Baseball Stats Counter section.
The MCP server reads player batting averages from text files, sorts them, and returns the results through Claude Desktop.
For all of the assignment, feel free to use your favorite AI tool to help.
What Is an MCP Server?
An MCP (Model Context Protocol) server is a local tool that Claude Desktop can talk to directly. It runs safely inside Docker, allowing Claude to use your Python scripts, such as this Cardinals data processor, through its chat interface.
Learn more: Model Context Protocol: Getting Started
Servers provide the fundamental building blocks for adding context to language models via MCP. These primitives enable interactions between clients, servers, and language models:
- Prompts: Predefined templates or instructions that guide language model interactions
- Resources: Structured data or content that provides additional context to the model
- Tools: Executable functions that allow models to perform actions or retrieve information
Each primitive can be summarized in the following control hierarchy:
Where Prompts, Resources, and Tools Fit in the MCP Server
- Prompts: In the context of this project, prompts are what the user will provide to interact with the MCP server. For example, if you want to fetch the top hitters from a particular season, you would issue a prompt like:
"Show Johnny Mize’s batting averages for 1930 and 1940."
The prompt is user-controlled, and the system will interpret it to execute the appropriate action.
- Resources: These are the data files, such as
cardinals-1930.txtandcardinals-1940.txt, that your MCP server uses to provide additional context. These text files are resources that store structured data (player stats like batting averages) which the server can access to provide meaningful results when the user queries it.
- Tools: The server exposes various tools that perform specific actions, like:
get_player_stats: Retrieve and show batting averages for a player.get_team_roster: List all players from a given season.get_top_players: Sort and show the top hitters.search_players: Search for players by name.
Each of these functions is a tool in the MCP system that the language model can call when responding to prompts from the user.
What Is Docker?
Docker lets you run software in isolated, self-contained environments called containers. A container is like a tiny computer that holds everything your app needs (code, libraries, and dependencies) so it runs the same way on every machine.
Claude uses Docker to safely run MCP tools without touching your main system. When you say:
“Use CardinalsMCP to calculate batting averages.”
Claude does not execute code directly. It asks Docker to launch your MCP container, which runs the script in isolation.
Goal
You will build a Python-based MCP server that:
- Reads Cardinals player batting averages from 1930 and 1940 text files
- Lists team rosters and top hitters
- Lets you search players or compare across seasons
- Runs inside Docker and connects to Claude Desktop
After setup, you will be able to say things like:
“Use CardinalsMCP to show the top 10 hitters from 1940.”
“Get batting averages for Johnny Mize in both 1930 and 1940.”
Claude will automatically run your tool and show the results.
Project Structure
Your final folder will look like this:
cardinals-mcp/
├── Dockerfile
├── cardinals_server.py
├── requirements.txt
└── data/
├── cardinals-1930.txt
└── cardinals-1940.txt
The /data directory contains the .txt files you created from the Baseball Stats Counter section, listing player names and batting averages.
If you only printed the results, copy and paste them into .txt files.
Step 1: Prerequisites
You will need:
- Docker Desktop
- Claude Desktop (not the browser version)
Enable MCP in Docker
- Open Docker Desktop.
- Go to Settings → Beta Features.
- Enable “Docker MCP Toolkit”.
- Click Apply & Restart.
Connect Claude to Docker
In Docker Desktop, go to MCP Toolkit → Clients → Claude Desktop → Connect. If it says “Disconnect”, you are good to go.
Step 2: Create the Files
cardinals_server.py
This is your main MCP script. It should:
- Load player data from
/data/cardinals-1930.txtand/data/cardinals-1940.txt - Implement at least two tools, such as:
get_player_stats: Show batting averages for one playerget_team_roster: List all players for a given season (1930 or 1940)get_top_players: Show the top hitters for a given season (by batting average)search_players: Find players by partial name and show their batting averages
Each tool should be implemented as an asynchronous function decorated with @mcp.tool(). For example, the provided get_player_stats function uses the @mcp.tool() decorator to indicate that it is a tool that can be invoked by Claude.
In this script, you need to:
Create MCP Tools:
- Implement at least two MCP tools (
get_player_stats,get_team_roster,get_top_players,search_players). - Use the
@mcp.tool()decorator to register each function as a tool that Claude can use. - Your functions should:
- Retrieve data from the loaded player stats.
- Perform calculations or sorting (for example, finding the top hitters or listing all players for a season).
- Return well-formatted strings with the results.
Starter Code
import os
import sys
import logging
import re
from datetime import datetime
from mcp.server.fastmcp import FastMCP
# Configure logging to stderr
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
stream=sys.stderr,
)
logger = logging.getLogger("CardinalsMCP-server")
# Initialize MCP server
mcp = FastMCP("CardinalsMCP")
# Data file paths
DATA_DIR = os.path.join(os.path.dirname(__file__), "data")
CARDINALS_1930_FILE = os.path.join(DATA_DIR, "cardinals-1930.txt")
CARDINALS_1940_FILE = os.path.join(DATA_DIR, "cardinals-1940.txt")
# ==== Helper Functions (You DO NOT need to use these) ====
def load_cardinals_data():
"""Load Cardinals player data from both 1930 and 1940 files."""
data = {"1930": [], "1940": []}
for year, file_path in [("1930", CARDINALS_1930_FILE), ("1940", CARDINALS_1940_FILE)]:
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
data[year] = parse_player_data(content)
except FileNotFoundError:
logger.warning(f"Data file not found: {file_path}")
except Exception as e:
logger.error(f"Error loading {year} data: {e}")
return data
def parse_player_data(content):
"""Parse player data from the simplified Cardinals text files."""
players = []
for line in content.split('\n'):
line = line.strip()
if not line or line.startswith('#'):
continue
# Parse format: "Player Name: 0.XXX"
if ':' in line:
parts = line.split(':')
if len(parts) == 2:
name = parts[0].strip()
try:
batting_avg = float(parts[1].strip())
players.append({
'name': name,
'batting_average': batting_avg
})
except ValueError:
continue
return players
def get_all_players(data):
"""Get all unique players from the data."""
players = set()
for year_data in data.values():
for player in year_data:
players.add(player['name'])
return sorted(list(players))
def get_player_by_name(data, player_name):
"""Get player data for a specific player."""
player_data = []
for year, year_data in data.items():
for player in year_data:
if player['name'].lower() == player_name.lower():
player_data.append({
'year': year,
'name': player['name'],
'batting_average': player['batting_average']
})
return player_data
# ==== MCP Tools ====
# This tool works - use to test your MCP connection.
@mcp.tool()
async def hello(name: str = "world") -> str:
"""Return a simple greeting to verify the server is working."""
return f"Hello CSE 3300 Student, {name}!"
# Only two tools are required here
@mcp.tool()
async def get_player_stats(player: str = "") -> str:
"""Get a player's batting averages for both 1930 and 1940 seasons."""
# TODO: Implement the function to fetch player data using the 'load_cardinals_data'
# and 'get_player_by_name' functions
@mcp.tool()
async def get_team_roster(year: str = "1930") -> str:
"""Get the roster of the Cardinals for the given year, listing player names and batting averages."""
# TODO: Implement the function to fetch and list the team roster for the specified year (1930 or 1940)
@mcp.tool()
async def get_top_players(year: str = "1930", limit: int = 10) -> str:
"""Get the top Cardinals players for a specific year, sorted by batting average."""
# TODO: Implement this function to return the top 'limit' players sorted by their batting averages
@mcp.tool()
async def search_players(search_term: str = "") -> str:
"""Search for Cardinals players by name and show their batting averages."""
# TODO: Implement this function to search for players by name and return their batting averages
# ==== Server Startup ====
if __name__ == "__main__":
logger.info("Starting Cardinals Historical Data MCP server...")
try:
mcp.run(transport="stdio")
except Exception as e:
logger.error(f"Server error: {e}", exc_info=True)
sys.exit(1)
You can find an explanation of the hello function in the MCP Fundamentals
Dockerfile
FROM python:3.11-slim
LABEL maintainer="cardinals_server"
ENV PYTHONUNBUFFERED=1
WORKDIR /app
# Copy dependencies
COPY requirements.txt /app/requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Copy application files
COPY cardinals_server.py /app/cardinals_server.py
COPY data/ /app/data/
# Create non-root user
RUN useradd -m -u 1000 mcpuser && chown -R mcpuser:mcpuser /app
USER mcpuser
CMD ["python", "/app/cardinals_server.py"]
requirements.txt
mcp
That is all you need unless you use extra libraries.
Step 3: Build and Test the Container
Build your Docker image:
$ docker build -t cardinals-mcp-server:latest .
Run it:
$ docker run --rm -it cardinals-mcp-server
If it launches correctly, you will see something like:
2025-10-19 20:00:00,000 - CardinalsMCP-server - INFO - Starting Cardinals Historical Data MCP server...
Step 4: Connect in Claude Desktop
Open Claude Desktop Settings
On macOS, click Claude → Settings… from the top menu bar. (Do not use the settings inside the Claude chat window; you need the app-level settings.)
This opens the Claude Desktop Settings panel, separate from your Claude account settings.
Go to Developer Settings
In the Settings window, select the Desktop app tab in the left sidebar. Then scroll all the way down to Extensions and select Manage. This section manages MCP servers and other developer tools.
Click Edit Config to open the configuration file. If you do not have one yet, Claude will create it automatically.
Configuration file locations:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
Add the MCP Gateway Configuration
Replace (or merge) the contents of the file with the JSON below. This tells Claude to launch the Docker MCP Gateway, which connects to your custom Cardinals MCP server.
{
"mcpServers": {
"cardinals-mcp-server": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"cardinals-mcp-server:latest"
]
}
}
}
Restart Claude Desktop
After saving the file:
- Completely quit Claude Desktop (not just close the window).
- Reopen it.
Claude will now start the MCP gateway automatically.
If everything is configured correctly, you will see a small MCP indicator in the lower-right corner of the chat input box. Click it to view all available tools, including your Cardinals MCP Server.
Once connected, you can run the hello tool in the chat of Claude Desktop:
"Use CardinalsMCP to say hello"
If everything is working correctly, you should see a "Hello CSE 3300 Student" response.
After you have implemented the MCP tools, you can run commands like this:
“Use CardinalsMCP to list the top 10 hitters from 1940.”
Step 5: Test It Out
Open a new chat in Claude and try:
“Use CardinalsMCP to list the 1940 Cardinals roster sorted by batting average.”
“Show Johnny Mize’s batting averages for 1930 and 1940.”
“Who were the top 5 hitters on the 1930 Cardinals?”
“Search for players with ‘Martin’ in their name.”
Claude will call your MCP tool, execute your Python script inside Docker, and display formatted results.
Grading
We will be grading the following aspects of your work. There are 70 points total.
Assignments (including code) must be committed to Github by the end of class on the due date (commit early and often). Failing to commit by the end of class on the due date will result in a 0.
Upload your baseball code and a file with your Regular expressions.
- Regular Expressions (15 Points):
- Your regex files should contain your regex (with no delimiters or flags) and nothing else.
- Regular Expression 1 Correct and saved in a file named
regex1.txt(5 points) - Regular Expression 2 Correct and saved in a file named
regex2.txt(5 points) - Regular Expression 3 Correct and saved in a file named
regex3.txt(5 points)
- Baseball Stats Counter (25 Points):
- Solution is written entirely in Python and saved in a file named
baseball.py(5 points)- Note: Failing to write your code in Python 3 will result in losing, at a minimum, points for this category.
- Correct usage of one or more regular expressions to parse and extract data from each line of the input file (5 points)
- Note: You should not be using
str.splitto extract data.
- Note: You should not be using
- Script prints a usage message if a command line argument is not present (3 points)
- Note: For an example of a usage message, see this link.
- Output is correct for all test cases (12 points)
- This includes sorting and rounding.
- Solution is written entirely in Python and saved in a file named
- Baseball Stats MCP Extension (30 Points):
- Docker image builds successfully without errors (5 points)
- MCP server (`cardinals_server.py`) loads player data and returns correct results.
- Includes at least 2 MCP tools (14 points)
- Each tool must function correctly and return expected output through Claude Desktop.
- README includes 3 different example prompts used in Claude (6 points)
- Each example should show the prompt you asked and the MCP’s response.
- Completion of the AI Reflections for the assignment following the AI Best Practices (5 points)
- Docker image builds successfully without errors (5 points)