Montrose API (MCP) - Programmera mot det

Den här tråden fokuserar primärt på att använda Montrose:s MCP som ett vanligt API från kod. Dvs, direkta anrop från program eller skript, ej från en AI-bot.


Conzums pekade ut att Montrose nu har en MCP-endpoint:

Den går självklart att använda från AI-bottar, men det borde också gå bra att använda den som ett vanligt API!

Jag ser framför mig hur man kan t ex kan suga in siffror i sitt kalkylblad eller förbereda fondköp med rätt balansering, beräknat mot nuvarande innehav.

Vet inte om man skulle våga lita på ett lib som någon annan lägger upp, men man kan ju knappa egna anrop iaf :slight_smile: .

API

Verkar vara ett read-only-API där man kan förbereda ordrar åt användaren. Montrose skriver:

Du som kund kan exempelvis be om att köpa en specifik aktie för tillgänglig kassa i portföljen. Agenten förbereder då ordern i AI-gränssnittet och skickar tillbaka en länk vidare till Montrose, där du loggar in och själv godkänner ordern.

Vad kan jag göra med Montrose MCP?

Du kan till exempel:

  • se hur din portfölj är fördelad mellan olika branscher
  • få en överblick över dina största innehav
  • följa nyheter kopplade till din portfölj
  • få hjälp att analysera din exponering
  • förbereda ett köp baserat på din tillgängliga kassa

