Metadata-Version: 2.4
Name: pyxirr-mcp-server
Version: 0.2.0
Summary: An MCP server exposing pyxirr financial calculation functions as tools
Project-URL: Homepage, https://github.com/b2impact-com/.github-private/tree/main/pyxirr-mcp-server
Project-URL: Repository, https://github.com/b2impact-com/.github-private
Project-URL: Issues, https://github.com/b2impact-com/.github-private/issues
Author: b2impact
License-Expression: MIT
License-File: LICENSE
Keywords: finance,irr,mcp,npv,pyxirr,xirr
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Requires-Dist: mcp>=1.2.0
Requires-Dist: pydantic>=2.0
Requires-Dist: pyxirr>=0.10.0
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# PyXIRR MCP Server

<!-- mcp-name: io.github.b2impact-com/pyxirr-mcp-server -->

A Model Context Protocol (MCP) server that exposes [pyxirr](https://github.com/Anexen/pyxirr) financial calculation functions as tools for use with GitHub Copilot and other MCP clients.

## Features

This MCP server provides the following financial calculation tools:

### Rate of Return Functions
- **xirr** - Extended Internal Rate of Return for irregular cash flows
- **irr** - Internal Rate of Return for periodic cash flows
- **mirr** - Modified Internal Rate of Return

### Net Present/Future Value Functions
- **xnpv** - Net Present Value for irregular cash flows
- **npv** - Net Present Value for periodic cash flows
- **xnfv** - Net Future Value for irregular cash flows
- **nfv** - Net Future Value for periodic cash flows

### Time Value of Money Functions
- **fv** - Future Value
- **pv** - Present Value
- **pmt** - Payment calculation
- **nper** - Number of periods
- **rate** - Interest rate calculation

### Loan Analysis Functions
- **ipmt** - Interest portion of a payment
- **ppmt** - Principal portion of a payment
- **cumipmt** - Cumulative interest paid
- **cumprinc** - Cumulative principal paid

## Installation

### Prerequisites
- Python 3.10 or higher
- pip or uv package manager

### Install from source

```bash
cd pyxirr-mcp-server
pip install -e .
```

Or with uv:
```bash
cd pyxirr-mcp-server
uv pip install -e .
```

## Configuration

### GitHub Copilot (VS Code) - Recommended

Create a `.vscode/mcp.json` file in your workspace (this is included in the project):

```json
{
  "servers": {
    "pyxirr": {
      "type": "stdio",
      "command": "python",
      "args": ["-m", "pyxirr_mcp_server.server"],
      "cwd": "${workspaceFolder}/src"
    }
  }
}
```

This follows the [VS Code MCP developer guidelines](https://code.visualstudio.com/api/extension-guides/ai/mcp).

**Alternative: Global VS Code settings**

Add to your VS Code `settings.json`:

```json
{
  "github.copilot.chat.mcpServers": {
    "pyxirr": {
      "command": "python",
      "args": ["-m", "pyxirr_mcp_server.server"],
      "cwd": "/path/to/pyxirr-mcp-server/src"
    }
  }
}
```

### Claude Desktop

Add to your Claude Desktop configuration (`claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "pyxirr": {
      "command": "python",
      "args": ["-m", "pyxirr_mcp_server.server"],
      "cwd": "/path/to/pyxirr-mcp-server/src"
    }
  }
}
```

## Usage Examples

Once configured, you can ask GitHub Copilot to perform financial calculations:

### XIRR Calculation
> "Calculate the XIRR for an investment of $10,000 on January 1, 2020, with returns of $5,750 on March 1, 2020, $4,250 on October 30, 2020, and $3,250 on February 15, 2021"

### Mortgage Payment
> "What's the monthly payment for a $300,000 mortgage at 6.5% annual rate over 30 years?"

### Investment Future Value
> "If I invest $500 per month for 20 years at 7% annual return, what will be the future value?"

### NPV Analysis
> "Calculate the NPV at 10% discount rate for an investment of $50,000 with annual returns of $15,000, $18,000, $20,000, and $22,000"

## Cash Flow Conventions

**Important:** By convention in financial calculations:
- **Negative values** represent money going OUT (investments, deposits, payments)
- **Positive values** represent money coming IN (returns, income, withdrawals)

## API Reference

All tools use camelCase parameter names following [VS Code MCP guidelines](https://code.visualstudio.com/api/extension-guides/ai/mcp).

### xirr(dates, amounts, guess?)
Calculate the Internal Rate of Return for irregular cash flows.

### xnpv(rate, dates, amounts)
Calculate the Net Present Value for irregular cash flows.

### irr(amounts, guess?)
Calculate the Internal Rate of Return for periodic cash flows.

### npv(rate, amounts, startFromZero?)
Calculate the Net Present Value for periodic cash flows.

### mirr(amounts, financeRate, reinvestRate)
Calculate the Modified Internal Rate of Return.

### fv(rate, nper, pmt, pv, pmtAtBeginning?)
Calculate the Future Value.

### pv(rate, nper, pmt, fv?, pmtAtBeginning?)
Calculate the Present Value.

### pmt(rate, nper, pv, fv?, pmtAtBeginning?)
Calculate the periodic Payment.

### nper(rate, pmt, pv, fv?, pmtAtBeginning?)
Calculate the Number of Periods.

### rate(nper, pmt, pv, fv?, pmtAtBeginning?, guess?)
Calculate the interest Rate per period.

### ipmt(rate, per, nper, pv, fv?, pmtAtBeginning?)
Calculate the Interest portion of a specific payment.

### ppmt(rate, per, nper, pv, fv?, pmtAtBeginning?)
Calculate the Principal portion of a specific payment.

### cumipmt(rate, nper, pv, startPeriod, endPeriod, pmtAtBeginning?)
Calculate Cumulative Interest paid between periods.

### cumprinc(rate, nper, pv, startPeriod, endPeriod, pmtAtBeginning?)
Calculate Cumulative Principal paid between periods.

### nfv(rate, nper, amounts)
Calculate Net Future Value for uneven periodic payments.

### xnfv(rate, dates, amounts)
Calculate Net Future Value for irregular cash flows.

## License

MIT

## Publishing

Releases of this package are automated by [`publish-pyxirr-mcp.yml`](../.github/workflows/publish-pyxirr-mcp.yml). The workflow runs three sequential, gated stages:

1. **test** — runs `pytest`. Required for everything downstream.
2. **pypi-publish** — builds and uploads to PyPI using a project-scoped API token (`PYPI_API_TOKEN` secret).
3. **mcp-publish** — registers `server.json` with the MCP registry using `mcp-publisher` and GitHub OIDC.

### Releasing a new version

1. Bump the version in **three places** (they must match — the workflow enforces it):
   - `pyxirr-mcp-server/pyproject.toml` → `[project].version`
   - `pyxirr-mcp-server/server.json` → top-level `version` and `packages[0].version`
2. Merge to `main`.
3. Tag and push:
   ```bash
   git tag pyxirr-v0.2.0
   git push origin pyxirr-v0.2.0
   ```
4. The workflow runs end-to-end. Watch it under Actions → "Publish pyxirr-mcp-server".

### One-time setup

#### PyPI API token

Trusted Publishing (OIDC) is not usable in this repo because the `b2impact-com` GitHub organisation is invisible to PyPI's anonymous GitHub API lookup (likely Enterprise Managed Users). The fallback is a project-scoped PyPI API token, gated by the `pypi` environment with required reviewers.

**First publish (bootstrap, account-scoped token):**

1. Create a PyPI account if needed and verify you can log in at <https://pypi.org/>.
2. Generate an **account-scoped** API token at <https://pypi.org/manage/account/token/>. (Project scope is not available until the project exists on PyPI.)
3. In this repo: Settings → Environments → `pypi` → Add secret `PYPI_API_TOKEN` with the token value. **Scope the secret to the `pypi` environment**, not the repo, so it is only readable while the environment's required reviewer has approved the run.
4. Run the publish workflow (push a `pyxirr-v0.2.0` tag). Approve the environment gate.

**After the first successful publish (lock down the token):**

5. On PyPI: revoke the account-scoped token.
6. Generate a new token **scoped to the `pyxirr-mcp-server` project only**.
7. Update the `PYPI_API_TOKEN` environment secret with the new value.

Rotate the project-scoped token periodically (e.g. yearly) or immediately if the secret is ever exposed.

#### GitHub Actions environments

Two environments gate the release. Create both under Settings → Environments, with the exact names below, and add **required reviewers** on each:

| Environment    | Used by job     | Secret                |
| -------------- | --------------- | --------------------- |
| `pypi`         | `pypi-publish`  | `PYPI_API_TOKEN`      |
| `mcp-registry` | `mcp-publish`   | `MCP_REGISTRY_URL`    |

Keeping them separate means PyPI credentials and the internal registry URL are never loaded into the same job, and each stage requires its own human approval click.

#### Security hardening (required before first release)

Configure these in repo Settings — they are not version-controlled and must be applied manually:

1. **Required reviewers on both environments** (`pypi` and `mcp-registry`). Every publish stage then requires a human approval click. With token-based auth this is the **primary** mitigation against secret misuse — without it, any workflow run that reaches a publish job can read the secret.
2. **Scope each secret to its environment**, not the repo: `PYPI_API_TOKEN` on `pypi`, `MCP_REGISTRY_URL` on `mcp-registry`. This way neither secret is readable from any other workflow, job, branch, or PR.
3. **Tag protection rule for `pyxirr-v*`** (Settings → Rules → Rulesets, target Tags). Restricts who can push release tags.
4. **Branch protection on `main`**: require PR review + status checks (incl. `pyxirr-mcp-server-tests` from `ci.yml`) before merge.
5. **Dependabot is configured** (`.github/dependabot.yml`) to weekly bump GitHub Actions and Python deps. Review and merge those PRs promptly; the workflow uses `mcp-publisher` pinned via the `MCP_PUBLISHER_VERSION` env in the workflow file.
6. **Pre-release audit**: the `build` job already lists every file in the wheel + sdist and computes SHA256s. Inspect the workflow log on the first release to confirm only expected files ship.

#### MCP registry

The target registry URL is supplied via the **environment secret `MCP_REGISTRY_URL`** on the `mcp-registry` environment (Settings → Environments → `mcp-registry` → Add secret). It points to an internal organisational registry, so it is stored as a secret (masked in workflow logs) and scoped to the publish environment only — not as a repo-wide secret or variable. The workflow fails fast if the secret is missing.

The registry must be configured to accept GitHub OIDC tokens. The server name `io.github.b2impact-com/pyxirr-mcp-server` matches the OIDC-granted namespace `io.github.{org}/*`.

### Ownership verification

PyPI ownership is verified by the MCP registry via the `mcp-name:` marker in this README (line 3). The marker is shipped inside the wheel because hatchling includes `README.md`. Do not remove it.

## Credits

This MCP server is powered by [pyxirr](https://github.com/Anexen/pyxirr), a Rust-powered collection of financial functions.
