-
Notifications
You must be signed in to change notification settings - Fork 1
Scripts Example Scripts
Runnable Python examples calling pp-mcp directly, without going through an AI assistant — e.g. to build your own reporting scripts or feed a dashboard. All examples use the official mcp Python SDK (the same package pp-mcp itself depends on) against a streamable-http server. See the note at the end for a stdio variant.
pip install mcp httpxA running pp-mcp instance reachable over HTTP (see Installation), e.g. http://localhost:8080/mcp. If MCP_AUTH_TOKEN is set, every example below needs the Authorization: Bearer <token> header shown in the connection helper.
All examples reuse this snippet — a small connect() async context manager wrapping the MCP handshake:
# pp_mcp_client.py
import json
from contextlib import asynccontextmanager
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
PP_MCP_URL = "http://localhost:8080/mcp"
MCP_AUTH_TOKEN = None # set to your token string, or leave None if auth is disabled
@asynccontextmanager
async def connect():
headers = {"Authorization": f"Bearer {MCP_AUTH_TOKEN}"} if MCP_AUTH_TOKEN else None
async with streamablehttp_client(PP_MCP_URL, headers=headers) as (read, write, _):
async with ClientSession(read, write) as session:
await session.initialize()
yield session
async def call(session: ClientSession, tool: str, **kwargs):
"""Calls a tool and returns its result as a plain Python object (dict/list/str/...)."""
result = await session.call_tool(tool, kwargs)
text = result.content[0].text # pp-mcp tools return JSON-serialized text content
data = json.loads(text)
if isinstance(data, dict) and data.get("status") == "error":
raise RuntimeError(f"{tool} failed: {data['message']}")
return dataEvery example below assumes this module is saved as pp_mcp_client.py next to the script.
# 01_ping.py
import asyncio
from pp_mcp_client import connect, call
async def main():
async with connect() as session:
print(await call(session, "ping"))
asyncio.run(main())Expected output:
pong
# 02_account_balances.py
import asyncio
from pp_mcp_client import connect, call
async def main():
async with connect() as session:
accounts = await call(session, "list_accounts")
for acc in accounts:
balance = await call(session, "get_account_balance", account=acc["uuid"])
print(f"{acc['name']:20} {balance['balance']:>12} {balance['currencyCode']}")
asyncio.run(main())Expected output:
Broker 1240.50 EUR
Savings 8000.00 EUR
# 03_export_transactions_csv.py
import asyncio
import csv
import sys
from pp_mcp_client import connect, call
async def main(date_from: str, date_to: str, out_path: str):
async with connect() as session:
transactions = await call(session, "get_transactions", date_from=date_from, date_to=date_to)
if not transactions:
print("No transactions in this range.")
return
fieldnames = ["date", "type", "accountName", "portfolioName", "securityName", "amount", "currencyCode"]
with open(out_path, "w", newline="", encoding="utf-8") as f:
writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
writer.writeheader()
writer.writerows(transactions)
print(f"Wrote {len(transactions)} transactions to {out_path}")
if __name__ == "__main__":
asyncio.run(main("2025-01-01", "2025-12-31", "transactions_2025.csv"))Expected output:
Wrote 214 transactions to transactions_2025.csv
# 04_holdings.py
import asyncio
from pp_mcp_client import connect, call
async def main():
async with connect() as session:
holdings = await call(session, "get_holdings")
for pos in holdings["positions"]:
print(f"{pos['securityName']:30} {pos['shares']:>10} x {pos['price']:>10} = {pos['value']:>12} {pos['currencyCode']}")
print("\nTotals:")
for currency, total in holdings["totalsByCurrency"].items():
print(f" {total} {currency}")
asyncio.run(main())Expected output:
iShares Core MSCI World 42.0000 84.20 = 3536.40 EUR
Apple Inc. 10.0000 178.55 = 1785.50 USD
...
Totals:
18420.30 EUR
1785.50 USD
# 05_plot_value_history.py
import asyncio
from datetime import date, timedelta
import matplotlib.pyplot as plt
from pp_mcp_client import connect, call
async def main():
date_to = date.today().isoformat()
date_from = (date.today() - timedelta(days=365)).isoformat()
async with connect() as session:
history = await call(
session, "get_holdings_history",
date_from=date_from, date_to=date_to, interval="monthly",
)
dates = [point["date"] for point in history]
# Adjust "EUR" if your base currency differs — remember pp-mcp does NOT convert currencies.
values = [float(point["totalsByCurrency"].get("EUR", 0)) for point in history]
plt.plot(dates, values, marker="o")
plt.xticks(rotation=45, ha="right")
plt.ylabel("Value (EUR)")
plt.title("Portfolio value over the last 12 months")
plt.tight_layout()
plt.savefig("portfolio_value.png")
print("Saved chart to portfolio_value.png")
asyncio.run(main())Requires pip install matplotlib in addition to the base prerequisites.
For a pp-mcp instance configured with PP_PORTFOLIOS_CONFIG (see Installation):
# 06_multi_source_balances.py
import asyncio
from pp_mcp_client import connect, call
async def main():
async with connect() as session:
sources = await call(session, "list_data_sources")
for src in sources:
source_id = src["id"]
print(f"\n== {src['label']} ({source_id}) ==")
accounts = await call(session, "list_accounts", source=source_id)
for acc in accounts:
balance = await call(session, "get_account_balance", account=acc["uuid"], source=source_id)
print(f" {acc['name']:20} {balance['balance']:>12} {balance['currencyCode']}")
asyncio.run(main())Expected output:
== Example1 (example1) ==
Broker 1240.50 EUR
Savings 8000.00 EUR
== Example2 (example2) ==
Broker 3120.00 EUR
If pp-mcp isn't running as a resident HTTP server (see Configuring AI Tools), replace the connection helper's streamablehttp_client(...) block with stdio_client, which starts pp-mcp itself as a subprocess:
from mcp import StdioServerParameters
from mcp.client.stdio import stdio_client
server_params = StdioServerParameters(
command="python3",
args=["-m", "src.main"],
env={"PYTHONPATH": "/path/to/pp-mcp", "MCP_TRANSPORT": "stdio", "PP_FILE_PATH": "/path/to/file.portfolio"},
cwd="/path/to/pp-mcp",
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
# ... same call() helper works unchanged from hereNo bearer token needed here — there's no HTTP layer for stdio.