Metadata-Version: 2.4
Name: psr-study-assistant-mcp
Version: 0.3.0
Summary: MCP server that exposes PSR/SDDP power-system tools to Claude Code
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: mcp[cli]>=1.0
Requires-Dist: psr-factory>=5.0.0b111
Requires-Dist: pandas
Requires-Dist: numpy

# psr-study-assistant-mcp

An MCP server that exposes **55 PSR/SDDP power-system tools** to Claude Code through a single unified server. Includes a skill that gives Claude a complete workflow guide — no tool discovery step required.

---

## Prerequisites

| Requirement | Notes |
|---|---|
| Python 3.10+ | Must be on your `PATH` |
| PSR Factory ≥ 5.0.0b111 | Licence must be installed and active on this machine |
| Claude Code | Desktop app or CLI (latest version) |

---

## Installation

### 1 — Install the package

```bash
pip install psr-study-assistant-mcp
```

This installs the `psr-study-assistant-mcp` command on your `PATH`.

### 2 — Register the MCP server

Add the following entry to `~/.claude/settings.json` under `"mcpServers"`:

```json
"mcpServers": {
  "psr-study-assistant-mcp": {
    "command": "psr-study-assistant-mcp"
  }
}
```

> **Claude Desktop** uses `%APPDATA%\Claude\claude_desktop_config.json` instead of `~/.claude/settings.json`. The JSON structure is identical — add the block under `"mcpServers"`.


### 3 - Skill 

You can add the skill direclty on claude. 

---

## Email Notifications (optional)

The server can send email notifications (e.g. when a long simulation finishes). This feature is **entirely optional** — the server works normally without it.

To enable, add your SMTP credentials to the `"env"` block of the MCP server entry in `settings.json`:

```json
"mcpServers": {
  "psr-study-assistant-mcp": {
    "command": "psr-study-assistant-mcp",
    "env": {
      "SMTP_USERNAME": "you@gmail.com",
      "SMTP_PASSWORD": "xxxx xxxx xxxx xxxx"
    }
  }
}
```

The server reads these variables at startup and pre-configures SMTP automatically. You can verify it worked by calling the `get_smtp_status` tool — it will show your address without requiring a `configure_smtp` call.

All available variables (only `SMTP_USERNAME` and `SMTP_PASSWORD` are required to activate email):

| Variable | Default | Description |
|---|---|---|
| `SMTP_USERNAME` | — | Sender email address |
| `SMTP_PASSWORD` | — | App password or SMTP password |
| `SMTP_HOST` | `smtp.gmail.com` | SMTP server hostname |
| `SMTP_PORT` | `587` | SMTP port |
| `SMTP_USE_TLS` | `true` | Set to `"false"` to disable STARTTLS |

### Gmail — generating an App Password

Gmail requires an **App Password** (a 16-character one-time code) instead of your regular account password. App Passwords are only available when 2-Step Verification is active.

**Step 1 — Enable 2-Step Verification** (skip if already on)

