Skip to content

Commit 247bdce

Browse files
authored
Merge pull request #3 from madebygps/main
Adds debugging instructions, config, and htp mcp example
2 parents 8a06b95 + 3c403d6 commit 247bdce

File tree

6 files changed

+271
-28
lines changed

6 files changed

+271
-28
lines changed

.vscode/launch.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"version": "0.2.0",
3+
"configurations": [
4+
{
5+
"name": "Attach to MCP Server",
6+
"type": "debugpy",
7+
"request": "attach",
8+
"connect": {
9+
"host": "localhost",
10+
"port": 5678
11+
},
12+
"pathMappings": [
13+
{
14+
"localRoot": "${workspaceFolder}",
15+
"remoteRoot": "${workspaceFolder}"
16+
}
17+
]
18+
}
19+
]
20+
}

.vscode/mcp.json

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,32 @@
77
"args": [
88
"run",
99
"main.py"
10-
]
10+
],
11+
"env": {
12+
"PYTHONUNBUFFERED": "1"
13+
}
14+
},
15+
"expenses-mcp-debug": {
16+
"type": "stdio",
17+
"command": "uv",
18+
"cwd": "${workspaceFolder}",
19+
"args": [
20+
"run",
21+
"--",
22+
"python",
23+
"-m",
24+
"debugpy",
25+
"--listen",
26+
"0.0.0.0:5678",
27+
"main.py"
28+
],
29+
"env": {
30+
"PYTHONUNBUFFERED": "1"
31+
}
32+
},
33+
"expenses-mcp-http": {
34+
"type": "http",
35+
"url": "http://localhost:8000/mcp"
1136
}
1237
},
1338
"inputs": []

README.md

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,20 @@ This repository implements a **minimal MCP expense tracker**.
44

55
The Model Context Protocol (MCP) is an open standard that enables LLMs to connect to external data sources and tools.
66

7-
## Getting Started
7+
## Table of Contents
88

