finanzen-zero-mcp 0.1.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.
- finanzen_zero_mcp-0.1.0/LICENSE +21 -0
- finanzen_zero_mcp-0.1.0/PKG-INFO +408 -0
- finanzen_zero_mcp-0.1.0/README.md +379 -0
- finanzen_zero_mcp-0.1.0/pyproject.toml +59 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/__init__.py +3 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/__main__.py +8 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/client.py +54 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/config.py +106 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/errors.py +77 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/py.typed +0 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/serialization.py +23 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/server.py +153 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/tools/__init__.py +24 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/tools/_annotations.py +28 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/tools/_helpers.py +21 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/tools/account.py +26 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/tools/instrument.py +103 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/tools/orders.py +365 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/tools/portfolio.py +29 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/tools/sessiontan.py +101 -0
- finanzen_zero_mcp-0.1.0/src/zero_mcp/tools/watchlist.py +62 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Klaus Deja
|
|
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,408 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: finanzen-zero-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for the German broker Finanzen.net ZERO
|
|
5
|
+
Keywords: finanzen.net,zero,broker,trading,mcp,model-context-protocol
|
|
6
|
+
Author: Klaus Deja
|
|
7
|
+
Author-email: Klaus Deja <klaus.deja@easesolutions.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Dist: finanzen-zero-api>=0.1.0,<0.2.0
|
|
20
|
+
Requires-Dist: mcp>=2.3.0,<3.0.0
|
|
21
|
+
Requires-Dist: pydantic>=2.14.1
|
|
22
|
+
Requires-Dist: pydantic-settings>=2.16.1,<3.0.0
|
|
23
|
+
Requires-Python: >=3.13
|
|
24
|
+
Project-URL: Homepage, https://github.com/wtfuii/zero-mcp
|
|
25
|
+
Project-URL: Repository, https://github.com/wtfuii/zero-mcp
|
|
26
|
+
Project-URL: Issues, https://github.com/wtfuii/zero-mcp/issues
|
|
27
|
+
Project-URL: Releases, https://github.com/wtfuii/zero-mcp/releases
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
<h1 align="center">zero-mcp</h1>
|
|
31
|
+
|
|
32
|
+
<p align="center">
|
|
33
|
+
<strong>MCP server for the German broker Finanzen.net ZERO</strong> π©πͺ<br>
|
|
34
|
+
Local stdio MCP server wrapping the unofficial Python client <code>finanzen-zero-api</code>.
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
<p align="center">
|
|
38
|
+
<a href="https://github.com/wtfuii/zero-mcp/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/wtfuii/zero-mcp/actions/workflows/ci.yml/badge.svg"></a>
|
|
39
|
+
<a href="https://pypi.org/project/finanzen-zero-mcp/"><img alt="PyPI" src="https://img.shields.io/pypi/v/finanzen-zero-mcp?logo=pypi&logoColor=white"></a>
|
|
40
|
+
<a href="https://www.python.org/downloads/"><img alt="Python 3.13+" src="https://img.shields.io/badge/python-3.13%2B-3776AB?logo=python&logoColor=white"></a>
|
|
41
|
+
<a href="https://mypy.readthedocs.io/en/stable/"><img alt="mypy: strict" src="https://img.shields.io/badge/mypy-strict-2A6DB2"></a>
|
|
42
|
+
<a href="#development"><img alt="Tests: offline only" src="https://img.shields.io/badge/tests-offline%20only-success"></a>
|
|
43
|
+
<a href="https://github.com/wtfuii/zero-mcp/blob/main/LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-yellow.svg"></a>
|
|
44
|
+
<a href="#project-status"><img alt="Status: alpha" src="https://img.shields.io/badge/status-alpha-orange.svg"></a>
|
|
45
|
+
</p>
|
|
46
|
+
|
|
47
|
+
> **Unofficial and unsupported.** Finanzen.net ZERO has no public API. This server wraps
|
|
48
|
+
> [`finanzen-zero-api`](https://github.com/wtfuii/zero-api), which mimics the broker's private
|
|
49
|
+
> web/app endpoints; they can change at any time. Use it only on an account you are allowed to
|
|
50
|
+
> automate, and never with money you cannot afford to lose. **Order tools move real money.**
|
|
51
|
+
|
|
52
|
+
`zero-mcp` turns the [`finanzen-zero-api`](https://github.com/wtfuii/zero-api) Python client into a
|
|
53
|
+
local [Model Context Protocol](https://modelcontextprotocol.io) server. It is published on PyPI as
|
|
54
|
+
[`finanzen-zero-mcp`](https://pypi.org/project/finanzen-zero-mcp/) and installs the `zero-mcp`
|
|
55
|
+
console script. An MCP host such as Claude Desktop, Claude Code or Cursor starts the server over
|
|
56
|
+
**stdio**; an assistant can then read the account, search instruments, inspect charts, list depot
|
|
57
|
+
positions and watchlist entries, review orders, and place or cancel orders. `ZERO_MODE` controls
|
|
58
|
+
whether the mutating tools are exposed and whether orders need confirmation β see
|
|
59
|
+
[Configuration](#configuration).
|
|
60
|
+
|
|
61
|
+
## Contents
|
|
62
|
+
|
|
63
|
+
- [Features](#features)
|
|
64
|
+
- [Tools](#tools)
|
|
65
|
+
- [Requirements](#requirements)
|
|
66
|
+
- [Installation](#installation)
|
|
67
|
+
- [Configuration](#configuration)
|
|
68
|
+
- [Connect an MCP host](#connect-an-mcp-host)
|
|
69
|
+
- [Usage examples](#usage-examples)
|
|
70
|
+
- [Authentication and 2FA](#authentication-and-2fa)
|
|
71
|
+
- [Output conventions](#output-conventions)
|
|
72
|
+
- [Safety and security](#safety-and-security)
|
|
73
|
+
- [Architecture](#architecture)
|
|
74
|
+
- [Development](#development)
|
|
75
|
+
- [Limitations](#limitations)
|
|
76
|
+
- [Project status](#project-status)
|
|
77
|
+
- [License](#license)
|
|
78
|
+
|
|
79
|
+
## Features
|
|
80
|
+
|
|
81
|
+
- **Full library coverage.** Account, instrument search/master data/detail/charts, depot positions,
|
|
82
|
+
watchlist, orders, and the app-push 2FA that unlocks order execution.
|
|
83
|
+
- **Local stdio only.** The MCP host spawns the process; there is no listening port, no browser and
|
|
84
|
+
no Playwright/Selenium dependency.
|
|
85
|
+
- **One serialized session.** All tools share a single lock-protected `ZeroClient`; broker calls are
|
|
86
|
+
serialized so concurrent requests cannot race the login, session refresh, 2FA unlock or order
|
|
87
|
+
flow.
|
|
88
|
+
- **Errors meant for a model.** Missing configuration, bad credentials, 2FA timeouts, broker
|
|
89
|
+
rejections and network failures become actionable MCP tool errors instead of stack traces.
|
|
90
|
+
- **Offline test suite.** A mocked broker client and a real stdio subprocess test; no live calls and
|
|
91
|
+
no credentials needed in CI.
|
|
92
|
+
|
|
93
|
+
## Tools
|
|
94
|
+
|
|
95
|
+
Fifteen tools are registered in the default mode. The ten read tools are always available; the five
|
|
96
|
+
write tools are absent from `tools/list` when `ZERO_MODE=READ`.
|
|
97
|
+
|
|
98
|
+
| Tool | Kind | What it does |
|
|
99
|
+
|---|---|---|
|
|
100
|
+
| `get_account` | read | Cash account (Verrechnungskonto): buying/transfer power, IBAN, saldo, closing date. |
|
|
101
|
+
| `search_instruments` | read | Typeahead search by name, WKN or ISIN; per-asset-class hit counts. |
|
|
102
|
+
| `get_instrument` | read | Master record: asset class, trading state, fees, venues and MIC codes. |
|
|
103
|
+
| `get_instrument_detail` | read | Market data and fundamentals: quote, ranges, volume, profile, dividends. |
|
|
104
|
+
| `get_chart` | read | Price history for one scale (`Intraday` β¦ `Maximum`). |
|
|
105
|
+
| `get_positions` | read | Depot positions with quantity, latest quote and entry quotes. |
|
|
106
|
+
| `get_watchlist` | read | Watchlist entries with quotes and metadata. |
|
|
107
|
+
| `list_orders` | read | Orders from one filter bucket (the broker's UI tabs) β `Open` (working, default), `Filled` (executed) or `Canceled` β with `has_next` pagination info. |
|
|
108
|
+
| `get_session_status` | read | Whether the session is unlocked for order execution. |
|
|
109
|
+
| `list_enrolled_devices` | read | Devices that can receive the 2FA push (with serials). |
|
|
110
|
+
| `add_to_watchlist` | write | Add an instrument by ISIN. |
|
|
111
|
+
| `remove_from_watchlist` | write | Remove an instrument by ISIN. |
|
|
112
|
+
| `unlock_order_execution` | write | Send the app push and wait for approval. |
|
|
113
|
+
| `place_order` | **write, real money** | Place a market/limit/stop-limit/OCO/trailing-stop order. |
|
|
114
|
+
| `cancel_order` | **write, real money** | Cancel an open order by its `order_id`. |
|
|
115
|
+
|
|
116
|
+
Order tools unlock the 2FA session automatically and can block for up to 60 seconds while the push is
|
|
117
|
+
approved. Their confirmation policy is set by `ZERO_MODE` (see [Configuration](#configuration)); for
|
|
118
|
+
device selection see [Authentication and 2FA](#authentication-and-2fa).
|
|
119
|
+
|
|
120
|
+
## Requirements
|
|
121
|
+
|
|
122
|
+
| Requirement | Why |
|
|
123
|
+
|---|---|
|
|
124
|
+
| **Python 3.13+** | The project targets 3.13 and is strictly typed. |
|
|
125
|
+
| **A Finanzen.net ZERO account** | Email + password login is the only supported method. |
|
|
126
|
+
| **The ZERO mobile app** | Needed only for order execution: the 2FA push is approved on a paired device. Read-only tools work without it. |
|
|
127
|
+
| **Network access to `mein.finanzen-zero.net`** | Every endpoint lives there. There is no sandbox. |
|
|
128
|
+
| **An MCP host** | Claude Desktop, Claude Code, Cursor, or any client that launches local stdio servers. |
|
|
129
|
+
|
|
130
|
+
## Installation
|
|
131
|
+
|
|
132
|
+
From this checkout, with [uv](https://docs.astral.sh/uv/):
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
git clone https://github.com/wtfuii/zero-mcp.git
|
|
136
|
+
cd zero-mcp
|
|
137
|
+
uv sync
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`uv sync` installs the server and its dependencies into `.venv`. The MCP host should run the
|
|
141
|
+
`zero-mcp` console script through `uv run` (see [Connect an MCP host](#connect-an-mcp-host)).
|
|
142
|
+
|
|
143
|
+
With pip:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install finanzen-zero-mcp
|
|
147
|
+
zero-mcp
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Or run it without installing, with uv:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
uvx --from finanzen-zero-mcp zero-mcp
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The PyPI distribution is `finanzen-zero-mcp`; the console script keeps the shorter name `zero-mcp`.
|
|
157
|
+
`zero-mcp` reads MCP messages from stdin and writes them to stdout. Running it directly in a
|
|
158
|
+
terminal will appear to hang; that is expected β an MCP host normally owns the process.
|
|
159
|
+
|
|
160
|
+
## Configuration
|
|
161
|
+
|
|
162
|
+
Configuration is read from environment variables, which the MCP host passes to the stdio process.
|
|
163
|
+
|
|
164
|
+
| Variable | Required | Default | Meaning |
|
|
165
|
+
|---|---|---|---|
|
|
166
|
+
| `ZERO_USERNAME` | for any broker call | β | The ZERO login **e-mail address** (the client's `email`). |
|
|
167
|
+
| `ZERO_PASSWORD` | for any broker call | β | The ZERO account password. |
|
|
168
|
+
| `ZERO_DEFAULT_DEVICE` | no | β | 2FA device serial used when several devices are enrolled. |
|
|
169
|
+
| `ZERO_MODE` | no | `WRITE` | `READ`, `WRITE` or `YOLO`; see [below](#safety-modes-zero_mode). Case-insensitive. |
|
|
170
|
+
|
|
171
|
+
Startup and `tools/list` work without credentials; a tool call then fails with a clear
|
|
172
|
+
`not configured` message. Credentials are held in memory by the `ZeroClient`, are never written to
|
|
173
|
+
disk by this server, and are never logged. The password is stored as a `SecretStr`.
|
|
174
|
+
|
|
175
|
+
> Environment variables are visible to the spawned process and (depending on the host) to other
|
|
176
|
+
> local processes. Do not commit an MCP config containing your password, and prefer
|
|
177
|
+
> `ZERO_MODE=READ` unless you actively want trading tools.
|
|
178
|
+
|
|
179
|
+
### Safety modes (`ZERO_MODE`)
|
|
180
|
+
|
|
181
|
+
`ZERO_MODE` is one switch with three mutually exclusive, case-insensitive values:
|
|
182
|
+
|
|
183
|
+
| Value | Registered tools | Order confirmation |
|
|
184
|
+
|---|---|---|
|
|
185
|
+
| `READ` | read tools only; the five write tools (watchlist edits, 2FA unlock, place/cancel order) are not registered | not applicable |
|
|
186
|
+
| `WRITE` (default) | all fifteen tools | the model must obtain the user's explicit confirmation before `place_order` or `cancel_order` |
|
|
187
|
+
| `YOLO` | all fifteen tools | no confirmation requirement in the server instructions or order tool descriptions |
|
|
188
|
+
|
|
189
|
+
Tools removed in `READ` never appear in `tools/list`. `WRITE` and `YOLO` differ only in the
|
|
190
|
+
instruction attached to the order tools; neither changes tool annotations. In every mode the broker's
|
|
191
|
+
app-push 2FA and any tool-approval prompt configured in the MCP host still apply β the server cannot
|
|
192
|
+
override its host.
|
|
193
|
+
|
|
194
|
+
> **`YOLO` is a footgun.** It removes the model-level requirement to ask before placing or cancelling
|
|
195
|
+
> an order. Use it only on an account and with amounts you can afford to trade without a human in
|
|
196
|
+
> the loop.
|
|
197
|
+
|
|
198
|
+
## Connect an MCP host
|
|
199
|
+
|
|
200
|
+
Use an **absolute path** to this checkout:
|
|
201
|
+
|
|
202
|
+
```json
|
|
203
|
+
{
|
|
204
|
+
"mcpServers": {
|
|
205
|
+
"finanzen-zero": {
|
|
206
|
+
"command": "uv",
|
|
207
|
+
"args": ["run", "--directory", "/absolute/path/to/zero-mcp", "zero-mcp"],
|
|
208
|
+
"env": {
|
|
209
|
+
"ZERO_USERNAME": "you@example.com",
|
|
210
|
+
"ZERO_PASSWORD": "your-password",
|
|
211
|
+
"ZERO_DEFAULT_DEVICE": "optional-device-serial",
|
|
212
|
+
"ZERO_MODE": "WRITE"
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
- Claude Desktop: `claude_desktop_config.json` (Settings β Developer β Edit Config).
|
|
220
|
+
- Cursor: `.cursor/mcp.json` (project) or `~/.cursor/mcp.json` (global).
|
|
221
|
+
- Any host that accepts `command`/`args`/`env`.
|
|
222
|
+
|
|
223
|
+
GUI hosts often start with a minimal `PATH` that does not contain `uv`. After `uv sync`, point the
|
|
224
|
+
config at the console script directly instead:
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
{
|
|
228
|
+
"mcpServers": {
|
|
229
|
+
"finanzen-zero": {
|
|
230
|
+
"command": "/absolute/path/to/zero-mcp/.venv/bin/zero-mcp",
|
|
231
|
+
"env": {
|
|
232
|
+
"ZERO_USERNAME": "you@example.com",
|
|
233
|
+
"ZERO_PASSWORD": "your-password"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
On Windows the script is `.venv\Scripts\zero-mcp.exe`.
|
|
241
|
+
|
|
242
|
+
### Claude Code
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
claude mcp add finanzen-zero \
|
|
246
|
+
--env ZERO_USERNAME=you@example.com \
|
|
247
|
+
--env ZERO_PASSWORD=your-password \
|
|
248
|
+
--env ZERO_MODE=WRITE \
|
|
249
|
+
-- uv run --directory /absolute/path/to/zero-mcp zero-mcp
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Read-only setup
|
|
253
|
+
|
|
254
|
+
Enable only the ten read tools and keep the trading surface out of the model's context entirely:
|
|
255
|
+
|
|
256
|
+
```json
|
|
257
|
+
{
|
|
258
|
+
"mcpServers": {
|
|
259
|
+
"finanzen-zero": {
|
|
260
|
+
"command": "uv",
|
|
261
|
+
"args": ["run", "--directory", "/absolute/path/to/zero-mcp", "zero-mcp"],
|
|
262
|
+
"env": {
|
|
263
|
+
"ZERO_USERNAME": "you@example.com",
|
|
264
|
+
"ZERO_PASSWORD": "your-password",
|
|
265
|
+
"ZERO_MODE": "READ"
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Restart the MCP host after changing environment variables. The process logs its mode and whether
|
|
273
|
+
credentials are configured to **stderr** on startup, never to stdout.
|
|
274
|
+
|
|
275
|
+
## Usage examples
|
|
276
|
+
|
|
277
|
+
Once the server is connected, ask the assistant in natural language:
|
|
278
|
+
|
|
279
|
+
- βWhat is my current buying power and next closing date?β
|
|
280
|
+
- βSearch for Apple and show me the one-year chart.β
|
|
281
|
+
- βWhich positions do I hold and what are they worth at the latest quote?β
|
|
282
|
+
- βList my open orders and cancel order ID `GBRβ¦`.β
|
|
283
|
+
- βPlace a limit buy for 1 share of Apple at β¬200 on gettex.β
|
|
284
|
+
- βI approved the push β check whether order execution is unlocked.β
|
|
285
|
+
|
|
286
|
+
The assistant discovers the account through the read tools. There is no separate login tool, and the
|
|
287
|
+
shared session is reused across calls.
|
|
288
|
+
|
|
289
|
+
## Authentication and 2FA
|
|
290
|
+
|
|
291
|
+
Login is handled entirely by `finanzen-zero-api`:
|
|
292
|
+
|
|
293
|
+
1. The first broker call performs a Cognito SRP login over HTTP through the broker's `/apic/authc`
|
|
294
|
+
proxy. No browser and no API key are involved.
|
|
295
|
+
2. The one-hour IdToken is refreshed with the refresh-token cookie; a rejected session is
|
|
296
|
+
re-established once and the request retried.
|
|
297
|
+
3. Sessions are in memory. Restarting the MCP server logs in again; no cookies are persisted.
|
|
298
|
+
|
|
299
|
+
Order execution additionally requires the app-push **session TAN** unlock:
|
|
300
|
+
|
|
301
|
+
1. `place_order` / `cancel_order` probe `is_authorized`; when locked they request a push to the
|
|
302
|
+
configured device and wait for approval.
|
|
303
|
+
2. `unlock_order_execution` exposes that step explicitly, with a configurable timeout and no-op when
|
|
304
|
+
already unlocked. It is useful only for pre-authorizing a batch or waiting longer than the order
|
|
305
|
+
tools' built-in 60 seconds.
|
|
306
|
+
3. If exactly one device is enrolled it is used automatically. With several devices, set
|
|
307
|
+
`ZERO_DEFAULT_DEVICE` or call `list_enrolled_devices` and pass a serial; otherwise the call fails
|
|
308
|
+
rather than guessing.
|
|
309
|
+
|
|
310
|
+
Keep the host clock synchronized (NTP): SRP signs a UTC timestamp, and large clock skew makes the
|
|
311
|
+
login fail.
|
|
312
|
+
|
|
313
|
+
## Output conventions
|
|
314
|
+
|
|
315
|
+
- Tool results are JSON objects. Lists are nested under a descriptive key (`positions`, `orders`,
|
|
316
|
+
`entries`, `devices`, `bars`) and usually come with a `count`.
|
|
317
|
+
- Keys use the Python client's **snake_case** field names (the names documented in the
|
|
318
|
+
`finanzen-zero-api` README), not the broker's camelCase wire aliases.
|
|
319
|
+
- Account, position, order and watchlist amounts are **decimal strings** (e.g. `"1000.50"`) so
|
|
320
|
+
precision survives the JSON round-trip. Parse them before doing arithmetic. Market-data values
|
|
321
|
+
such as chart bars and instrument detail quotes keep the broker's wire floats and are numbers.
|
|
322
|
+
- Timestamps are ISO 8601 strings; dates use `YYYY-MM-DD`. Account amounts are EUR; instrument and
|
|
323
|
+
order prices use the instrument's quote currency (`quote_currency_code`), not necessarily EUR.
|
|
324
|
+
- Failures are MCP `isError` results with a human/model-readable message, including the broker's
|
|
325
|
+
HTTP status, error code and field errors when available.
|
|
326
|
+
|
|
327
|
+
## Safety and security
|
|
328
|
+
|
|
329
|
+
- **Real orders.** `place_order` and `cancel_order` are marked `destructiveHint` and
|
|
330
|
+
`readOnlyHint=false`. A market order can fill immediately and cannot be undone. If a placement
|
|
331
|
+
fails after submission (timeout or network error), check `list_orders` before retrying β the order
|
|
332
|
+
may already exist.
|
|
333
|
+
- **Credentials.** Passwords arrive via environment variables and stay in memory. This server does
|
|
334
|
+
not read `.env` files, keychains or config files. Treat MCP host config files as secrets when they
|
|
335
|
+
contain `ZERO_PASSWORD`.
|
|
336
|
+
- **Unofficial endpoints.** The broker can change or block these private endpoints at any time;
|
|
337
|
+
schema drift is surfaced as a tool error rather than silently wrong data. Automating a brokerage
|
|
338
|
+
account may violate the broker's terms β use only an account you are allowed to automate.
|
|
339
|
+
|
|
340
|
+
## Architecture
|
|
341
|
+
|
|
342
|
+
```
|
|
343
|
+
src/zero_mcp/
|
|
344
|
+
βββ server.py # MCPServer (MCP SDK v2), dynamic tool registration, stdio main()
|
|
345
|
+
βββ config.py # ZERO_* env settings, lazy credential validation
|
|
346
|
+
βββ client.py # ClientProvider + lazy, lock-protected shared ZeroClient
|
|
347
|
+
βββ errors.py # zero_api/httpx exceptions β actionable ToolError
|
|
348
|
+
βββ serialization.py # Pydantic model β JSON-safe dict
|
|
349
|
+
βββ __main__.py # python -m zero_mcp
|
|
350
|
+
βββ tools/ # vertical slices, one module per broker area
|
|
351
|
+
βββ account.py
|
|
352
|
+
βββ instrument.py
|
|
353
|
+
βββ portfolio.py
|
|
354
|
+
βββ watchlist.py
|
|
355
|
+
βββ orders.py
|
|
356
|
+
βββ sessiontan.py
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
The MCP server is deliberately thin: it owns argument mapping, error translation, tool annotations,
|
|
360
|
+
`ZERO_MODE` gating and the confirmation/YOLO instructions. Everything else β HTTP, SRP login, refresh,
|
|
361
|
+
2FA, Pydantic models β belongs to [`finanzen-zero-api`](https://github.com/wtfuii/zero-api).
|
|
362
|
+
|
|
363
|
+
## Development
|
|
364
|
+
|
|
365
|
+
```bash
|
|
366
|
+
uv sync
|
|
367
|
+
uv run mypy . # strict; run first
|
|
368
|
+
uv run pytest # offline; no broker calls
|
|
369
|
+
uv build # wheel + sdist
|
|
370
|
+
uvx twine check --strict dist/*
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
To exercise the server by hand, launch it through any MCP client or the MCP Inspector:
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
npx @modelcontextprotocol/inspector uv run --directory . zero-mcp
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Set `ZERO_USERNAME`/`ZERO_PASSWORD` in the inspector's environment only if you intend to make live
|
|
380
|
+
(read-only) calls. There is **no live smoke test in CI** and tests must never contact the broker.
|
|
381
|
+
|
|
382
|
+
See [AGENTS.md](AGENTS.md) for the architecture rules, MCP SDK conventions and contribution
|
|
383
|
+
constraints. Releases are cut from a `vX.Y.Z` tag and published to PyPI through Trusted Publishing;
|
|
384
|
+
see [RELEASING.md](RELEASING.md) for the registry setup and release checklist.
|
|
385
|
+
|
|
386
|
+
## Limitations
|
|
387
|
+
|
|
388
|
+
- **Version coupling.** The wrapper pins `finanzen-zero-api>=0.1.0,<0.2.0`. Model or endpoint
|
|
389
|
+
changes in the alpha client can break tool output until the wrapper is updated.
|
|
390
|
+
- **stdio only.** No HTTP/SSE transport, no multi-user server, no OAuth. One process, one account.
|
|
391
|
+
- **No order modification.** The broker client supports placing and cancelling, not amending.
|
|
392
|
+
- **Blocking 2FA.** `place_order`, `cancel_order` and `unlock_order_execution` wait for the phone
|
|
393
|
+
approval; MCP hosts with a short tool timeout may abort the call before the push is accepted.
|
|
394
|
+
- **No streaming quotes.** Charts and details are snapshots returned by the private endpoints.
|
|
395
|
+
- **One page of orders.** `list_orders` returns a single page plus `has_next`; the page size is
|
|
396
|
+
bounded by the tool's `size` argument (max 1000).
|
|
397
|
+
- **Unknown rate limits.** The broker publishes none; avoid tight polling loops.
|
|
398
|
+
|
|
399
|
+
## Project status
|
|
400
|
+
|
|
401
|
+
Alpha (`0.1.0`). All slices currently supported by `finanzen-zero-api` are wrapped: account,
|
|
402
|
+
instruments, portfolio, watchlist, orders and session TAN/2FA. The tool surface may change between
|
|
403
|
+
minor versions while the wrapper stabilizes. Pin the version you depend on.
|
|
404
|
+
|
|
405
|
+
## License
|
|
406
|
+
|
|
407
|
+
MIT β see [LICENSE](LICENSE). This project is not affiliated with Finanzen.net, Finanzen.net ZERO,
|
|
408
|
+
or Baader Bank.
|