Skip to content

Lab 03: MCP Server Implementation

Elementary Due: 2026-03-24
  • Implement an MCP server with FastMCP (Tool + Resource + Prompt — all three primitives)
  • Validate the server with MCP Inspector
  • Apply input validation and path traversal defense
  • Configure filesystem + Git MCP servers

MCP is a protocol that allows AI coding agents to communicate with external tools and data sources in a standardized way. Capabilities are exposed through three primitives — Tool (invoked by the model), Resource (controlled by the application), and Prompt (selected by the user).

AI Coding Agent
MCP Client
MCP Server (Tool + Resource + Prompt)
Filesystem / Git / Custom API

1. MCP Server Configuration — settings.json

Section titled “1. MCP Server Configuration — settings.json”

Add built-in servers to your AI coding CLI’s configuration file.

Config file: ~/.claude/settings.json (or project-level .claude/settings.json)

{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/home/[student-id]/lab-03"
]
},
"git": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-git",
"--repository",
"/home/[student-id]/lab-03"
]
}
}
}

2. Environment Setup and Server Verification

Section titled “2. Environment Setup and Server Verification”
  1. Project Structure and Environment Setup

    Terminal window
    mkdir -p lab-03-mcp && cd lab-03-mcp
    python -m venv .venv
    source .venv/bin/activate
    # Using uv (recommended)
    uv add "mcp[cli]" fastmcp
    # Using pip
    pip install fastmcp
    lab-03-mcp/
    ├── custom_server.py # FastMCP server (Tool + Resource + Prompt)
    ├── settings.json # MCP configuration
    └── test_sample.py # Tests to run via the custom server's run_pytest
  2. Check Built-in Server List

    Terminal window
    claude mcp list
    # filesystem npx -y @modelcontextprotocol/server-filesystem ...
    # git npx -y @modelcontextprotocol/server-git ...
  3. Test the Filesystem Server

    Terminal window
    claude "Show me the list of files in the current directory."
    # The list_directory tool from the filesystem server should be called
  4. Test the Git Server

    Terminal window
    git init /home/[student-id]/lab-03
    claude "Summarize the last 3 commit messages."

Implement a server using FastMCP that includes all three primitives (Tool + Resource + Prompt).

custom_server.py
import os
import subprocess
import sys
from fastmcp import FastMCP
mcp = FastMCP("lab03-custom", description="Lab 03 Practice Server")
# === Allowed path configuration (path traversal defense) ===
ALLOWED_BASE = os.path.realpath(os.path.dirname(__file__))
def _validate_path(path: str) -> str:
"""Defends against path traversal attacks. Blocks access outside the allowed directory."""
real_path = os.path.realpath(path)
if not real_path.startswith(ALLOWED_BASE):
raise ValueError(
f"Access denied: {path} — outside allowed scope ({ALLOWED_BASE})"
)
return real_path
# === Tool 1: Run pytest ===
@mcp.tool()
def run_pytest(path: str) -> str:
"""Runs pytest in the specified directory and returns the results.
Args:
path: Directory path where pytest should be run
"""
safe_path = _validate_path(path)
try:
result = subprocess.run(
["python", "-m", "pytest", safe_path, "-v", "--tb=short"],
capture_output=True,
text=True,
timeout=60,
)
return result.stdout + result.stderr
except subprocess.TimeoutExpired:
return "ERROR: pytest execution timed out (60 seconds)"
except Exception as e:
print(f"[ERROR] {e}", file=sys.stderr)
return f"ERROR: {e}"
# === Tool 2: Count lines ===
@mcp.tool()
def count_lines(file_path: str) -> str:
"""Returns the number of lines in a file.
Args:
file_path: Path to the file to count lines in
"""
safe_path = _validate_path(file_path)
try:
with open(safe_path, "r") as f:
lines = f.readlines()
return f"{len(lines)} lines"
except FileNotFoundError:
return f"ERROR: File not found — {file_path}"
except Exception as e:
print(f"[ERROR] {e}", file=sys.stderr)
return f"ERROR: {e}"
# === Resource: Project statistics ===
@mcp.resource("project://stats")
def project_stats() -> str:
"""Returns file statistics for the project directory."""
py_files = []
for root, _dirs, files in os.walk(ALLOWED_BASE):
for f in files:
if f.endswith(".py"):
py_files.append(os.path.join(root, f))
total_lines = 0
for fp in py_files:
try:
with open(fp) as f:
total_lines += len(f.readlines())
except Exception:
pass
return (
f"Python files: {len(py_files)}\n"
f"Total lines: {total_lines}\n"
f"Project path: {ALLOWED_BASE}"
)
# === Prompt: Code review ===
@mcp.prompt()
def code_review(file_path: str) -> str:
"""A structured prompt template for requesting a code review.
Args:
file_path: Path to the file to review
"""
return (
f"Please code review the following file: {file_path}\n\n"
f"Review criteria:\n"
f"1. Security vulnerabilities (path traversal, command injection, input validation)\n"
f"2. Error handling (exception handling, safe failures)\n"
f"3. Code quality (readability, naming, structure)\n"
f"4. Testability (is the design easy to test?)"
)
if __name__ == "__main__":
mcp.run()

Add the custom server to settings.json.

{
"mcpServers": {
"filesystem": { "...": "..." },
"git": { "...": "..." },
"custom": {
"command": "python",
"args": ["/home/[student-id]/lab-03-mcp/custom_server.py"]
}
}
}
Terminal window
# Run Inspector
npx @modelcontextprotocol/inspector python custom_server.py
# Go to http://localhost:6274 in your browser, then:
# 1. tools/list → verify run_pytest, count_lines
# 2. resources/list → verify project://stats
# 3. prompts/list → verify code_review
# 4. tools/call → run run_pytest and check the result
# 5. Capture screenshots (for assignment submission)
Terminal window
claude "Run pytest in the lab-03-mcp directory."
# The run_pytest tool should be called

Submit a PR to assignments/lab-03/[student-id]/:

  • settings.json — Configuration file with filesystem, git, and custom servers registered
  • custom_server.py — FastMCP-based implementation with all three primitives (Tool + Resource + Prompt)
  • test_sample.py — Test file to be run via the custom server’s run_pytest (minimum 3 tests)
  • Inspector screenshots — Confirming tools/list, resources/list, prompts/list
  • README.md — Verification results for each MCP server, security validation (path traversal blocking test), troubleshooting notes