9-
### Environment Setup
9+
- [Getting Started](#getting-started)
10+
- [Environment Setup](#environment-setup)
11+
- [Run the MCP Server in VS Code](#run-the-mcp-server-in-vs-code)
12+
- [GitHub Copilot Chat Integration](#github-copilot-chat-integration)
13+
- [MCP Inspector](#mcp-inspector)
14+
- [Debugging](#debugging)
15+
- [Debugging with VS Code and debugpy](#debugging-with-vs-code-and-debugpy)
16+
- [Testing with MCP Inspector](#testing-with-mcp-inspector)
17+
- [Contributing](#contributing)
18+
19+
20+
## Environment Setup
1021

1122
#### 1. GitHub Codespaces
1223

@@ -31,57 +42,56 @@ If you prefer a plain local environment, use **uv** for dependency management:
3142
uv sync
3243
```
3344

34-
### Run the MCP Server in VS Code
35-
36-
1. Open `.vscode/mcp.json` in the editor
37-
2. Click the **Start** button (▶) above the server name `expenses-mcp`
38-
3. Confirm in the output panel that the server is running
39-
40-
### GitHub Copilot Chat Integration
45+
## Using the Expenses MCP Server
4146

42-
Make sure the MCP server is running, then:
47+
### Run the MCP Server in VS Code with GitHub Copilot (Codespace/Local Dev Container/Local)
4348

49+
1. Open `.vscode/mcp.json` in the editor
50+
1. Click the **Start** button (▶) above the server name `expenses-mcp`
51+
1. Confirm in the output panel that the server is running
4452
1. Open the GitHub Copilot Chat panel (bottom right, or via Command Palette: `GitHub Copilot: Focus Chat`)
45-
2. Click the **Tools** icon (wrench) at the bottom of the chat panel
46-
3. Ensure `expenses-mcp` is selected in the list of available tools
47-
4. Ask Copilot to invoke the tool:
53+
1. Click the **Tools** icon (wrench) at the bottom of the chat panel
54+
1. Ensure `expenses-mcp` is selected in the list of available tools
55+
1. Ask Copilot to invoke the tool:
4856
- "Use add_expense to record a $12 lunch today paid with visa"
49-
- "Read the expenses resource"
5057

5158
### MCP Inspector
5259

53-
The MCP Inspector is a browser-based visual testing and debugging tool for MCP servers.
60+
The MCP Inspector is a browser-based visual testing and debugging tool for MCP servers. At the moment it does not work great with non STDIO MCP Servers when working inside of a Codespace.
5461

55-
**Launch the inspector in GitHub Codespaces:**
62+
#### Launch the MCP Inspector and connect to STDIO MCP Server in GitHub Codespace
5663

5764
1. Run the following command in the terminal:
5865
```bash
59-
.devcontainer/launch-inspector.sh
66+
.devcontainer/launch-inspector-codespace.sh
6067
```
61-
6268
2. Note the **Inspector Proxy Address** and **Session Token** from the terminal output
6369

6470
3. In the **Ports** view, set port **6277** to **PUBLIC** visibility
6571

6672
4. Access the Inspector UI and configure:
67-
- **Transport Type**: `SSE`
68-
- **Inspector Proxy Address**: (from terminal output)
69-
- **Proxy Session Token**: (from terminal output)
73+
- **Transport Type**: `STDIO`
7074
- **Command**: `uv`
7175
- **Arguments**: `run main.py`
76+
- Expand configuration area
77+
- **Inspector Proxy Address**: (from terminal output)
78+
- **Proxy Session Token**: (from terminal output)
7279

73-
**Launch the inspector inside of a Dev Container:**
80+
#### Launch the MCP Inspector and connect to STDIO MCP Server in VS Code and Dev Container
7481

7582
1. Run the following command in the terminal:
7683
```bash
77-
HOST=0.0.0.0 DANGEROUSLY_OMIT_AUTH=true npx -y @modelcontextprotocol/inspector uv run main.py
84+
HOST=0.0.0.0 DANGEROUSLY_OMIT_AUTH=true npx -y @modelcontextprotocol/inspector
7885
```
79-
2. Open `http://localhost:6274` in your browser
80-
3. The Inspector should now connect to your MCP server
86+
1. Open `http://localhost:6274` in your browser
87+
1. Access the Inspector UI and configure:
88+
- **Transport Type**: `STDIO`
89+
- **Command**: `uv`
90+
- **Arguments**: `run main.py`
8191

8292
> **Note:** `HOST=0.0.0.0` is required in devcontainer environments to bind the Inspector to all network interfaces, allowing proper communication between the UI and proxy server. `DANGEROUSLY_OMIT_AUTH=true` disables authentication - only use in trusted development environments.
8393
84-
**Launch the inspector locally without Dev Container:**
94+
#### Launch the inspector locally:**
8595

8696
1. Run the following command in the terminal:
8797
```bash
@@ -90,6 +100,42 @@ The MCP Inspector is a browser-based visual testing and debugging tool for MCP s
90100
2. The Inspector will automatically open in your browser at `http://localhost:6274`
91101

92102

103+
---
104+
105+
## Debugging
106+
107+
You can attach the VS Code debugger to the running MCP server to set breakpoints and inspect code execution.
108+
109+
### Debugging STDIO MCP server with GitHub Copilot in Codespace/Dev Container/locally
110+
111+
112+
1. Open `.vscode/mcp.json` in the editor
113+
114+
2. Start the **expenses-mcp-debug** server (instead of expenses-mcp)
115+
116+
3. In VS Code, open the Run and Debug panel (Ctrl+Shift+D / Cmd+Shift+D)
117+
118+
4. Select **"Attach to MCP Server"** from the dropdown and click the play button (or press F5)
119+
120+
5. In GitHub Copilot Chat, make sure **expenses-mcp-debug** is selected in the tools menu
121+
122+
6. Set breakpoints in `main.py` and use the server from GitHub Copilot Chat. Breakpoints will be hit when tools are invoked.
123+
124+
### Debugging HTTP MCP server with GitHub Copilot in Codespace/Dev Container/locally
125+
126+
1. Run the HTTP MCP Server in the terminal with `uv run -- python -m debugpy --listen 0.0.0.0:5678 main_http.py`
127+
128+
1. Open `.vscode/mcp.json` in the editor
129+
130+
1. Start the **expenses-mcp-debug** server (instead of expenses-mcp)
131+
132+
1. In VS Code, open the Run and Debug panel (Ctrl+Shift+D / Cmd+Shift+D)
133+
134+
1. Select **"Attach to MCP Server"** from the dropdown and click the play button (or press F5)
135+
136+
1. In GitHub Copilot Chat, make sure **expenses-mcp-debug** is selected in the tools menu
137+
138+
1. Set breakpoints in `main_http.py` and use the server from GitHub Copilot Chat. Breakpoints will be hit when tools are invoked.
93139

94140
---
95141

main_http.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import csv
2+
import logging
3+
from datetime import date
4+
from enum import Enum
5+
from pathlib import Path
6+
from typing import Annotated
7+
8+
from fastmcp import FastMCP
9+
10+
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(message)s")
11+
logger = logging.getLogger("ExpensesMCP")
12+
13+
14+
SCRIPT_DIR = Path(__file__).parent
15+
EXPENSES_FILE = SCRIPT_DIR / "expenses.csv"
16+
17+
18+
mcp = FastMCP("Expenses Tracker")
19+
20+
21+
class PaymentMethod(Enum):
22+
AMEX = "amex"
23+
VISA = "visa"
24+
CASH = "cash"
25+
26+
27+
class Category(Enum):
28+
FOOD = "food"
29+
TRANSPORT = "transport"
30+
ENTERTAINMENT = "entertainment"
31+
SHOPPING = "shopping"
32+
GADGET = "gadget"
33+
OTHER = "other"
34+
35+
36+
@mcp.tool
37+
async def add_expense(
38+
date: Annotated[date, "Date of the expense in YYYY-MM-DD format"],
39+
amount: Annotated[float, "Positive numeric amount of the expense"],
40+
category: Annotated[Category, "Category label"],
41+
description: Annotated[str, "Human-readable description of the expense"],
42+
payment_method: Annotated[PaymentMethod, "Payment method used"],
43+
):
44+
"""Add a new expense to the expenses.csv file."""
45+
if amount <= 0:
46+
return "Error: Amount must be positive"
47+
48+
date_iso = date.isoformat()
49+
logger.info(f"Adding expense: ${amount} for {description} on {date_iso}")
50+
51+
try:
52+
file_exists = EXPENSES_FILE.exists()
53+
54+
with open(EXPENSES_FILE, "a", newline="", encoding="utf-8") as file:
55+
writer = csv.writer(file)
56+
57+
if not file_exists:
58+
writer.writerow(
59+
["date", "amount", "category", "description", "payment_method"]
60+
)
61+
62+
writer.writerow(
63+
[date_iso, amount, category.value, description, payment_method.name]
64+
)
65+
66+
return f"Successfully added expense: ${amount} for {description} on {date_iso}"
67+
68+
except Exception as e:
69+
logger.error(f"Error adding expense: {str(e)}")
70+
return "Error: Unable to add expense"
71+
72+
73+
@mcp.resource("resource://expenses")
74+
async def get_expenses_data():
75+
"""Get raw expense data from CSV file"""
76+
logger.info("Expenses data accessed")
77+
78+
try:
79+
with open(EXPENSES_FILE, "r", newline="", encoding="utf-8") as file:
80+
reader = csv.DictReader(file)
81+
expenses_data = list(reader)
82+
83+
csv_content = f"Expense data ({len(expenses_data)} entries):\n\n"
84+
for expense in expenses_data:
85+
csv_content += (
86+
f"Date: {expense['date']}, "
87+
f"Amount: ${expense['amount']}, "
88+
f"Category: {expense['category']}, "
89+
f"Description: {expense['description']}, "
90+
f"Payment: {expense['payment_method']}\n"
91+
)
92+
93+
return csv_content
94+
95+
except FileNotFoundError:
96+
logger.error("Expenses file not found")
97+
return "Error: Expense data unavailable"
98+
except Exception as e:
99+
logger.error(f"Error reading expenses: {str(e)}")
100+
return "Error: Unable to retrieve expense data"
101+
102+
103+
@mcp.prompt
104+
def create_expense_prompt(
105+
date: str,
106+
amount: float,
107+
category: str,
108+
description: str,
109+
payment_method: str
110+
) -> str:
111+
112+
"""Generate a prompt to add a new expense using the add_expense tool."""
113+
114+
logger.info(f"Expense prompt created for: {description}")
115+
116+
return f"""
117+
Please add the following expense:
118+
- Date: {date}
119+
- Amount: ${amount}
120+
- Category: {category}
121+
- Description: {description}
122+
- Payment Method: {payment_method}
123+
Use the `add_expense` tool to record this transaction.
124+
"""
125+
126+
127+
if __name__ == "__main__":
128+
logger.info("MCP Expenses server starting (HTTP mode on port 8000)")
129+
# Run in HTTP mode on port 8000
130+
mcp.run(transport="http", host="0.0.0.0", port=8000)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ readme = "README.md"
66
requires-python = ">=3.13"
77
dependencies = [
88
"fastmcp>=2.12.5",
9+
"debugpy>=1.8.0",
910
]

uv.lock

Lines changed: 22 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)