(Text nedan lyft delvis från Montrose #619)

Kan inget om MCP-protokollet och jag misslyckades med att använda ett färdigt interaktivt utforskningsprogram (mcp-probe), men ett snabb-Claude:at Python-script gav nedanståend “tools” vid förfrågan mot servern. (Tips på att använda ett färdigt verktyg mottages gärna.)

==================================================
  TOOLS (3)
==================================================

  • get_holdings
    Returns holdings for either one account (when accountId is provided) or all accessible accounts. Use GetUserAccounts first to find valid account IDs.
    Parameters:
      - accountId (['string', 'null']) *: Optional account ID. If omitted, holdings are returned for all accessible accounts.

  • create_trade_ticket
    Creates a pre-filled trade ticket URL for the Montrose app. Specify side (Buy/Sell), quantity or amount, and an instrument identifier. Instruments can be specified by orderbookId directly, or by ticker/name which will be resolved automatically. Returns a URL that opens the trade ticket in the Montrose app with the order details pre-filled.
    Parameters:
      - side (string) *: The side of the order: Buy or Sell.
      - orderbookId (['integer', 'null']): Optional orderbookId (int) to identify the instrument directly.
      - ticker (['string', 'null']): Optional ticker (string) to identify the instrument by ticker symbol, e.g. "VOLV B".
      - name (['string', 'null']): Optional instrument name (string) to search for the instrument.
      - quantity (['number', 'null']): Optional number of shares to trade. Exactly one of quantity or amount must be provided.
      - amount (['number', 'null']): Optional SEK amount to trade. Exactly one of quantity or amount must be provided.
      - price (['number', 'null']): Optional price for the order.
      - accountId (['string', 'null']): Optional account ID. Use GetUserAccounts to find valid account IDs.

  • get_user_accounts
    Returns all user accounts with stable account IDs and display names. Use this tool to discover valid account IDs before calling GetHoldings for a specific account.

Anonymiserat resultat från get_holdings, med accountId: null:

[
  {
    "accountId": "00000000-0000-0000-0000-000000000000",
    "accountNumber": "1234567",
    "accountName": "Mitt konto",
    "currency": "SEK",
    "summary": {
      "totalMarketValue": 123.45,
      "availableForPurchase": 12.34,
      "totalValue": 123.45,
      "currency": "SEK"
    },
    "positions": [
      {
        "instrumentName": "L\u00C4NSF\u00D6RS\u00C4KRINGAR GLOBAL INDEX",
        "ticker": "LF GLOB",
        "orderbookId": 3361,
        "possibleOrderbookIds": [],
        "quantity": 12.34,
        "marketValue": {
          "accountCurrency": 123.45,
          "instrumentCurrency": 123.45
        },
        "unrealizedResult": {
          "accountCurrency": 12.34,
          "instrumentCurrency": 12.34
        },
        "unrealizedResultPercent": 1.23,
        "instrumentCurrency": "SEK",
        "fxRate": 1
      }
    ]
  }
]

Python-script

Här är skriptet jag använt ovan:

mcp-query.py (Ser rimligt men kanske inte optimalt ut. Såg inga rader som läcker data.)
requirements.txt

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python3 mcp-query.py

Rust?

Lite sugen på att se om det går att göra något i Rust, men som sagt för dåligt insatt i MCP. Det här kanske är en bra start:
https://github.com/modelcontextprotocol/rust-sdk/blob/main/docs/OAUTH_SUPPORT.md
https://github.com/modelcontextprotocol/rust-sdk/blob/main/examples/clients/src/auth/oauth_client.rs

1 gillning

Mcp är ju ett api som du är inne på. Ser inga problem med att göra som du tänkt. Men AI är väldigt bra på att sammanfatta saker, varför lägga i excel?

Sammanställning av konton från olika banker och över tid. Garanterat inga slumpmässiga felräkningar. Vet inte heller om jag vill dela min data till AI-leverantörerna.

Kör du claude code eller motsvarande så kan du bara be den verifiera matten, då skriver den ett python script som verifierar. Kommer nog få jobba lite med att generera slides, excel osv framåt.. inte jobbat så mycket med annat än kod innan så lite dålig koll.

Flytten till Montrose känns mer och mer rätt!

Jag skulle tro att det enklaste sättet för en person utan utvecklarbakgrund är att använda ett redan befintligt gränssnitt med MCP funktionalitet, som t.ex. Claude Desktop eller Gemini Cli. Att bygga en helt egen integrationslösning verkar lite overkill.

Nu har jag lyckats knappa ihop ett Rust-bibliotek. Kommer nog stöka runt en del i det efter hand, då jag testat lite nya saker och inte är expert på Rust-struktur.

tmr-client-sample använder tmr-client för att prata med Montrose.

Har problem med att jag inte lyckas göra en refresh av den OAuth token man får, så efter ett dygn måste man kasta ~/.config/tmr-client/credentials.json och göra en ny autentisering mot Montrose. Har inte lyckats lista ut vad felet beror på. Tycker förfrågan ser rätt ut och den görs av bibliotek jag använder, inte av min egen kod. :confused:

1 gillning

Mekar med att få in client_secret lite halvsnyggt i rmcp, eftersom den inte skickas med vid OAuth refresh.

Med inpetad client_secret har jag dock ändå problem med att MCP-resursen vägrar acceptera den nya access token man får. Refresh vill dock använda den nya refresh token man får..

Eventuellt kör resursen vidare på den initiala access token:en även efter 24h (expires_in). :thinking:

Någon som har en aning om OAuth får gärna testa skripten jag skapade för “manuell” OAuth-hantering:

https://github.com/thomasa88/tmr-client/tree/main/manual-request

# Authenticate with server
./02-auth.sh

# Use credentials to talk to MCP -> OK
./03-mcp-init.sh credentials.json

# Refresh credentials
./03-refresh.sh

# Use new credentials -> 401
./03-mcp-init.sh credentials.json

# Use old credentials -> 200
./03-mcp-init.sh credentials.json.1

# Refresh the new credentials -> OK
./03-refresh.sh

Kan förresten nästa svära på att det tidigare stod att MCP får tillgång till kontouppgifterna i 90 dagar, men kanske minns fel?

Hemnet gjorde en app till chatgpt, gillade konceptet och känns som framtiden.

Inte fått ordning på OAuth refresh än. Vågar jag börja misstänka felkonfiguration hos Montrose? :grimacing:

Men! Man kan ha kul ändå! :partying_face:

Se aktuellt innehav. Fördelningen räknar med kontanter.

> cargo r --example=show-data
2026-04-05T13:28:42.251673Z  INFO tmr_client::client: Establishing authorized connection to MCP server...
2026-04-05T13:28:42.526821Z  INFO tmr_client::client: Initialized authorization manager from credential store
2026-04-05T13:28:42.854365Z  INFO tmr_client::client: Successfully connected to MCP server
-------- Accounts --------
Account: xxxxxxxxxxxxxxxxxxxxxxxxx 1234567 Konto

-------- Holdings --------
Account: 1234567 Konto
  Account ID: xxxxxxxxxxxxxxxxxxxxxxxxx
  Currency: SEK
  Total market value: 422.39 SEK
  Available for purchase: 200.0 SEK
  Total value: 622.39 SEK
  Position: Cash
    Value: 200.00 SEK (32.13%)
  Position: LÄNSFÖRSÄKRINGAR GLOBAL INDEX
    Order book ID: 3361
    Ticker: LF GLOB
    Quantity: 1
    Value: 222.39 SEK (67.87%)

Köpa enligt given fördelning.

> cargo r --example=create-balanced-trade -- --help

Usage: create-balanced-trade <margin/total SEK> [ …]

Margin specifies how much cash to leave in the account after the trade, to account for fees and currency conversion costs. Specifying a negative value sets the total amount to invest, instead of the margin.

No trade will be initiated if the amount to invest is more than the available cash in the account.

> cargo r --example=create-balanced-trade Konto 100 "LF GLOB" 90 "LF TILLVÄXT" 10

Using account Konto (1234567) with 200.00 SEK

Will buy the following instruments for a total of 100.00 SEK:
LF GLOB               90% (     90.00 SEK)
LF TILLVÄXT           10% (     10.00 SEK)

Continue? (y/N) y

Creating trade tickets...
LF GLOB               90% (     90.00 SEK): https://app.montrose.io/trade?ticketId=xxxxxx
LF TILLVÄXT           10% (     10.00 SEK): https://app.montrose.io/trade?ticketId=xxxxxx
Done

Nästa idé är ett program som köper för att sträva mot en given målfördelning på kontot. Så har man lite av ett visst värdepapper så köps det mer av det.

Som jag förstår det, efter att ha testat runt lite själv är att endast godkänta tjänster kan genomföra OAuth för att få token för MCP. Kan köra min lilla app alldeles utmärkt i Claude, Cursor osv men så fort man försöker köra det som en webapp exempelvis så står det att tjänsten inte är godkänd.

Min kod har inget problem med initial auth :). Kallar sig för TMR Sample Client i and exemplena.

