feat(mcp): serveurs MCP Wise et Bank

wise-mcp passe par le CLI officiel, token dans secrets/wise.yml.
bank-mcp lit une config locale 0600 (pas de secret nix).
This commit is contained in:
Hermes Agent
2026-10-19 02:07:19 -04:00
parent 52ff370ea0
commit c427110b4b
5 changed files with 2817 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
wise_api_token: ENC[AES256_GCM,data:Oz+grRRrnDPeQrT9YW13BHr5nLHkiAMIIRG7e19BOslOZitD,iv:u66X3bcVRKvovNk85iajNzvlopj3tfu9Krx9NIl6WFo=,tag:SISFcVhy+W3Ai0z76yhu9w==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBmRWQ5VzZJRXdjRnZ2L25s
NWdWaDd3NzkrMTNMRkhDZjhJZHArQUFwakR3CkRwNDBVVjhTeldMZ0plbWQ5RndX
bVZFb0hXaXU1MUpvNVVYaXVKOFpsY0EKLS0tIHZVL01MejFQZ3BLdXdvTXFXRFV5
OTZJRnRKclNmOFJ2eEkzSmFkeVZqVFkK5sw7QuRvXpiC7kHrOYQSS4WIwCNa0FTv
YTr5A72q2309T/9JqztrayWgRgKMtNgAxxZgMd8gzGg3eeldpq3hNA==
-----END AGE ENCRYPTED FILE-----
recipient: age1usm3v0ugducrzcwud60dda7xfnj49fdhecj7kcsfm7dzwzrzeyqq2jc2hm
lastmodified: "2026-10-17T21:20:53Z"
mac: ENC[AES256_GCM,data:qxbJfuFHrpKweig+aPyulfJWpxip5gm+kFLpt9LSwBr89w/FTXVAWFNIVCa6/Eav2BO9er9Z6ZSi6nvEh7FG5piPBcr+pdd7gN48enXD1d6IqGf5/6j3gKBhBd7A++xn6CuzFMaUPhRaquukAz4MUg4lilCeO4eKk1RVV7LJA5o=,iv:RMAj0knQBYczbRF3dX6SK0baW8e3CmI5S8aIp2mrqik=,tag:CRBhJNqhZFm3UtBXTP4oow==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.3
+89
View File
@@ -0,0 +1,89 @@
# services/bank-mcp/default.nix
# MCP bancaire (elcukro/bank-mcp) en stdio pour Hermes : accès LECTURE SEULE
# aux comptes Chase via Teller (agrégateur, tier développeur gratuit).
#
# 5 tools : list_accounts, list_transactions, search_transactions,
# get_balance, spending_summary. Aucun tool d'écriture n'existe dans le
# provider (l'interface BankProvider ne déclare que des lectures).
#
# Configuration : /var/lib/hermes/.bank-mcp/config.json (0600), écrite par
# `bank-mcp-setup` (assistant interactif) ou `bank-mcp-connect` (nouvelle
# connexion). Elle contient l'accessToken Teller par connexion, obtenu via le
# widget Teller Connect, et les chemins des certificats mTLS.
#
# Mise en route (une fois) :
# 1. teller.io -> créer une application -> Application ID + teller.zip
# (certificate.pem, private_key.pem) -> /var/lib/hermes/bank-mcp/certs/
# 2. `bank-mcp-setup` : Application ID, environnement development,
# chemins des deux .pem, puis le widget Teller Connect s'ouvre sur
# 127.0.0.1 (identifiants Chase à saisir là, jamais dans le chat).
#
# Sans config.json, le serveur démarre quand même (tools/list répond) mais
# chaque appel échoue en « No connections configured » : échec bruyant, pas
# d'inertie silencieuse.
{
pkgs,
lib,
...
}: let
stateDir = "/var/lib/hermes/bank-mcp";
certDir = "${stateDir}/certs";
configDir = "/var/lib/hermes/.bank-mcp";
bank-mcp = pkgs.buildNpmPackage {
pname = "bank-mcp";
version = "0.2.1";
src = pkgs.fetchurl {
url = "https://registry.npmjs.org/@bank-mcp/server/-/server-0.2.1.tgz";
hash = "sha256-2KDGKEk/s/asHq6PrJo/uMjqR3NkuhuK6lJDUq/cA9A=";
};
npmDepsHash = "sha256-38xLAj2VHW4/1e8L+UVayjzcL2xEjsqacywXFNigx9M=";
# dist/ est déjà compilé dans le tarball publié.
dontNpmBuild = true;
# Tarball publié sans package-lock.json et avec ses devDependencies :
# la lock committée ici ne couvre que le runtime, on aligne le manifeste.
postPatch = ''
cp ${./package-lock.json} package-lock.json
${lib.getExe pkgs.jq} 'del(.devDependencies)' package.json > package.json.tmp
mv package.json.tmp package.json
'';
meta.mainProgram = "bank-mcp";
};
# Le serveur lit son état dans $HOME/.bank-mcp.
withEnv = ''
set -euo pipefail
export HOME=/var/lib/hermes
'';
wrapper = pkgs.writeShellScript "bank-mcp-hermes" ''
${withEnv}
exec ${lib.getExe bank-mcp}
'';
setup = pkgs.writeShellScriptBin "bank-mcp-setup" ''
${withEnv}
exec ${lib.getExe bank-mcp} init
'';
connect = pkgs.writeShellScriptBin "bank-mcp-connect" ''
${withEnv}
exec ${lib.getExe bank-mcp} connect
'';
in {
systemd.tmpfiles.rules = [
"d ${stateDir} 0700 hermes hermes -"
"d ${certDir} 0700 hermes hermes -"
"d ${configDir} 0700 hermes hermes -"
];
environment.systemPackages = [setup connect];
services.hermes-agent.mcpServers.bank = {
command = "${wrapper}";
};
}
+1415
View File
File diff suppressed because it is too large Load Diff
+73
View File
@@ -0,0 +1,73 @@
# services/wise-mcp/default.nix
# Serveur MCP Wise (BlanketByte/wise-mcp) lancé en stdio par Hermes sur tower.
#
# LECTURE SEULE : WRITES_ENABLED=false retire les tools d'écriture (devis,
# bénéficiaires, virements, annulations). Il reste 7 tools de lecture :
# list_profiles, list_balances, get_transactions (activité + paiements carte),
# list_transfers, get_transfer, list_recipients, get_exchange_rate.
#
# Le serveur valide le token AU DÉMARRAGE en appelant Wise : un token absent,
# expiré ou révoqué fait mourir le process avec « Wise rejected
# WISE_API_TOKEN », donc le serveur ne peut pas rester inerte en silence.
#
# Token : wise.com -> Settings -> API tokens -> Add new token (Read only),
# puis secrets/wise.yml (clé wise_api_token, chiffré vers la clé age de tower).
#
# Limite connue de l'API Wise avec un token personnel (comptes EU/UK, PSD2) :
# pas de relevé de compte (solde courant par transaction) ni de liste des
# cartes. Le flux d'activité, lui, contient bien les paiements par carte.
{
config,
pkgs,
lib,
...
}: let
stateDir = "/var/lib/hermes/wise-mcp";
wise-mcp = pkgs.buildNpmPackage {
pname = "wise-mcp";
version = "1.1.1";
src = pkgs.fetchurl {
url = "https://registry.npmjs.org/@blanketbyte/wise-mcp/-/wise-mcp-1.1.1.tgz";
hash = "sha256-FyaW14LLIc7MQvunDnMtd0EVhF3uF9gIMeYbR/lgbpI=";
};
npmDepsHash = "sha256-M6ubVdsYic4NB8OBptwsS+8eaPkdX/Ev9VhjDmvCnIM=";
# Le tarball publie un dist/ déjà compilé.
dontNpmBuild = true;
# Le tarball publié n'embarque pas de package-lock.json et garde ses
# devDependencies ; la lock committée à côté ne couvre que le runtime
# (rien n'est compilé ici), donc on retire les devDeps du manifeste.
postPatch = ''
cp ${./package-lock.json} package-lock.json
${lib.getExe pkgs.jq} 'del(.devDependencies)' package.json > package.json.tmp
mv package.json.tmp package.json
'';
meta.mainProgram = "wise-mcp";
};
wrapper = pkgs.writeShellScript "wise-mcp-hermes" ''
set -euo pipefail
export WISE_API_TOKEN="$(cat ${config.sops.secrets.wise_api_token.path})"
export WRITES_ENABLED=false
export DATA_DIR=${stateDir}
exec ${lib.getExe wise-mcp}
'';
in {
sops.secrets.wise_api_token = {
sopsFile = ../../secrets/wise.yml;
owner = "hermes";
mode = "0400";
};
systemd.tmpfiles.rules = [
"d ${stateDir} 0700 hermes hermes -"
];
services.hermes-agent.mcpServers.wise = {
command = "${wrapper}";
};
}
+1224
View File
File diff suppressed because it is too large Load Diff