portfolio-analytics-mcp 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- portfolio_analytics_mcp-0.2.0/.github/workflows/ci.yml +33 -0
- portfolio_analytics_mcp-0.2.0/.github/workflows/release.yml +54 -0
- portfolio_analytics_mcp-0.2.0/.gitignore +64 -0
- portfolio_analytics_mcp-0.2.0/.python-version +1 -0
- portfolio_analytics_mcp-0.2.0/CONTRIBUTING.md +134 -0
- portfolio_analytics_mcp-0.2.0/LICENSE +21 -0
- portfolio_analytics_mcp-0.2.0/PKG-INFO +133 -0
- portfolio_analytics_mcp-0.2.0/README.md +108 -0
- portfolio_analytics_mcp-0.2.0/docs/USER_GUIDE.md +225 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/__init__.py +1 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/__main__.py +5 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/auth.py +111 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/concurrency.py +78 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/justetf/__init__.py +1 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/justetf/analysis.py +330 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/justetf/client.py +246 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/justetf/data/etfs.json +281 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/justetf/parser.py +470 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/justetf/registry.py +214 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/justetf/search.py +134 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/justetf/service.py +76 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/justetf/tools.py +370 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/portfolio/__init__.py +1 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/portfolio/resolve.py +179 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/portfolio/risk.py +400 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/portfolio/tools.py +117 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/server.py +91 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/yahoo/__init__.py +1 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/yahoo/analytics.py +516 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/yahoo/data.py +339 -0
- portfolio_analytics_mcp-0.2.0/finance_mcp/yahoo/tools.py +599 -0
- portfolio_analytics_mcp-0.2.0/pyproject.toml +116 -0
- portfolio_analytics_mcp-0.2.0/render.yaml +16 -0
- portfolio_analytics_mcp-0.2.0/skill/portfolio-analytics/SKILL.md +170 -0
- portfolio_analytics_mcp-0.2.0/tests/__init__.py +1 -0
- portfolio_analytics_mcp-0.2.0/tests/conftest.py +109 -0
- portfolio_analytics_mcp-0.2.0/tests/fakes.py +293 -0
- portfolio_analytics_mcp-0.2.0/tests/justetf/__init__.py +1 -0
- portfolio_analytics_mcp-0.2.0/tests/justetf/test_analysis.py +66 -0
- portfolio_analytics_mcp-0.2.0/tests/justetf/test_client.py +209 -0
- portfolio_analytics_mcp-0.2.0/tests/justetf/test_parser.py +110 -0
- portfolio_analytics_mcp-0.2.0/tests/justetf/test_registry.py +31 -0
- portfolio_analytics_mcp-0.2.0/tests/justetf/test_search.py +46 -0
- portfolio_analytics_mcp-0.2.0/tests/justetf/test_service.py +90 -0
- portfolio_analytics_mcp-0.2.0/tests/justetf/test_tools.py +152 -0
- portfolio_analytics_mcp-0.2.0/tests/portfolio/__init__.py +1 -0
- portfolio_analytics_mcp-0.2.0/tests/portfolio/test_resolve.py +126 -0
- portfolio_analytics_mcp-0.2.0/tests/portfolio/test_risk.py +144 -0
- portfolio_analytics_mcp-0.2.0/tests/portfolio/test_tools.py +45 -0
- portfolio_analytics_mcp-0.2.0/tests/test_auth.py +60 -0
- portfolio_analytics_mcp-0.2.0/tests/test_server.py +176 -0
- portfolio_analytics_mcp-0.2.0/tests/yahoo/__init__.py +1 -0
- portfolio_analytics_mcp-0.2.0/tests/yahoo/test_analytics.py +203 -0
- portfolio_analytics_mcp-0.2.0/tests/yahoo/test_data.py +158 -0
- portfolio_analytics_mcp-0.2.0/tests/yahoo/test_tools.py +212 -0
- portfolio_analytics_mcp-0.2.0/uv.lock +1082 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
# Cancel superseded runs of the same branch/PR.
|
|
13
|
+
concurrency:
|
|
14
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
15
|
+
cancel-in-progress: true
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
ci-checks:
|
|
19
|
+
name: CI checks
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
23
|
+
# Python version comes from .python-version (kept in sync with
|
|
24
|
+
# PYTHON_VERSION in render.yaml).
|
|
25
|
+
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
|
|
26
|
+
with:
|
|
27
|
+
enable-cache: true
|
|
28
|
+
- run: uv sync --locked
|
|
29
|
+
# Same command as locally: format-check (ruff + prettier), lint,
|
|
30
|
+
# type check and tests with the coverage minimum.
|
|
31
|
+
- run: uv run poe ci-checks
|
|
32
|
+
- name: Build package
|
|
33
|
+
run: uv build
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Pushing a tag like v1.2.3 runs the checks, builds version 1.2.3 (taken from
|
|
4
|
+
# the tag by hatch-vcs) and publishes it to PyPI.
|
|
5
|
+
on:
|
|
6
|
+
push:
|
|
7
|
+
tags: ["v*"]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
name: Check and build
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
18
|
+
with:
|
|
19
|
+
fetch-depth: 0 # full history and tags, so hatch-vcs sees the tag
|
|
20
|
+
persist-credentials: false
|
|
21
|
+
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
|
|
22
|
+
- run: uv sync --locked
|
|
23
|
+
- run: uv run poe ci-checks
|
|
24
|
+
- run: uv build
|
|
25
|
+
# Guard against a tag on the wrong commit or an unexpected version.
|
|
26
|
+
- name: Check version matches tag
|
|
27
|
+
run: |
|
|
28
|
+
expected="$(uv run --with packaging python -c \
|
|
29
|
+
'from packaging.version import Version; import sys; print(Version(sys.argv[1]))' \
|
|
30
|
+
"${GITHUB_REF_NAME#v}")"
|
|
31
|
+
ls dist
|
|
32
|
+
test -f "dist/portfolio_analytics_mcp-${expected}-py3-none-any.whl" \
|
|
33
|
+
|| { echo "Built version does not match tag ${GITHUB_REF_NAME}"; exit 1; }
|
|
34
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
35
|
+
with:
|
|
36
|
+
name: dist
|
|
37
|
+
path: dist/
|
|
38
|
+
|
|
39
|
+
publish:
|
|
40
|
+
name: Publish to PyPI
|
|
41
|
+
needs: build
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
environment:
|
|
44
|
+
name: pypi
|
|
45
|
+
url: https://pypi.org/project/portfolio-analytics-mcp/
|
|
46
|
+
permissions:
|
|
47
|
+
id-token: write # trusted publishing: PyPI accepts this job's identity, no token stored
|
|
48
|
+
steps:
|
|
49
|
+
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
50
|
+
with:
|
|
51
|
+
name: dist
|
|
52
|
+
path: dist/
|
|
53
|
+
- uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 # v7
|
|
54
|
+
- run: uv publish --trusted-publishing always
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
*.egg-info/
|
|
24
|
+
.installed.cfg
|
|
25
|
+
*.egg
|
|
26
|
+
|
|
27
|
+
# Unit test / coverage reports
|
|
28
|
+
htmlcov/
|
|
29
|
+
.tox/
|
|
30
|
+
.coverage
|
|
31
|
+
.coverage.*
|
|
32
|
+
.cache
|
|
33
|
+
nosetests.xml
|
|
34
|
+
coverage.xml
|
|
35
|
+
*.cover
|
|
36
|
+
.hypothesis/
|
|
37
|
+
.pytest_cache/
|
|
38
|
+
|
|
39
|
+
# Environments
|
|
40
|
+
.env
|
|
41
|
+
.venv
|
|
42
|
+
env/
|
|
43
|
+
venv/
|
|
44
|
+
ENV/
|
|
45
|
+
env.bak/
|
|
46
|
+
venv.bak/
|
|
47
|
+
|
|
48
|
+
# mypy
|
|
49
|
+
.mypy_cache/
|
|
50
|
+
.dmypy.json
|
|
51
|
+
dmypy.json
|
|
52
|
+
|
|
53
|
+
# IDEs
|
|
54
|
+
.idea/
|
|
55
|
+
|
|
56
|
+
# Jupyter Notebook
|
|
57
|
+
.ipynb_checkpoints
|
|
58
|
+
|
|
59
|
+
# macOS
|
|
60
|
+
.DS_Store
|
|
61
|
+
|
|
62
|
+
# Local tooling
|
|
63
|
+
.vscode/
|
|
64
|
+
.ruff_cache/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for your interest! Bug reports, fixes and new tools are welcome.
|
|
4
|
+
Please open an issue first for larger changes, so we can agree on the
|
|
5
|
+
approach.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
You need [uv](https://docs.astral.sh/uv/) and Python 3.12. Formatting
|
|
10
|
+
Markdown and YAML also needs [Node.js](https://nodejs.org) (Prettier runs
|
|
11
|
+
through `npx`, nothing to install).
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git clone https://github.com/RugSec/portfolio-analytics-mcp.git
|
|
15
|
+
cd portfolio-analytics-mcp
|
|
16
|
+
uv sync # creates .venv with runtime + dev dependencies
|
|
17
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Tasks
|
|
21
|
+
|
|
22
|
+
Tasks run with [poe](https://poethepoet.natn.io/) (`poe -h` lists them):
|
|
23
|
+
|
|
24
|
+
| Command | What it does |
|
|
25
|
+
| ------------------ | ------------------------------------------------------------------ |
|
|
26
|
+
| `poe ci-checks` | Everything CI runs, read-only: format-check, lint, typecheck, test |
|
|
27
|
+
| `poe test` | Tests with coverage; fails below 80% |
|
|
28
|
+
| `poe format` | Format Python (ruff) and Markdown/YAML (Prettier), sort imports |
|
|
29
|
+
| `poe fix` | Format, then lint |
|
|
30
|
+
| `poe format-check` | Check formatting without writing files |
|
|
31
|
+
| `poe lint` | Lint with ruff (style, docstrings, type hints) |
|
|
32
|
+
| `poe typecheck` | Type check with basedpyright |
|
|
33
|
+
| `poe check` | Quick check: lint + typecheck |
|
|
34
|
+
|
|
35
|
+
**Run `poe ci-checks` before opening a pull request.** GitHub Actions runs
|
|
36
|
+
the same command on Python 3.12.
|
|
37
|
+
|
|
38
|
+
To try the tools interactively, use the MCP inspector:
|
|
39
|
+
`mcp dev finance_mcp/server.py`. To check that Yahoo is reachable from your
|
|
40
|
+
machine:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
uv run python -c "from finance_mcp.yahoo.tools import get_quote; print(get_quote(['AAPL']))"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Project layout
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
finance_mcp/
|
|
50
|
+
server.py builds the FastMCP server and registers all tools
|
|
51
|
+
auth.py API-key middleware for the HTTP transport
|
|
52
|
+
yahoo/
|
|
53
|
+
analytics.py return/risk maths: pure pandas/numpy, no network
|
|
54
|
+
data.py yfinance downloads, currencies, FX, error classification
|
|
55
|
+
tools.py Yahoo MCP tools
|
|
56
|
+
justetf/
|
|
57
|
+
client.py polite HTTP: rate limit, retries, disk cache, ISIN check
|
|
58
|
+
parser.py profile page HTML -> dict (stable data-testid hooks)
|
|
59
|
+
service.py fetch + parse + cache, the tools' single entry point
|
|
60
|
+
registry.py local name/ticker -> ISIN registry (seed + learned)
|
|
61
|
+
search.py ETF search: ISIN, registry, then Yahoo fallback
|
|
62
|
+
analysis.py ETF portfolio maths: pure functions, no network
|
|
63
|
+
tools.py justETF MCP tools
|
|
64
|
+
data/etfs.json seed registry of popular ETFs
|
|
65
|
+
portfolio/
|
|
66
|
+
resolve.py ISIN -> Yahoo symbols via justETF listings
|
|
67
|
+
risk.py whole-portfolio risk in one base currency
|
|
68
|
+
tools.py portfolio MCP tools
|
|
69
|
+
tests/ mirrors finance_mcp/ (tests/yahoo, tests/justetf, ...)
|
|
70
|
+
conftest.py fixtures that fake Yahoo and justETF
|
|
71
|
+
fakes.py synthetic prices, pages and profiles
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The pattern in each package: network code (`data.py`, `client.py`) is
|
|
75
|
+
separate from pure logic (`analytics.py`, `parser.py`, `analysis.py`), and
|
|
76
|
+
`tools.py` only adapts inputs and outputs for the MCP client.
|
|
77
|
+
|
|
78
|
+
## Conventions
|
|
79
|
+
|
|
80
|
+
- **Tests never touch the network.** Fake Yahoo and justETF with the
|
|
81
|
+
fixtures in `tests/conftest.py` (`fake_yahoo`, `fake_yf`, `registry_env`)
|
|
82
|
+
or with `monkeypatch`. Put a module's tests in the matching file under
|
|
83
|
+
`tests/`, e.g. `finance_mcp/justetf/parser.py` in
|
|
84
|
+
`tests/justetf/test_parser.py`.
|
|
85
|
+
- **Coverage stays at or above 80%** (branch coverage); CI fails below it.
|
|
86
|
+
- **Type hints on every function**, checked by basedpyright.
|
|
87
|
+
- **Docstrings on every function** in Google style: a one-line summary, then
|
|
88
|
+
`Args:`, `Returns:` and `Raises:` sections as they apply. Test functions
|
|
89
|
+
only need the one-line summary. Ruff enforces this.
|
|
90
|
+
- **Tool docstrings are read by the LLM.** They are the tool's description,
|
|
91
|
+
so say when to use the tool, what it needs and what comes back, and keep
|
|
92
|
+
them short.
|
|
93
|
+
- **Tools return errors, they don't raise them.** Return
|
|
94
|
+
`YahooError(...).to_dict()` (with an `error_code` from the
|
|
95
|
+
[user guide](docs/USER_GUIDE.md#errors)) or `{"error": ...}`, so the agent
|
|
96
|
+
can explain the failure or try another tool.
|
|
97
|
+
- **Be polite to data sources.** New justETF requests go through
|
|
98
|
+
`client._get` (rate limit + retries) and the cache; never retry a 403/429.
|
|
99
|
+
|
|
100
|
+
## Dependencies
|
|
101
|
+
|
|
102
|
+
Dependencies are declared in `pyproject.toml` and locked in `uv.lock`.
|
|
103
|
+
|
|
104
|
+
- `mcp` is pinned below 2 because the code uses the v1 `FastMCP` API.
|
|
105
|
+
- `pandas` is pinned exactly, with a matching `pandas-stubs` in the dev
|
|
106
|
+
group: bump both together.
|
|
107
|
+
- Dependabot opens monthly update PRs for Python packages and GitHub
|
|
108
|
+
Actions.
|
|
109
|
+
|
|
110
|
+
## Releases
|
|
111
|
+
|
|
112
|
+
The version comes from the git tag ([hatch-vcs](https://github.com/ofek/hatch-vcs)),
|
|
113
|
+
so there is no version number to edit in `pyproject.toml`. To release from an
|
|
114
|
+
up-to-date `main`:
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
git tag -a v0.2.0 -m "v0.2.0"
|
|
118
|
+
git push origin v0.2.0
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The [Release workflow](.github/workflows/release.yml) runs `poe ci-checks`,
|
|
122
|
+
builds the package, checks that its version matches the tag and publishes it
|
|
123
|
+
to [PyPI](https://pypi.org/project/portfolio-analytics-mcp/) with trusted
|
|
124
|
+
publishing (no token stored). Users running `uvx portfolio-analytics-mcp@latest`
|
|
125
|
+
get it the next time the server starts.
|
|
126
|
+
|
|
127
|
+
- Use [semantic versioning](https://semver.org): patch for fixes, minor for
|
|
128
|
+
new tools or fields, major for removed or renamed tools.
|
|
129
|
+
- PyPI never accepts the same version twice, even after deleting it. Fix a
|
|
130
|
+
bad release by tagging the next patch version.
|
|
131
|
+
- Builds between tags get a development version such as
|
|
132
|
+
`0.2.1.dev3+g1a2b3c4`; PyPI rejects these, so only tags can be published.
|
|
133
|
+
- `skill/portfolio-analytics/SKILL.md` has its own `version`: bump it only
|
|
134
|
+
when you change the skill and upload it to Agensi again.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ruggiero Seccia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: portfolio-analytics-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: MCP server for portfolio analysis with Yahoo Finance and justETF data
|
|
5
|
+
Project-URL: Homepage, https://github.com/RugSec/portfolio-analytics-mcp
|
|
6
|
+
Project-URL: Documentation, https://github.com/RugSec/portfolio-analytics-mcp/blob/main/docs/USER_GUIDE.md
|
|
7
|
+
Project-URL: Issues, https://github.com/RugSec/portfolio-analytics-mcp/issues
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: etf,finance,investing,justetf,mcp,portfolio,yfinance
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
14
|
+
Requires-Python: ==3.12.*
|
|
15
|
+
Requires-Dist: beautifulsoup4>=4.15.0
|
|
16
|
+
Requires-Dist: httpx>=0.28.1
|
|
17
|
+
Requires-Dist: mcp[cli]<2,>=1.9.0
|
|
18
|
+
Requires-Dist: numpy>=2.0
|
|
19
|
+
Requires-Dist: pandas==3.0.6
|
|
20
|
+
Requires-Dist: pydantic>=2.7
|
|
21
|
+
Requires-Dist: starlette>=0.40
|
|
22
|
+
Requires-Dist: uvicorn>=0.30
|
|
23
|
+
Requires-Dist: yfinance>=0.2.50
|
|
24
|
+
Description-Content-Type: text/markdown
|
|
25
|
+
|
|
26
|
+
# Portfolio Analytics MCP
|
|
27
|
+
|
|
28
|
+
[](https://pypi.org/project/portfolio-analytics-mcp/)
|
|
29
|
+
[](https://github.com/RugSec/portfolio-analytics-mcp/actions/workflows/ci.yml)
|
|
30
|
+
[](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/pyproject.toml)
|
|
31
|
+
[](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/LICENSE)
|
|
32
|
+
|
|
33
|
+
**Ask Claude about your investments in plain language.** This is an
|
|
34
|
+
[MCP](https://modelcontextprotocol.io) server that gives Claude (or any MCP
|
|
35
|
+
client) read-only tools for market data and portfolio analysis: prices, FX,
|
|
36
|
+
ETF costs and exposure, and risk for a whole portfolio of stocks and ETFs,
|
|
37
|
+
measured in your own currency.
|
|
38
|
+
|
|
39
|
+
It is built for European investors as much as US ones: it understands ISINs,
|
|
40
|
+
UCITS ETFs listed on several exchanges, and prices quoted in pence.
|
|
41
|
+
|
|
42
|
+
## What you can ask
|
|
43
|
+
|
|
44
|
+
> "I hold 30k EUR of IE00B4L5Y983, 10 ASML.AS and 25 AAPL. How volatile is
|
|
45
|
+
> this portfolio, and which position contributes most to the risk?"
|
|
46
|
+
|
|
47
|
+
> "Compare the costs of the iShares Core MSCI World and the Vanguard FTSE
|
|
48
|
+
> All-World. Which is cheaper and bigger?"
|
|
49
|
+
|
|
50
|
+
> "How much of my ETF portfolio is in US tech stocks? Do my ETFs overlap?"
|
|
51
|
+
|
|
52
|
+
> "What has VWCE returned since 2021 in euros, compared with the S&P 500?"
|
|
53
|
+
|
|
54
|
+
Claude picks the right tools, fetches the data and explains the result.
|
|
55
|
+
|
|
56
|
+
## What's inside
|
|
57
|
+
|
|
58
|
+
| Area | What it gives you | Data source |
|
|
59
|
+
| ------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------ |
|
|
60
|
+
| **Portfolio** | Risk and return of a mixed portfolio in one currency: volatility, drawdown, beta, risk per position | Yahoo Finance + justETF |
|
|
61
|
+
| **ETFs** | Costs (TER), fund size, country/sector/stock exposure, listings, similar funds, portfolio look-through | [justETF](https://www.justetf.com) |
|
|
62
|
+
| **Markets** | Price history, quotes, FX rates, performance statistics, correlations, dividends | [Yahoo Finance](https://finance.yahoo.com) |
|
|
63
|
+
|
|
64
|
+
20 tools in total, all read-only. No API key or account needed. See the
|
|
65
|
+
[user guide](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/docs/USER_GUIDE.md) for the full list.
|
|
66
|
+
|
|
67
|
+
## Quickstart
|
|
68
|
+
|
|
69
|
+
You need [uv](https://docs.astral.sh/uv/getting-started/installation/). It
|
|
70
|
+
downloads Python 3.12, the server and its dependencies on first run, and
|
|
71
|
+
checks for a new release each time the server starts.
|
|
72
|
+
|
|
73
|
+
**1. Connect it to Claude**
|
|
74
|
+
|
|
75
|
+
Claude Code:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
claude mcp add finance -- uvx portfolio-analytics-mcp@latest
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Claude Desktop: add this to `claude_desktop_config.json`, then restart the
|
|
82
|
+
app. If it can't find `uvx`, use the full path that `which uvx` prints.
|
|
83
|
+
|
|
84
|
+
```json
|
|
85
|
+
{
|
|
86
|
+
"mcpServers": {
|
|
87
|
+
"finance": {
|
|
88
|
+
"command": "uvx",
|
|
89
|
+
"args": ["portfolio-analytics-mcp@latest"]
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
To stay on one version instead, use `portfolio-analytics-mcp==0.2.0`.
|
|
96
|
+
|
|
97
|
+
The server runs on your machine. Only symbols and ISINs go out, to Yahoo
|
|
98
|
+
Finance and justETF; your quantities and amounts never leave it.
|
|
99
|
+
|
|
100
|
+
**2. Ask a question**, for example _"What is the TER of IE00B4L5Y983?"_
|
|
101
|
+
|
|
102
|
+
To work on the code instead, see [Contributing](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/CONTRIBUTING.md#setup).
|
|
103
|
+
|
|
104
|
+
Want to run it as a shared remote server instead? See
|
|
105
|
+
[Remote deployment](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/docs/USER_GUIDE.md#remote-deployment-http).
|
|
106
|
+
|
|
107
|
+
## Important to know
|
|
108
|
+
|
|
109
|
+
- **Not financial advice.** The tools report data and statistics; past
|
|
110
|
+
performance does not predict future results. Data can be delayed,
|
|
111
|
+
incomplete or wrong, and is provided as is, without warranty.
|
|
112
|
+
- **Not affiliated** with Yahoo, justETF or any fund provider. Names and
|
|
113
|
+
trademarks belong to their owners.
|
|
114
|
+
- **Personal use only.** Yahoo Finance (through the unofficial
|
|
115
|
+
[yfinance](https://pypi.org/project/yfinance/) library) and justETF are
|
|
116
|
+
third-party sites. This server makes polite, on-demand requests
|
|
117
|
+
(rate-limited and cached) and is not meant for bulk downloads or
|
|
118
|
+
redistributing their data. Check their terms before using it.
|
|
119
|
+
- Quotes can be delayed by up to ~20 minutes, and Yahoo's coverage of
|
|
120
|
+
European ETF holdings is patchy (the justETF tools fill that gap).
|
|
121
|
+
|
|
122
|
+
## Learn more
|
|
123
|
+
|
|
124
|
+
- [User guide](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/docs/USER_GUIDE.md): every tool, example workflows,
|
|
125
|
+
configuration, remote deployment and limitations.
|
|
126
|
+
- [Agent skill](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/skill/portfolio-analytics/SKILL.md): tells an agent when
|
|
127
|
+
and how to use the tools, and how to present results.
|
|
128
|
+
- [Contributing](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/CONTRIBUTING.md): development setup, tests and project
|
|
129
|
+
layout.
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
[MIT](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/LICENSE)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Portfolio Analytics MCP
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/portfolio-analytics-mcp/)
|
|
4
|
+
[](https://github.com/RugSec/portfolio-analytics-mcp/actions/workflows/ci.yml)
|
|
5
|
+
[](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/pyproject.toml)
|
|
6
|
+
[](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
**Ask Claude about your investments in plain language.** This is an
|
|
9
|
+
[MCP](https://modelcontextprotocol.io) server that gives Claude (or any MCP
|
|
10
|
+
client) read-only tools for market data and portfolio analysis: prices, FX,
|
|
11
|
+
ETF costs and exposure, and risk for a whole portfolio of stocks and ETFs,
|
|
12
|
+
measured in your own currency.
|
|
13
|
+
|
|
14
|
+
It is built for European investors as much as US ones: it understands ISINs,
|
|
15
|
+
UCITS ETFs listed on several exchanges, and prices quoted in pence.
|
|
16
|
+
|
|
17
|
+
## What you can ask
|
|
18
|
+
|
|
19
|
+
> "I hold 30k EUR of IE00B4L5Y983, 10 ASML.AS and 25 AAPL. How volatile is
|
|
20
|
+
> this portfolio, and which position contributes most to the risk?"
|
|
21
|
+
|
|
22
|
+
> "Compare the costs of the iShares Core MSCI World and the Vanguard FTSE
|
|
23
|
+
> All-World. Which is cheaper and bigger?"
|
|
24
|
+
|
|
25
|
+
> "How much of my ETF portfolio is in US tech stocks? Do my ETFs overlap?"
|
|
26
|
+
|
|
27
|
+
> "What has VWCE returned since 2021 in euros, compared with the S&P 500?"
|
|
28
|
+
|
|
29
|
+
Claude picks the right tools, fetches the data and explains the result.
|
|
30
|
+
|
|
31
|
+
## What's inside
|
|
32
|
+
|
|
33
|
+
| Area | What it gives you | Data source |
|
|
34
|
+
| ------------- | ------------------------------------------------------------------------------------------------------ | ------------------------------------------ |
|
|
35
|
+
| **Portfolio** | Risk and return of a mixed portfolio in one currency: volatility, drawdown, beta, risk per position | Yahoo Finance + justETF |
|
|
36
|
+
| **ETFs** | Costs (TER), fund size, country/sector/stock exposure, listings, similar funds, portfolio look-through | [justETF](https://www.justetf.com) |
|
|
37
|
+
| **Markets** | Price history, quotes, FX rates, performance statistics, correlations, dividends | [Yahoo Finance](https://finance.yahoo.com) |
|
|
38
|
+
|
|
39
|
+
20 tools in total, all read-only. No API key or account needed. See the
|
|
40
|
+
[user guide](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/docs/USER_GUIDE.md) for the full list.
|
|
41
|
+
|
|
42
|
+
## Quickstart
|
|
43
|
+
|
|
44
|
+
You need [uv](https://docs.astral.sh/uv/getting-started/installation/). It
|
|
45
|
+
downloads Python 3.12, the server and its dependencies on first run, and
|
|
46
|
+
checks for a new release each time the server starts.
|
|
47
|
+
|
|
48
|
+
**1. Connect it to Claude**
|
|
49
|
+
|
|
50
|
+
Claude Code:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
claude mcp add finance -- uvx portfolio-analytics-mcp@latest
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Claude Desktop: add this to `claude_desktop_config.json`, then restart the
|
|
57
|
+
app. If it can't find `uvx`, use the full path that `which uvx` prints.
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"mcpServers": {
|
|
62
|
+
"finance": {
|
|
63
|
+
"command": "uvx",
|
|
64
|
+
"args": ["portfolio-analytics-mcp@latest"]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
To stay on one version instead, use `portfolio-analytics-mcp==0.2.0`.
|
|
71
|
+
|
|
72
|
+
The server runs on your machine. Only symbols and ISINs go out, to Yahoo
|
|
73
|
+
Finance and justETF; your quantities and amounts never leave it.
|
|
74
|
+
|
|
75
|
+
**2. Ask a question**, for example _"What is the TER of IE00B4L5Y983?"_
|
|
76
|
+
|
|
77
|
+
To work on the code instead, see [Contributing](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/CONTRIBUTING.md#setup).
|
|
78
|
+
|
|
79
|
+
Want to run it as a shared remote server instead? See
|
|
80
|
+
[Remote deployment](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/docs/USER_GUIDE.md#remote-deployment-http).
|
|
81
|
+
|
|
82
|
+
## Important to know
|
|
83
|
+
|
|
84
|
+
- **Not financial advice.** The tools report data and statistics; past
|
|
85
|
+
performance does not predict future results. Data can be delayed,
|
|
86
|
+
incomplete or wrong, and is provided as is, without warranty.
|
|
87
|
+
- **Not affiliated** with Yahoo, justETF or any fund provider. Names and
|
|
88
|
+
trademarks belong to their owners.
|
|
89
|
+
- **Personal use only.** Yahoo Finance (through the unofficial
|
|
90
|
+
[yfinance](https://pypi.org/project/yfinance/) library) and justETF are
|
|
91
|
+
third-party sites. This server makes polite, on-demand requests
|
|
92
|
+
(rate-limited and cached) and is not meant for bulk downloads or
|
|
93
|
+
redistributing their data. Check their terms before using it.
|
|
94
|
+
- Quotes can be delayed by up to ~20 minutes, and Yahoo's coverage of
|
|
95
|
+
European ETF holdings is patchy (the justETF tools fill that gap).
|
|
96
|
+
|
|
97
|
+
## Learn more
|
|
98
|
+
|
|
99
|
+
- [User guide](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/docs/USER_GUIDE.md): every tool, example workflows,
|
|
100
|
+
configuration, remote deployment and limitations.
|
|
101
|
+
- [Agent skill](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/skill/portfolio-analytics/SKILL.md): tells an agent when
|
|
102
|
+
and how to use the tools, and how to present results.
|
|
103
|
+
- [Contributing](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/CONTRIBUTING.md): development setup, tests and project
|
|
104
|
+
layout.
|
|
105
|
+
|
|
106
|
+
## License
|
|
107
|
+
|
|
108
|
+
[MIT](https://github.com/RugSec/portfolio-analytics-mcp/blob/main/LICENSE)
|