feat: connecteurs Discord/Telegram/MCP + outils MCP dynamiques au registre (v7.57.0)
- presets Discord / Telegram : colonnes kind + auth (bearer/bot/none) sur
agent_connectors (migration 34) ; Discord envoie le jeton préfixé dans
l'en-tête d'autorisation, Telegram le place dans l'URL via le substitut
{secret} remplacé à l'appel — jamais stocké en clair
- Teams : scope ChannelMessage.Read.All ajouté aux Graph scopes M365
- app/services/mcp_client.py : handshake initialize → notifications/initialized
→ tools/list → tools/call (JSON-RPC 2.0, réponse JSON ou premier data: d'un
text/event-stream), garde SSRF, outils cachés en base (tools_json) —
le bouton « Tester » rejoue le handshake
- ToolRegistry._all() merge le cache MCP à chaque run : outils exposés au LLM
sous mcp_<serveur>_<outil> avec leur inputSchema, dispatch tools/call ;
serveur désactivé → outil absent du schéma, échec réseau → ToolResult(error)
- menu + : champ Type dans le formulaire connecteur (preset URL + auth),
badge « · N outil(s) » sur la fiche d'un serveur MCP
- tests : tests/test_v757_mcp_discord.py (12 tests, 0 appel réseau réel) ;
suite complète 1331 verts, ruff 0, eslint 0
- livraison : VERSION + app/main = 7.57.0, OpenAPI 523 chemins, CHANGELOG,
ROADMAP phase 7 cochée, avenant phase 7 dans docs/V74_Agent_Plus_Menu.md
This commit is contained in:
@@ -75,7 +75,8 @@
|
||||
galleryFilter: '',
|
||||
canvases: [], canvas: null,
|
||||
connectors: [], connector: null,
|
||||
connectorForm: {name: '', url: '', secret: ''},
|
||||
connectorForm: {name: '', url: '', secret: '', kind: 'custom',
|
||||
auth: 'bearer', hint: ''},
|
||||
cmdFocus: -1, slashDismiss: false,
|
||||
toastMsg: '', toastErr: false, _toastTimer: null,
|
||||
_livePh: null,
|
||||
@@ -1270,7 +1271,8 @@
|
||||
_connectorStatus(c){
|
||||
var mark = c.status === 'ok' ? '✓ ' : (c.status === 'error' ? '✗ '
|
||||
: (c.status === 'missing' ? '⚠ ' : '? '));
|
||||
return mark + (c.detail || c.status) + (c.enabled ? '' : ' · désactivé');
|
||||
var tools = (c.kind === 'mcp' && c.tools_count) ? ' · ' + c.tools_count + ' outil(s)' : '';
|
||||
return mark + (c.detail || c.status) + tools + (c.enabled ? '' : ' · désactivé');
|
||||
},
|
||||
_connectorItems(){
|
||||
var self = this;
|
||||
@@ -1377,6 +1379,20 @@
|
||||
self.fetchConnectors();
|
||||
}).catch(function(){ self.toast('Suppression impossible (réseau).', true); });
|
||||
},
|
||||
// Type choisi dans le formulaire → URL / schéma d'auth / aide pré-remplis.
|
||||
connectorPreset(){
|
||||
var f = this.connectorForm;
|
||||
var presets = {
|
||||
custom: {url:'', auth:'bearer', hint:'URL publique de votre API (hôtes privés refusés)'},
|
||||
discord: {url:'https://discord.com/api/v10', auth:'bot',
|
||||
hint:'Discord Developers → Bot Token (jeton en Authorization)'},
|
||||
telegram:{url:'https://api.telegram.org/bot{secret}', auth:'none',
|
||||
hint:'BotFather → Bot Token ({secret} complète l\u2019URL)'},
|
||||
mcp: {url:'', auth:'bearer', hint:'URL du endpoint MCP (streamable HTTP)'}
|
||||
};
|
||||
var p = presets[f.kind] || presets.custom;
|
||||
f.url = p.url; f.auth = p.auth; f.hint = p.hint;
|
||||
},
|
||||
connectorCreate(){
|
||||
var self = this, f = this.connectorForm;
|
||||
if(!(f.name || '').trim() || !(f.url || '').trim()){
|
||||
@@ -1384,7 +1400,8 @@
|
||||
}
|
||||
fetch('/api/agent/connectors', {
|
||||
method:'POST', headers:{'X-CSRF-Token': getCsrf(), 'Content-Type':'application/json'},
|
||||
body: JSON.stringify({name: f.name, url: f.url, secret: f.secret || ''})
|
||||
body: JSON.stringify({name: f.name, url: f.url, secret: f.secret || '',
|
||||
kind: f.kind || 'custom', auth: f.auth || 'bearer'})
|
||||
}).then(function(r){ return r.json().then(function(d){ return {ok:r.ok, d:d}; }); })
|
||||
.then(function(res){
|
||||
if(!res.ok){ self.toast((res.d && res.d.detail) || 'Création impossible.', true); return; }
|
||||
@@ -1558,7 +1575,8 @@
|
||||
this.connector = it.connector; this.plusSection = 'connector'; this.plusFocus = 0; return;
|
||||
}
|
||||
if(it.action === 'connector-new'){
|
||||
this.connectorForm = {name:'', url:'', secret:''};
|
||||
this.connectorForm = {name:'', url:'', secret:'', kind:'custom',
|
||||
auth:'bearer', hint:''};
|
||||
this.plusSection = 'connector-new'; this.plusFocus = -1; return;
|
||||
}
|
||||
if(it.action === 'connector-probe'){ this.connectorProbe(); return; }
|
||||
|
||||
Reference in New Issue
Block a user