1. Go to [myaccount.google.com/security](https://myaccount.google.com/security).
2. Under **"How you sign in to Google"**, click **2-Step Verification** and follow the steps.

**Step 2 — Generate the App Password**

1. Go to [myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords).
2. In the **"App name"** field type `PSR Study Assistant` and click **Create**.
3. Google shows a **16-character code** (format: `xxxx xxxx xxxx xxxx`).  
   Copy it immediately — it will not be shown again.

**Step 3 — Add to settings.json**

Paste the values into the `"env"` block shown above. Spaces in the password are optional — Google shows them for readability but they are not part of the credential.

**Revoking the App Password**

Go to [myaccount.google.com/apppasswords](https://myaccount.google.com/apppasswords), find **PSR Study Assistant**, and click the trash icon. Then remove the `"env"` block from `settings.json`.

**Using a different email provider**

Override the defaults in `"env"`:

```json
"env": {
  "SMTP_HOST": "smtp.office365.com",
  "SMTP_PORT": "587",
  "SMTP_USERNAME": "you@company.com",
  "SMTP_PASSWORD": "your-password",
  "SMTP_USE_TLS": "true"
}
```

**Troubleshooting**

| Error | Likely cause | Fix |
|---|---|---|
| `SMTPAuthenticationError` | Wrong password or 2FA not enabled | Re-generate the App Password |
| `SMTPConnectError` | Firewall blocking port 587 | Set `SMTP_USE_TLS` to `"false"` and `SMTP_PORT` to `"465"` |
| `SMTP not configured` | Variables missing or misspelled | Check `get_smtp_status` — confirm key names in `"env"` |

---

## Using the Plugin

### Starting a session

Invoke the skill to load the full workflow guide:

```
/psr-gateway-complete C:/studies/my_case
```

You can also describe what you want to do:

```
/psr-gateway-complete C:/studies/my_case  scale thermal marginal costs by 10%
```

### Without the skill

All MCP tools are directly callable from any Claude Code session — the skill just provides additional workflow context.

### Setup calls

Every session requires one setup call before domain tools:

| Goal | Setup tool |
|---|---|
| Read / query input data | `setup_input_study(path)` |
| Edit a case (safe copy) | `setup_edit_study(original, target)` |
| Edit in-place (needs user approval) | `setup_edit_study_inplace(path)` |
| Manage output files | `setup_output_case(path)` |

### Example — query thermal plant capacity

```
setup_input_study("C:/studies/case")
get_objects_by_type("ThermalPlant")
get_objects_table("ThermalPlant", ["InstalledCapacity", "MarginalCost"])
```

### Example — scale all thermal costs by 10%

```
setup_edit_study("C:/studies/case", "C:/studies/case_+10pct")
get_objects_table("ThermalPlant", ["MarginalCost"])
scale_dataframe("ThermalPlant Gas1 [3]", "MarginalCost", 10)
# ... repeat for each plant key
save_study("C:/studies/case_+10pct")
```

---

## Tool Reference

All 55 tools across 10 categories:

| Category | Tools |
|---|---|
| **Setup** | `setup_input_study`, `setup_edit_study`, `setup_edit_study_inplace`, `setup_output_case`, `save_study` |
| **Run** | `get_case_version`, `list_available_versions`, `run_psr_model`, `get_run_status`, `read_run_log`, `kill_run` |
| **Discovery** | `discover_objects`, `discover_object_properties`, `get_all_objects`, `get_objects_by_type`, `get_neighbors` |
| **Query** | `get_static_properties`, `get_dynamic_property`, `get_objects_table` |
| **Filter & Aggregate** | `query_by_property_condition`, `find_by_reference`, `count_by_reference`, `sum_property_by_reference` |
| **Input DataFrame** | `input_get_dataframe`, `input_aggregate_dataframe`, `input_filter_dataframe_by_index`, `input_filter_dataframe_by_value` |
| **Binary DataFrame** | `input_get_binary_dataframe`, `input_aggregate_binary_dataframe`, `input_filter_binary_dataframe_by_index`, `input_filter_binary_dataframe_by_value` |
| **Edit** | `modify_static_element`, `rename_element`, `modify_element_code`, `modify_element_key`, `get_dataframe_schema`, `set_dataframe`, `scale_dataframe`, `create_element`, `modify_study_setting` |
| **Output Management** | `list_enabled_outputs`, `list_enabled_outputs_paths`, `get_output_num`, `convert_output`, `change_output_availability` |
| **Output DataFrame** | `df_info`, `df_aggregate`, `df_select_columns`, `df_combine_columns`, `df_filter_by_value`, `df_filter_between`, `df_filter_by_index`, `df_merge`, `df_concat` |

For full descriptions and usage examples see [`TOOLS_GUIDE.md`](TOOLS_GUIDE.md) and the skill at [`skills/psr-gateway-complete/SKILL.md`](skills/psr-gateway-complete/SKILL.md).

---

## Repository Structure

```
PSR-Plugin/
├── pyproject.toml                      # Package: psr-study-assistant-mcp
├── skills/
│   └── psr-gateway-complete/
│       └── SKILL.md                    # Analyst workflow guide (55 tools)
└── src/psr_mcp/
    ├── common.py                       # Study setup + artifact discovery
    ├── input_discover.py               # Query and filter tools
    ├── edit_case.py                    # Edit tools
    ├── outputs.py                      # Output management + DataFrame tools
    ├── execution.py                    # Run/execution tools
    ├── notification.py                 # Email notification tools
    ├── state.py                        # Shared study state
    └── gateway/
        └── server.py                   # Unified gateway — all tools registered here
```

---

## How the Gateway Works

All tools are registered at startup and **immediately callable** — no discovery or activation step is needed.

`find_tools(query)` is an optional helper that ranks the catalog by relevance to a natural-language query. Use it only when you don't know a tool's name.

---

## Adding a New Tool

### 1 — Write the function in the appropriate module

```python
# src/psr_mcp/input_discover.py
def my_new_tool(obj_key: str, value: float) -> str:
    """One-line summary."""
    ...
```

### 2 — Register it in the gateway catalog

```python
# src/psr_mcp/gateway/server.py — CATALOG dict
"my_new_tool": {
    "fn":          _input.my_new_tool,
    "stub":        "Short label (5–10 words)",
    "description": "One-line description shown in find_tools results.",
    "tags":        ["query", "input"],
},
```

Available tags: `setup`, `input`, `edit`, `output`, `discovery`, `query`, `filter`, `aggregate`, `dataframe`, `binary`, `run`.