Fick dock patcha det lib som mitt lib använder, för att få med client_secret i refresh. Om du kikar i mappen manual_request i repot så kan du se shell-script som skickar alla requests som behövs med curl.

Efter mycket svett (men inga tårar) så har jag fått till ett exempel som köper för att gå mot önskad balans på kontot! :hot_face: :tada:

Usage: create-trade-towards-allocation <account name> <margin/-total SEK> <ticker> <percentage> [<ticker> <percentage> …]

Margin specifies how much cash to leave in the account after the trade, to account for fees and currency conversion costs. Specifying a negative value sets the total amount to invest, instead of the margin.

No trade will be initiated if the amount to invest is more than the available cash in the account.

Lite simulerade siffror:

> cargo r --example=create-trade-towards-allocation Spar 200 "LF GLOB" 80 "LF TILLVÄXT" 10 "PLUS ALLA SV" 10

Using account Spar (1234567) with 500.00 SEK

Will buy the following instruments for a total of 300.00 SEK:

Ticker               | Current allocation         | Wanted allocation          | Trade                         | Resulting allocation
---------------------|----------------------------|----------------------------|-------------------------------|---------------------------
LF GLOB              |       950.00 SEK ( 76.61%) |      1232.00 SEK ( 80.00%) |       +244.50 SEK (  +0.95pp) |      1194.50 SEK ( 77.56%)
LF TILLVÄXT          |        90.00 SEK (  7.25%) |       154.00 SEK ( 10.00%) |        +55.49 SEK (  +2.18pp) |       145.49 SEK (  9.44%)
PLUS ALLA SV         |       200.00 SEK ( 16.12%) |       154.00 SEK ( 10.00%) |         +0.00 SEK (  -3.14pp) |       200.00 SEK ( 12.98%)
---------------------|----------------------------|----------------------------|-------------------------------|---------------------------
Total                |      1240.00 SEK (100.00%) |      1540.00 SEK (100.00%) |       +300.00 SEK (  +0.00pp) |      1540.00 SEK (100.00%)

Continue? (y/N) y

Creating trade tickets...
LF GLOB             :       +244.50 SEK https://app.montrose.io/trade?ticketId=12344-3333333-444444-555555
LF TILLVÄXT         :        +55.49 SEK https://app.montrose.io/trade?ticketId=12344-3333333-444444-555555
Done

pp = percentage point = procentenhet

2 gillningar

Har nu bekräftat med Claude att kopplingen pajar efter 24h och måste göras om.

Så 98% säker på att felet är på Montrose:s sida.

1 gillning

Inte för att jag har Montrose men rent spontant låter det här inte som ett fel. Det låter mer som att din token behöver förnyas efter 24 timmar och att antagandet nedan är fel.

1 gillning

Vad säger Montrose support?

Senaste budet, för någon vecka sen, var att de skulle kika på det.

De säger att access token ska vara giltig i 7 dagar.

Tycker det är konstigt om Claude inte försöker en refresh, men det är ju bara ett antagande från min sida.

Har försökt att inte göra token refresh och att göra det. Får såhär både vid auth och refresh:


< HTTP/2 200
< date: Mon, 06 Apr 2026 09:50:37 GMT
< content-type: application/json
< content-length: 183
< server: istio-envoy
< x-envoy-upstream-service-time: 76
<
< {
<   "token_type": "bearer",
<   "access_token": "SCRUB",
<   "refresh_token": "SCRUB",
<   "scope": "mcp",
<   "expires_in": 86400
< }

86400/3600=24

Som sagt får jag göra hur många refresh jag vill, men det är bara den första access token jag fått som fungerar (i 24h).

Lät Github Copilot löpa lös i en container på min kod och testskript och den kom också fram till att resursen inte verkar använda den nya access token som skapas. Säger inte att den har rätt, men lite mer test gjort iaf.

Fick svar nu att de hade hittat felet och att en fix är på gång :slight_smile:

2 gillningar