moneymanager-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.
- moneymanager_mcp-0.2.0/.github/workflows/ci.yml +58 -0
- moneymanager_mcp-0.2.0/.github/workflows/release.yml +27 -0
- moneymanager_mcp-0.2.0/.gitignore +15 -0
- moneymanager_mcp-0.2.0/CHANGELOG.md +23 -0
- moneymanager_mcp-0.2.0/LICENSE +21 -0
- moneymanager_mcp-0.2.0/PKG-INFO +114 -0
- moneymanager_mcp-0.2.0/README.md +83 -0
- moneymanager_mcp-0.2.0/pyproject.toml +65 -0
- moneymanager_mcp-0.2.0/src/moneymanager_mcp/__init__.py +3 -0
- moneymanager_mcp-0.2.0/src/moneymanager_mcp/py.typed +0 -0
- moneymanager_mcp-0.2.0/src/moneymanager_mcp/server.py +137 -0
- moneymanager_mcp-0.2.0/tests/test_server.py +82 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
lint:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
steps:
|
|
11
|
+
- uses: actions/checkout@v4
|
|
12
|
+
- uses: actions/setup-python@v5
|
|
13
|
+
with:
|
|
14
|
+
python-version: '3.12'
|
|
15
|
+
cache: pip
|
|
16
|
+
- run: |
|
|
17
|
+
python -m pip install 'mcp>=1.2' 'moneymanager-parser>=0.2' build mypy pytest pytest-cov ruff
|
|
18
|
+
python -m pip install --no-deps -e .
|
|
19
|
+
- run: ruff check .
|
|
20
|
+
- run: ruff format --check .
|
|
21
|
+
typecheck:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: '3.12'
|
|
28
|
+
cache: pip
|
|
29
|
+
- run: |
|
|
30
|
+
python -m pip install 'mcp>=1.2' 'moneymanager-parser>=0.2' build mypy pytest pytest-cov ruff
|
|
31
|
+
python -m pip install --no-deps -e .
|
|
32
|
+
- run: mypy src
|
|
33
|
+
test:
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
strategy:
|
|
36
|
+
matrix:
|
|
37
|
+
python-version: ['3.10', '3.11', '3.12', '3.13']
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v4
|
|
40
|
+
- uses: actions/setup-python@v5
|
|
41
|
+
with:
|
|
42
|
+
python-version: ${{ matrix.python-version }}
|
|
43
|
+
cache: pip
|
|
44
|
+
- run: |
|
|
45
|
+
python -m pip install 'mcp>=1.2' 'moneymanager-parser>=0.2' build mypy pytest pytest-cov ruff
|
|
46
|
+
python -m pip install --no-deps -e .
|
|
47
|
+
- run: pytest --cov --cov-report=term-missing --cov-report=xml
|
|
48
|
+
- name: Coverage summary
|
|
49
|
+
run: |
|
|
50
|
+
python - <<'PY' >> "$GITHUB_STEP_SUMMARY"
|
|
51
|
+
import xml.etree.ElementTree as ET
|
|
52
|
+
root = ET.parse('coverage.xml').getroot()
|
|
53
|
+
print(f"Coverage: {float(root.attrib['line-rate']) * 100:.2f}%")
|
|
54
|
+
PY
|
|
55
|
+
- uses: actions/upload-artifact@v4
|
|
56
|
+
with:
|
|
57
|
+
name: coverage-${{ matrix.python-version }}
|
|
58
|
+
path: coverage.xml
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# One-time PyPI setup before the first tag:
|
|
2
|
+
# create a PyPI project or pending publisher for shubham1172/moneymanager-mcp,
|
|
3
|
+
# workflow release.yml, environment pypi, package moneymanager-mcp.
|
|
4
|
+
name: Release
|
|
5
|
+
|
|
6
|
+
on:
|
|
7
|
+
push:
|
|
8
|
+
tags:
|
|
9
|
+
- 'v*'
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
id-token: write
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build-and-publish:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
environment: pypi
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
- uses: actions/setup-python@v5
|
|
22
|
+
with:
|
|
23
|
+
python-version: '3.12'
|
|
24
|
+
cache: pip
|
|
25
|
+
- run: python -m pip install build
|
|
26
|
+
- run: python -m build
|
|
27
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here.
|
|
4
|
+
|
|
5
|
+
## [Unreleased]
|
|
6
|
+
|
|
7
|
+
## [0.2.0] - 2026-06-01
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- `accounts` tool exposing account names and balances.
|
|
11
|
+
- `currency` tool exposing the main currency and all declared currencies.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- Require `moneymanager-parser>=0.2`.
|
|
15
|
+
- CI installs the published parser instead of a local stub.
|
|
16
|
+
|
|
17
|
+
### Removed
|
|
18
|
+
- `summary` tool, following its removal from `moneymanager-parser`.
|
|
19
|
+
|
|
20
|
+
## [0.1.0] - 2026-01-01
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Initial MCP stdio server exposing summary, query, schema, and category tools.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shubham Sharma
|
|
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,114 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: moneymanager-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: MCP stdio server for Realbyte Money Manager .mmbak backups.
|
|
5
|
+
Project-URL: Homepage, https://github.com/shubham1172/moneymanager-mcp
|
|
6
|
+
Project-URL: Repository, https://github.com/shubham1172/moneymanager-mcp
|
|
7
|
+
Project-URL: Issues, https://github.com/shubham1172/moneymanager-mcp/issues
|
|
8
|
+
Author-email: Shubham Sharma <shubhamsharma1172@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: finance,mcp,mmbak,money-manager,realbyte
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.10
|
|
22
|
+
Requires-Dist: mcp>=1.2
|
|
23
|
+
Requires-Dist: moneymanager-parser>=0.2
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: build>=1.2; extra == 'dev'
|
|
26
|
+
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=8.2; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# moneymanager-mcp
|
|
33
|
+
|
|
34
|
+
[](https://github.com/shubham1172/moneymanager-mcp/actions/workflows/ci.yml)
|
|
35
|
+

|
|
36
|
+
|
|
37
|
+
MCP stdio server for querying Realbyte Money Manager `.mmbak` exports with AI clients.
|
|
38
|
+
|
|
39
|
+
The server reads a local `.mmbak` file, which is a ZIP-wrapped SQLite database, through the
|
|
40
|
+
`moneymanager-parser` SDK. It exposes tools for filtered queries, schema inspection, category
|
|
41
|
+
listing, accounts (names and balances), and currencies.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
pip install moneymanager-mcp
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Run
|
|
50
|
+
|
|
51
|
+
Point the server at a backup file with `MONEY_BACKUP` or pass `backup_path` to each tool:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
export MONEY_BACKUP=/path/to/backup.mmbak
|
|
55
|
+
moneymanager-mcp
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Tools:
|
|
59
|
+
|
|
60
|
+
- `query(backup_path=None, date_from=None, date_to=None, month=None, category=None, search=None, kind="expense", group_by=None, top=None, list_n=None, limit=24)`
|
|
61
|
+
- `schema(backup_path=None)`
|
|
62
|
+
- `list_categories(backup_path=None)`
|
|
63
|
+
- `accounts(backup_path=None)`
|
|
64
|
+
- `currency(backup_path=None)`
|
|
65
|
+
|
|
66
|
+
Invalid or missing backups return a JSON error payload instead of crashing the server.
|
|
67
|
+
|
|
68
|
+
## Client configuration
|
|
69
|
+
|
|
70
|
+
Claude Desktop example:
|
|
71
|
+
|
|
72
|
+
```json
|
|
73
|
+
{
|
|
74
|
+
"mcpServers": {
|
|
75
|
+
"money-manager": {
|
|
76
|
+
"command": "moneymanager-mcp",
|
|
77
|
+
"env": {
|
|
78
|
+
"MONEY_BACKUP": "/path/to/backup.mmbak"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Generic stdio example:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"command": "moneymanager-mcp",
|
|
90
|
+
"args": [],
|
|
91
|
+
"env": {"MONEY_BACKUP": "/path/to/backup.mmbak"}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Development
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
python3 -m venv .venv
|
|
99
|
+
. .venv/bin/activate
|
|
100
|
+
pip install -e '.[dev]'
|
|
101
|
+
ruff check .
|
|
102
|
+
ruff format --check .
|
|
103
|
+
mypy src
|
|
104
|
+
pytest --cov
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Contributing
|
|
108
|
+
|
|
109
|
+
Issues and pull requests are welcome. Please use generated sample backups in tests rather than
|
|
110
|
+
personal exports.
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT © 2026 Shubham Sharma
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# moneymanager-mcp
|
|
2
|
+
|
|
3
|
+
[](https://github.com/shubham1172/moneymanager-mcp/actions/workflows/ci.yml)
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
MCP stdio server for querying Realbyte Money Manager `.mmbak` exports with AI clients.
|
|
7
|
+
|
|
8
|
+
The server reads a local `.mmbak` file, which is a ZIP-wrapped SQLite database, through the
|
|
9
|
+
`moneymanager-parser` SDK. It exposes tools for filtered queries, schema inspection, category
|
|
10
|
+
listing, accounts (names and balances), and currencies.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install moneymanager-mcp
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Run
|
|
19
|
+
|
|
20
|
+
Point the server at a backup file with `MONEY_BACKUP` or pass `backup_path` to each tool:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
export MONEY_BACKUP=/path/to/backup.mmbak
|
|
24
|
+
moneymanager-mcp
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Tools:
|
|
28
|
+
|
|
29
|
+
- `query(backup_path=None, date_from=None, date_to=None, month=None, category=None, search=None, kind="expense", group_by=None, top=None, list_n=None, limit=24)`
|
|
30
|
+
- `schema(backup_path=None)`
|
|
31
|
+
- `list_categories(backup_path=None)`
|
|
32
|
+
- `accounts(backup_path=None)`
|
|
33
|
+
- `currency(backup_path=None)`
|
|
34
|
+
|
|
35
|
+
Invalid or missing backups return a JSON error payload instead of crashing the server.
|
|
36
|
+
|
|
37
|
+
## Client configuration
|
|
38
|
+
|
|
39
|
+
Claude Desktop example:
|
|
40
|
+
|
|
41
|
+
```json
|
|
42
|
+
{
|
|
43
|
+
"mcpServers": {
|
|
44
|
+
"money-manager": {
|
|
45
|
+
"command": "moneymanager-mcp",
|
|
46
|
+
"env": {
|
|
47
|
+
"MONEY_BACKUP": "/path/to/backup.mmbak"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Generic stdio example:
|
|
55
|
+
|
|
56
|
+
```json
|
|
57
|
+
{
|
|
58
|
+
"command": "moneymanager-mcp",
|
|
59
|
+
"args": [],
|
|
60
|
+
"env": {"MONEY_BACKUP": "/path/to/backup.mmbak"}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Development
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
python3 -m venv .venv
|
|
68
|
+
. .venv/bin/activate
|
|
69
|
+
pip install -e '.[dev]'
|
|
70
|
+
ruff check .
|
|
71
|
+
ruff format --check .
|
|
72
|
+
mypy src
|
|
73
|
+
pytest --cov
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Contributing
|
|
77
|
+
|
|
78
|
+
Issues and pull requests are welcome. Please use generated sample backups in tests rather than
|
|
79
|
+
personal exports.
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
MIT © 2026 Shubham Sharma
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.25"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "moneymanager-mcp"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "MCP stdio server for Realbyte Money Manager .mmbak backups."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
license-files = ["LICENSE"]
|
|
13
|
+
authors = [{ name = "Shubham Sharma", email = "shubhamsharma1172@gmail.com" }]
|
|
14
|
+
keywords = ["mcp", "money-manager", "realbyte", "mmbak", "finance"]
|
|
15
|
+
classifiers = [
|
|
16
|
+
"Development Status :: 3 - Alpha",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Programming Language :: Python :: 3.13",
|
|
24
|
+
"Typing :: Typed",
|
|
25
|
+
]
|
|
26
|
+
dependencies = ["mcp>=1.2", "moneymanager-parser>=0.2"]
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/shubham1172/moneymanager-mcp"
|
|
30
|
+
Repository = "https://github.com/shubham1172/moneymanager-mcp"
|
|
31
|
+
Issues = "https://github.com/shubham1172/moneymanager-mcp/issues"
|
|
32
|
+
|
|
33
|
+
[project.scripts]
|
|
34
|
+
moneymanager-mcp = "moneymanager_mcp.server:main"
|
|
35
|
+
|
|
36
|
+
[project.optional-dependencies]
|
|
37
|
+
dev = ["build>=1.2", "mypy>=1.10", "pytest>=8.2", "pytest-cov>=5.0", "ruff>=0.5"]
|
|
38
|
+
|
|
39
|
+
[tool.hatch.version]
|
|
40
|
+
path = "src/moneymanager_mcp/__init__.py"
|
|
41
|
+
|
|
42
|
+
[tool.ruff]
|
|
43
|
+
line-length = 100
|
|
44
|
+
target-version = "py310"
|
|
45
|
+
|
|
46
|
+
[tool.ruff.lint]
|
|
47
|
+
select = ["E", "F", "I", "UP", "B"]
|
|
48
|
+
ignore = ["E501"]
|
|
49
|
+
|
|
50
|
+
[tool.mypy]
|
|
51
|
+
python_version = "3.10"
|
|
52
|
+
strict = true
|
|
53
|
+
mypy_path = "src"
|
|
54
|
+
|
|
55
|
+
[tool.pytest.ini_options]
|
|
56
|
+
addopts = "-ra"
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
|
|
59
|
+
[tool.coverage.run]
|
|
60
|
+
branch = true
|
|
61
|
+
source = ["moneymanager_mcp"]
|
|
62
|
+
|
|
63
|
+
[tool.coverage.report]
|
|
64
|
+
show_missing = true
|
|
65
|
+
fail_under = 90
|
|
File without changes
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""FastMCP stdio server for Realbyte Money Manager backups."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from collections.abc import Callable, Sequence
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Literal, TypeVar
|
|
9
|
+
|
|
10
|
+
from mcp.server.fastmcp import FastMCP
|
|
11
|
+
from moneymanager_parser import Currency, MoneyManagerBackup
|
|
12
|
+
|
|
13
|
+
Json = dict[str, Any]
|
|
14
|
+
Kind = Literal["expense", "income", "all"]
|
|
15
|
+
Group = Literal["month", "category", "day", "week"]
|
|
16
|
+
T = TypeVar("T")
|
|
17
|
+
|
|
18
|
+
mcp = FastMCP("moneymanager")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _currency_dict(item: Currency) -> Json:
|
|
22
|
+
return {
|
|
23
|
+
"iso": item.iso,
|
|
24
|
+
"symbol": item.symbol,
|
|
25
|
+
"name": item.name,
|
|
26
|
+
"is_main": item.is_main,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _resolve_backup(path: str | None = None) -> Path:
|
|
31
|
+
value = path or os.environ.get("MONEY_BACKUP")
|
|
32
|
+
if not value:
|
|
33
|
+
raise ValueError("backup path is required; pass backup_path or set MONEY_BACKUP")
|
|
34
|
+
backup = Path(value).expanduser()
|
|
35
|
+
if not backup.exists():
|
|
36
|
+
raise FileNotFoundError(f"backup not found: {backup}")
|
|
37
|
+
return backup
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _with_backup(path: str | None, func: Callable[[MoneyManagerBackup], T]) -> T | Json:
|
|
41
|
+
try:
|
|
42
|
+
with MoneyManagerBackup.from_file(_resolve_backup(path)) as backup:
|
|
43
|
+
return func(backup)
|
|
44
|
+
except Exception as exc: # noqa: BLE001
|
|
45
|
+
return {"status": "error", "reason": str(exc)}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@mcp.tool()
|
|
49
|
+
def accounts(backup_path: str | None = None) -> Json:
|
|
50
|
+
"""Return accounts with names and balances from a Money Manager backup.
|
|
51
|
+
|
|
52
|
+
Balances come from the backup's balance column when present, otherwise they
|
|
53
|
+
are derived from transactions (income adds, expense subtracts, transfers move
|
|
54
|
+
to the destination account) as raw sums without currency conversion.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
def run(backup: MoneyManagerBackup) -> Json:
|
|
58
|
+
return {
|
|
59
|
+
"status": "ok",
|
|
60
|
+
"accounts": [{"name": a.name, "balance": a.balance} for a in backup.accounts()],
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return _with_backup(backup_path, run)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@mcp.tool()
|
|
67
|
+
def currency(backup_path: str | None = None) -> Json:
|
|
68
|
+
"""Return the main currency and all currencies declared in the backup."""
|
|
69
|
+
|
|
70
|
+
def run(backup: MoneyManagerBackup) -> Json:
|
|
71
|
+
main = backup.currency()
|
|
72
|
+
return {
|
|
73
|
+
"status": "ok",
|
|
74
|
+
"main": _currency_dict(main) if main else None,
|
|
75
|
+
"all": [_currency_dict(c) for c in backup.currencies()],
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return _with_backup(backup_path, run)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@mcp.tool()
|
|
82
|
+
def query(
|
|
83
|
+
backup_path: str | None = None,
|
|
84
|
+
date_from: str | None = None,
|
|
85
|
+
date_to: str | None = None,
|
|
86
|
+
month: str | None = None,
|
|
87
|
+
category: str | None = None,
|
|
88
|
+
search: str | None = None,
|
|
89
|
+
kind: Kind = "expense",
|
|
90
|
+
group_by: Group | None = None,
|
|
91
|
+
top: int | None = None,
|
|
92
|
+
list_n: int | None = None,
|
|
93
|
+
limit: int = 24,
|
|
94
|
+
) -> Json:
|
|
95
|
+
"""Run a filtered full-history query over a Money Manager backup."""
|
|
96
|
+
|
|
97
|
+
def run(backup: MoneyManagerBackup) -> Json:
|
|
98
|
+
return backup.query(
|
|
99
|
+
date_from=date_from,
|
|
100
|
+
date_to=date_to,
|
|
101
|
+
month=month,
|
|
102
|
+
category=category,
|
|
103
|
+
search=search,
|
|
104
|
+
kind=kind,
|
|
105
|
+
group_by=group_by,
|
|
106
|
+
top=top,
|
|
107
|
+
list_n=list_n,
|
|
108
|
+
limit=limit,
|
|
109
|
+
).as_dict()
|
|
110
|
+
|
|
111
|
+
result = _with_backup(backup_path, run)
|
|
112
|
+
return result
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@mcp.tool()
|
|
116
|
+
def schema(backup_path: str | None = None) -> Json:
|
|
117
|
+
"""Return resolved tables, columns, and transaction type counts."""
|
|
118
|
+
result = _with_backup(backup_path, lambda backup: backup.schema())
|
|
119
|
+
return result
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@mcp.tool()
|
|
123
|
+
def list_categories(backup_path: str | None = None) -> Json:
|
|
124
|
+
"""Return category names found in the backup."""
|
|
125
|
+
result = _with_backup(
|
|
126
|
+
backup_path, lambda backup: {"status": "ok", "categories": backup.categories()}
|
|
127
|
+
)
|
|
128
|
+
return result
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def main(_argv: Sequence[str] | None = None) -> int:
|
|
132
|
+
mcp.run(transport="stdio")
|
|
133
|
+
return 0
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
if __name__ == "__main__":
|
|
137
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sqlite3
|
|
4
|
+
import zipfile
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from moneymanager_mcp import __version__
|
|
10
|
+
from moneymanager_mcp.server import (
|
|
11
|
+
_resolve_backup,
|
|
12
|
+
accounts,
|
|
13
|
+
currency,
|
|
14
|
+
list_categories,
|
|
15
|
+
main,
|
|
16
|
+
mcp,
|
|
17
|
+
query,
|
|
18
|
+
schema,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _backup(path: Path) -> Path:
|
|
23
|
+
db = path / "sample.sqlite"
|
|
24
|
+
con = sqlite3.connect(db)
|
|
25
|
+
con.executescript(
|
|
26
|
+
"""
|
|
27
|
+
CREATE TABLE ZCATEGORY (uid TEXT, ZNAME TEXT);
|
|
28
|
+
CREATE TABLE ASSETS (uid TEXT, ZNAME TEXT, ZMONEY TEXT);
|
|
29
|
+
CREATE TABLE CURRENCY (uid TEXT, ISO TEXT, SYMBOL TEXT, NAME TEXT, IS_MAIN_CURRENCY INTEGER);
|
|
30
|
+
CREATE TABLE INOUTCOME (ZMONEY TEXT, WDATE TEXT, DO_TYPE TEXT, ctgUid TEXT, assetUid TEXT, ZCONTENT TEXT);
|
|
31
|
+
INSERT INTO ZCATEGORY VALUES ('1', 'Food');
|
|
32
|
+
INSERT INTO ASSETS VALUES ('a1', 'Cash', '50');
|
|
33
|
+
INSERT INTO CURRENCY VALUES ('c1', 'USD', '$', 'US Dollar', 1);
|
|
34
|
+
INSERT INTO INOUTCOME VALUES ('12', '2026-01-02', '1', '1', 'a1', 'test coffee');
|
|
35
|
+
INSERT INTO INOUTCOME VALUES ('100', '2026-01-02', '0', '1', 'a1', 'test income');
|
|
36
|
+
"""
|
|
37
|
+
)
|
|
38
|
+
con.commit()
|
|
39
|
+
con.close()
|
|
40
|
+
archive_path = path / "sample.mmbak"
|
|
41
|
+
with zipfile.ZipFile(archive_path, "w", zipfile.ZIP_DEFLATED) as archive:
|
|
42
|
+
archive.write(db, "money.sqlite")
|
|
43
|
+
return archive_path
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def test_tools_with_explicit_path(tmp_path: Path) -> None:
|
|
47
|
+
backup = _backup(tmp_path)
|
|
48
|
+
assert query(str(backup), month="2026-01", group_by="category")["groups"][0]["key"] == "Food"
|
|
49
|
+
assert schema(str(backup))["transaction_table"] == "INOUTCOME"
|
|
50
|
+
assert list_categories(str(backup))["categories"] == ["Food"]
|
|
51
|
+
accts = accounts(str(backup))
|
|
52
|
+
assert accts["status"] == "ok"
|
|
53
|
+
assert accts["accounts"] == [{"name": "Cash", "balance": 50.0}]
|
|
54
|
+
cur = currency(str(backup))
|
|
55
|
+
assert cur["status"] == "ok"
|
|
56
|
+
assert cur["main"]["iso"] == "USD"
|
|
57
|
+
assert cur["main"]["symbol"] == "$"
|
|
58
|
+
assert [c["iso"] for c in cur["all"]] == ["USD"]
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_env_path_and_errors(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
62
|
+
backup = _backup(tmp_path)
|
|
63
|
+
monkeypatch.setenv("MONEY_BACKUP", str(backup))
|
|
64
|
+
assert _resolve_backup() == backup
|
|
65
|
+
assert query()["status"] == "ok"
|
|
66
|
+
monkeypatch.delenv("MONEY_BACKUP")
|
|
67
|
+
assert query()["status"] == "error"
|
|
68
|
+
assert accounts(str(tmp_path / "missing.mmbak"))["status"] == "error"
|
|
69
|
+
assert currency(str(tmp_path / "missing.mmbak"))["status"] == "error"
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_entry_point_smoke(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
73
|
+
assert __version__ == "0.2.0"
|
|
74
|
+
assert mcp.name == "moneymanager"
|
|
75
|
+
called: list[str] = []
|
|
76
|
+
|
|
77
|
+
def fake_run(*, transport: str) -> None:
|
|
78
|
+
called.append(transport)
|
|
79
|
+
|
|
80
|
+
monkeypatch.setattr(mcp, "run", fake_run)
|
|
81
|
+
assert main([]) == 0
|
|
82
|
+
assert called == ["stdio"]
|