Skip to main content
Glama
amansinghbawa

Employee MCP Server

README.md
# Employee MCP Server

A small [Model Context Protocol](https://modelcontextprotocol.io) server that
exposes an employee directory — backed by SQLite, validated with Pydantic —
as a set of MCP tools and resources. Includes an interactive CLI client for
exercising it without a full MCP-aware host.

## Contents

- [server.py](server.py) — the MCP server: tools, resources, database access
- [models.py](models.py) — Pydantic schemas that validate every tool's input and shape its output
- [seed_db.py](seed_db.py) — one-time script that creates and populates `employees.db`
- [client.py](client.py) — interactive REPL client for calling the server by hand
- [tests/](tests) — pytest suite for the server and models

## Requirements

- Python 3.11+ (developed against 3.14)
- A virtual environment (the project expects one at `.venv`)

## Setup

Create a virtual environment and install dependencies:

```bash
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
```

For running the test suite, install the dev dependencies instead (this
includes everything in `requirements.txt` plus `pytest`):

```bash
.venv/bin/pip install -r requirements-dev.txt
```

Create and seed the database (run once per checkout — it creates
`employees.db` next to `server.py`):

```bash
.venv/bin/python seed_db.py
```

`employees.db` is gitignored, so every fresh checkout needs this step. To
wipe and reseed an existing database:

```bash
.venv/bin/python seed_db.py --force
```

## Data model

Two tables, one employee record per `id`:

- `employees(id, name, role)`
- `skills(id, employee_id, skill)` — many rows per employee, cascade-deleted
  with the employee

Input to every write tool is validated by a Pydantic model in `models.py`
before it reaches the database: `name`/`role` can't be blank, `skills` must
contain at least one non-empty entry, and `employee_id` must be a positive
whole number. A tool called with bad input gets back a clear error message
(e.g. `employee_id must be a positive whole number, got 'abc'`) instead of a
stack trace or a silent failure.

## Running the server

The server speaks MCP over stdio and isn't meant to be run standalone in a
terminal — it's launched as a subprocess by an MCP client. `client.py` does
this for you via `StdioServerParameters`, spawning `server.py` with the
venv's Python interpreter.

To use it from an MCP-aware host (e.g. Claude Desktop, Claude Code), point
the host's MCP config at:

```json
{
  "mcpServers": {
    "employees": {
      "command": "/absolute/path/to/mcp_tool/.venv/bin/python",
      "args": ["/absolute/path/to/mcp_tool/server.py"]
    }
  }
}
```

## Tools

| Tool | Arguments | Description |
|---|---|---|
| `list_employees` | — | List all employees |
| `get_employee` | `employee_id` | Get one employee by ID |
| `search_employees` | `name` | Case-insensitive partial name match |
| `find_employees_by_skill` | `skill` | Case-insensitive exact skill match |
| `add_employee` | `name`, `role`, `skills`, `employee_id?` | Add an employee; ID auto-assigned if omitted |
| `update_employee` | `employee_id`, `name?`, `role?`, `skills?` | Update one or more fields |
| `delete_employee` | `employee_id` | Delete an employee |

Every tool also declares an output schema (an `Employee` or `DeleteResult`
shape), so a schema-aware client sees structured results, not just text.

## Resources

| URI | Description |
|---|---|
| `employees://all` | All employee records as JSON |
| `employees://{employee_id}` | A single employee record as JSON |

## Using the interactive client

`client.py` opens one MCP session and gives you a REPL to call tools and
read resources by hand:

```bash
.venv/bin/python client.py
```

```
> list
> get 101
> search an
> byskill AWS
> add Zara Khan | Data Scientist | Python,SQL
> update 122 | role=Senior Data Scientist | skills=Python,SQL,Spark
> delete 122
> resource employees://all
> resource employees://101
> resources
> tools
> help
> quit
```

- `add` fields are pipe-separated: `name | role | comma,separated,skills [| employee_id]`
- `update` takes `employee_id`, then one or more `field=value` pairs
  (`name=`, `role=`, or `skills=`), pipe-separated
- Tool errors (e.g. validation failures) print inline and drop you back to
  the prompt — the session stays open

## Running tests

```bash
.venv/bin/pip install -r requirements-dev.txt
.venv/bin/python -m pytest tests/
```

Tests point the server at a temporary SQLite database (via `tmp_path` +
`monkeypatch`) and never touch the real `employees.db`.

## Project layout

```
server.py              MCP server: tools, resources, DB access
models.py               Pydantic input/output schemas
seed_db.py              Creates and seeds employees.db
client.py               Interactive REPL client
employees.db            SQLite database (generated, gitignored)
requirements.txt        Runtime dependencies
requirements-dev.txt    Runtime + test dependencies
tests/test_server.py    Server/tool tests
tests/test_models.py    Pydantic model tests
```

Maintenance

ActivityInactive
ResponsivenessNo issues