school-finance-mcp
by HassanGeesey
README.md
# SchoolFinance MCP Server
A production-grade [Model Context Protocol](https://modelcontextprotocol.io) server for **SchoolFinance**, a single-user desktop school finance application. It exposes the entire school finance domain — students, classes, payments, income/expenses, reports, settings, notifications and a recycle bin — as MCP **tools** and **resources**, backed by a local SQLite database.
- Single administrator (school owner), full access, no authentication
- Synchronous SQLite via `better-sqlite3` — no server, no runtime surprises
- Zod-validated tool inputs and resource data
- Structured JSON responses (`{ success, data }` / `{ success, error }`)
- Soft deletes + recycle bin + audit log
- CSV and JSON export
## Tech stack
| Piece | Choice |
| ---------------- | -------------------------------------------- |
| Language | TypeScript (strict), ESModules |
| MCP SDK | `@modelcontextprotocol/sdk` (`McpServer`) |
| Transport | stdio (spec-compliant JSON-RPC messages) |
| Database | SQLite file (`schoolfinance.db`) + `better-sqlite3` |
| Validation | `zod` v3 |
## Getting started
```bash
npm install
npm run build # compile TypeScript to dist/
npm start # run the compiled server (stdio)
```
For development you can run the server directly with tsx:
```bash
npm run dev
```
The server speaks MCP over stdio, so it is driven by an MCP client. In an MCP client config:
```json
{
"mcpServers": {
"school-finance": {
"command": "node",
"args": ["/path/to/school-finance-mcp/dist/index.js"],
"env": { "DB_PATH": "/path/to/schoolfinance.db" }
}
}
}
```
## Configuration
All configuration is via environment variables (see `.env.example`):
| Variable | Default | Description |
| ------------ | -------------------- | ------------------------------------ |
| `DB_PATH` | `./schoolfinance.db` | SQLite database file location |
| `BACKUP_DIR` | `./backups` | Where `backup_database` writes files |
| `LOG_LEVEL` | `info` | `debug` \| `info` \| `warn` \| `error` |
Logs are written to **stderr** (MCP best practice) so the stdout protocol stream stays clean.
## Tools
### Dashboard
- `get_dashboard_stats` — counts, outstanding balance, monthly revenue/expenses, recent activity
- `get_recent_activity` — last 10 audit-log entries
- `get_financial_summary` — month income/expenses/balance plus previous-month trend
### Students
- `create_student` / `update_student` / `delete_student` (soft) / `restore_student`
- `get_student` — detail incl. payments + balance
- `search_students` / `list_students` — query, filters, sort, pagination
### Classes
- `create_class` / `update_class` / `delete_class` / `assign_student_to_class` / `list_classes`
### Payments
- `record_payment` / `edit_payment` / `delete_payment` (soft)
- `get_payment_history` / `get_unpaid_students` / `get_overdue_students` / `get_student_balance`
### Income & Expenses
- `add_income` / `add_expense` / `update_transaction` / `delete_transaction`
- `get_transaction_history` / `get_monthly_summary`
### Reports
- `generate_financial_report` / `generate_student_balance_report` / `generate_class_report`
- `export_data` — CSV or JSON for any resource
### Settings
- `update_school_info` / `manage_payment_templates` / `set_academic_year`
- `backup_database` / `restore_database`
### Notifications
- `get_pending_reminders` / `mark_reminder_completed`
### Recycle Bin
- `list_deleted_items` / `restore_deleted_item` / `permanently_delete_item`
## Resources
The `schoolfinance://*` namespace exposes live data:
```
schoolfinance://dashboard
schoolfinance://students
schoolfinance://classes
schoolfinance://payments
schoolfinance://income
schoolfinance://expenses
schoolfinance://reports
schoolfinance://settings
schoolfinance://notifications
schoolfinance://recycle-bin
```
## Domain rules
- A student's **balance** = class fee − total active payments. Balances are recalculated whenever payments or class fees change.
- **Overdue** = outstanding balance **and** enrolment older than 30 days (documented heuristic for this single-user app).
- Deleting a class with enrolled students is blocked — reassign students first.
- `delete_student`, `delete_payment` and `delete_transaction` are **soft deletes**; items appear in the recycle bin.
- `income`/`expenses` carry a `deleted_at` column (slightly beyond the spec's table list) because they are soft-deletable and recyclable.
## Database
Schema is created idempotently on startup. Tables: `classes`, `students`, `payments`, `income`, `expenses`, `settings`, `notifications`, `audit_log`. WAL mode is enabled for durability and concurrent access safety.
## Testing
```bash
npm test # spawns the server and speaks real MCP over stdio
```
Tests use a throwaway database in the OS temp directory (see `tests/setup.ts`).
## Project structure
```
src/
├── index.ts # entry point
├── server.ts # McpServer assembly, tool + resource registration
├── config/database.ts # SQLite connection + schema
├── services/ # business logic (audit, students, classes, payments, ...)
├── schemas/ # Zod schemas for inputs and resource data
├── resources/templates/ # report template
├── utils/ # db, date, export, logging, response helpers
└── tools/ # MCP tool registrations
```
## Connecting to MCP Clients
The server runs as a **stdio process** — the client spawns it, sends JSON-RPC messages over stdin, and reads responses from stdout. Below are step-by-step instructions for each client.
### Prerequisites
Build the server first:
```bash
npm install
npm run build # compiles to dist/index.js
```
Find the absolute path:
```bash
realpath dist/index.js # e.g. /home/user/mcp-schoolfinance/dist/index.js
realpath schoolfinance.db # (or create a persistent DB path)
```
### Claude Desktop
1. Edit `claude_desktop_config.json`:
- **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows**: `%APPDATA%\Claude\claude_desktop_config.json`
- **Linux**: `~/.config/Claude/claude_desktop_config.json`
2. Add the server:
```json
{
"mcpServers": {
"school-finance": {
"command": "node",
"args": ["/absolute/path/to/school-finance-mcp/dist/index.js"],
"env": {
"DB_PATH": "/absolute/path/to/school-finance.db"
}
}
}
}
```
3. Restart Claude Desktop. The server will appear in Claude's MCP settings.
### Claude Code (CLI / VS Code extension)
1. Edit `.opencode.jsonc` or `.claude.json` in your project root:
```json
{
"mcp": {
"school-finance": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/school-finance-mcp/dist/index.js"],
"env": { "DB_PATH": "/absolute/path/to/school-finance.db" }
}
}
}
```
2. Or add to `~/.config/.opencode/mcp.json` for all projects.
### Cursor
1. Open Settings → `Cursor Settings` → `MCP` tab (or search "MCP")
2. Click "Add MCP Server"
3. Enter:
- **Name**: `school-finance`
- **Type**: `stdio`
- **Command**: `node /absolute/path/to/school-finance-mcp/dist/index.js`
- **Environment Variables**: add `DB_PATH` → `/absolute/path/to/school-finance.db`
### ChatGPT (Desktop / web with ChatGPT Pro)
ChatGPT's desktop app supports MCP via `mcp_config.json`:
1. Edit `~/.chatgpt/mcp_config.json` (or the config path shown in-app)
2. Add:
```json
{
"mcpServers": {
"school-finance": {
"command": "node",
"args": ["/absolute/path/to/school-finance-mcp/dist/index.js"],
"env": { "DB_PATH": "/absolute/path/to/school-finance.db" }
}
}
}
```
3. Restart the ChatGPT desktop app.
### VS Code (with MCP extension)
1. Install the `modelcontextprotocol.mcp` extension (or use Claude/Copilot with MCP support)
2. Edit `.vscode/mcp.json`:
```json
{
"servers": {
"school-finance": {
"type": "stdio",
"command": "node",
"args": ["/absolute/path/to/school-finance-mcp/dist/index.js"],
"env": { "DB_PATH": "/absolute/path/to/school-finance.db" }
}
}
}
```
### Using with OpenCode
Add to your OpenCode configuration to let the agent use SchoolFinance tools:
```jsonc
// ~/.config/opencode/opencode.jsonc
{
"mcp": [
{
"name": "school-finance",
"command": "node /absolute/path/to/school-finance-mcp/dist/index.js",
"env": { "DB_PATH": "/absolute/path/to/school-finance.db" }
}
]
}
```
### Available Tools
Once connected, AI assistants can call these tools, for example:
```
# Create a class and add students
create_class(name="Grade 10A", year_level=10, fee_amount=1200, description="Morning class")
create_student(name="Ahmed Hassan", email="ahmed@example.com", phone="+252612345678",
address="Mogadishu, Somalia", class_id=1, enrollment_date="2025-09-01")
# Record a payment
record_payment(student_id=1, amount=500, payment_date="2025-09-15",
method="cash", reference="REC-001", notes="Partial payment")
# Check student balance
get_student_balance(student_id=1)
# Generate reports
generate_financial_report(start_date="2025-01-01", end_date="2025-12-31")
export_data(resource="students", format="csv")
```
### Troubleshooting
| Problem | Fix |
|---------|-----|
| Server doesn't appear | Check absolute paths in config, ensure `npm run build` was run |
| Database errors | Ensure `DB_PATH` points to a writable directory, or omit to use `./schoolfinance.db` |
| "Cannot find module" | Run `npm install` in the project directory |
| Logs not visible | MCP clients may suppress stderr; check the client's log viewer |
| Port/resource conflicts | SQLite is file-based, no port conflicts — ensure the DB file is accessible |
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessSyncing