Agents et MCP
GuideExploitationDepuis la version 0.1.0
Sur cette page
Guide d'exploitation : configurer, déployer et observer la passerelle. Ce que fait la capacité côté produit : guide produit ; l'appeler : guide développeur.
Objectif : rendre vos API et vos serveurs MCP existants appelables par des agents IA, derrière une autorisation, un quota et des métriques communs - sans écrire de serveur MCP.
Trois modes, une surface
%%{init: {'theme':'base','themeVariables':{'fontSize':'13px','lineColor':'#94a3b8'}}}%%
graph LR
A["Agent IA<br/>client MCP"]:::ext
subgraph FW["Flexway - surface /mcp d'un hôte"]
S["Jeton émis pour la surface<br/>tools/list filtré par portées<br/>quota de l'appelant"]:::hub
P["Mode 1 - outil projeté<br/>une opération du contrat"]:::hub
F["Mode 2 - outil fédéré<br/>un serveur MCP existant"]:::hub
end
R["Service REST"]:::spoke
M["Serveur MCP existant"]:::spoke
I["Fournisseur d'identité"]:::secret
A -->|"MCP"| S
S --> P -->|"la décision REST, rejouée"| R
S --> F -->|"MCP"| M
P -->|"jeton échangé, si l'amont l'exige"| I
F -->|"jeton échangé, si l'amont l'exige"| I
classDef hub fill:#4f46e5,stroke:#3730a3,color:#fff,font-weight:bold
classDef spoke fill:#0891b2,stroke:#155e75,color:#fff,font-weight:bold
classDef secret fill:#a855f7,stroke:#7e22ce,color:#fff,font-weight:bold
classDef ext fill:#64748b,stroke:#334155,color:#fff,font-weight:bold
L'échange de jeton n'a lieu que si l'amont l'exige (
token_exchange). Tout ce schéma est construit (0.1.0).
| Vous avez | Mode | Ce que vous écrivez | Exemple |
|---|---|---|---|
| Des services REST décrits en OpenAPI | 1 - outils projetés du contrat | x-flexway-tool sur chaque opération à offrir |
Outils pour agents (MCP) |
| Des serveurs MCP qui tournent déjà | 2 - fédération | un amont protocol: mcp et agents.mcp.servers |
Serveur MCP existant, liste déclarée, Serveur MCP existant, tout ce qu'il annonce |
| Les deux | 3 - mixte | les deux, sur la même surface | Outils projetés et fédérés ensemble |
| Des agents qui se parlent en A2A | A2A (pas MCP : hors de la surface /mcp) |
une carte déclarée et un amont protocol: a2a |
Agents A2A, Agent A2A derrière la passerelle |
Commun aux trois, une fois par hôte : l'URL publique de la surface, qui est l'audience qu'un jeton d'agent doit porter (RFC 8707). Elle est déclarée, jamais devinée depuis la requête - un jeton émis pour un autre serveur MCP ne doit pas ouvrir celui-ci.
agents:
mcp:
resources:
api.example.com: https://api.example.com/mcpMode 1 - outils projetés du contrat
Rien à développer : une opération déjà servie en REST devient un outil.
x-flexway-tool: true # ou { name: get_order, description: "…" }| Étape | Ce que fait la passerelle |
|---|---|
tools/list |
Nom (operationId, ou name), description (summary), schéma d'entrée tiré des paramètres et du corps déclarés |
tools/call |
Construit la requête REST de l'opération et la fait passer, en mémoire, par la même décision qu'un appel REST : portées du contrat, quota, validation du schéma, politiques, délais, coupe-circuit, métriques |
| Résultat | Le corps de la réponse ; isError pour un statut >= 400, avec le problème RFC 9457 ; borné à 1 Mio |
Une opération non marquée n'est jamais offerte. Une opération à réponse en flux ne peut pas être un outil : elle est refusée au chargement.
Mode 2 - serveurs MCP existants (fédération)
Rien à changer chez le serveur ni chez ses agents : la passerelle se place devant lui.
upstreams:
orders-mcp:
url: http://order-service:8080/mcp # son endpoint MCP
protocol: mcp
agents:
mcp:
servers:
orders:
upstream: orders-mcp
tools: [get_order, create_order] # ou "*"| Réglage | Effet |
|---|---|
tools: [...] |
Liste déclarée - le défaut, fermé : un outil ajouté côté serveur n'apparaît pas sans décision |
tools: "*" |
Tout ce que le serveur annonce - un choix explicite |
rename |
Un autre nom sur la surface, par exemple contre une collision |
scopes, tool_scopes |
Portées exigées de chaque outil, ou d'un outil ; vide : un appelant authentifié - jamais anonyme |
rate_classes |
Classe de débit d'un outil ; sinon read si le serveur l'annonce en lecture seule (readOnlyHint), autrement write |
hosts |
Les surfaces qui le servent ; défaut : toutes |
discovery.issuer |
Le serveur exige un jeton même pour lister : la passerelle en demande un pour elle-même (client_credentials) |
discovery.refresh |
Intervalle de redécouverte ; défaut 5 min |
| Fonctionnement | Règle |
|---|---|
| Découverte | En arrière-plan : tools/list du serveur, retenté, rafraîchi. Le démarrage n'attend jamais un serveur ; injoignable, ses outils sont absents, puis apparaissent quand il répond |
| Noms | D'origine. Une collision est refusée au chargement pour une liste déclarée ; pour "*", l'outil en collision n'est pas servi, et c'est dit (/config, journal) |
| Appel | Identité exigée, portées, quota de l'appelant, cloisonnement et coupe-circuit de l'amont, délai de l'amont, résultat borné |
| État | /config (port d'administration), mcp_federation : découvert ou injoignable, outils servis, outils non servis et pourquoi |
Les politiques de transformation (en-têtes, chemin) ne s'appliquent pas aux outils fédérés : ce sont
des appels MCP, pas des requêtes REST du contrat. Seule la politique rate-limit (niveaux global et
hôte) s'applique.
Mode 3 - mixte
Les deux modes sur la même surface : l'agent voit une liste, triée par nom. Les noms doivent être uniques sur un hôte - vérifié au chargement pour les outils déclarés.
Le jeton que reçoit l'amont
Le jeton de l'agent n'est jamais relayé : il a été émis pour la surface, pas pour l'amont, et la spécification MCP l'interdit. Ce que l'amont reçoit dépend de ce qu'il attend :
| L'amont | Configuration | Il reçoit |
|---|---|---|
| Fait confiance à la passerelle (réseau privé, maillage) | Rien | Aucun Authorization ; le tenant par l'en-tête que pose la passerelle (outils projetés) |
| Vérifie lui-même les jetons | token_exchange: { audience } sur l'amont, client sur l'émetteur |
Un jeton émis pour lui, au nom de l'appelant, par le fournisseur de l'appelant (RFC 8693) - Jeton échangé vers l'amont |
upstreams:
orders-mcp:
token_exchange: { audience: order-service } # le jeton que le serveur attend
auth:
issuers:
- name: customers
client: { id: flexway, secret: "file:/etc/flexway/secrets/client" } # une référence, relue à chaque échangeSi le fournisseur refuse l'échange, l'appel est refusé ; s'il est injoignable, l'appel échoue - jamais de repli sur le jeton de l'agent.
Chaîne d'agents bornée
Une chaîne réelle est humain -> agent A -> agent B -> service. Le fournisseur d'identité l'écrit
dans le jeton, en revendications act imbriquées (RFC 8693) : sub reste l'humain, chaque act nomme
un agent. La passerelle la lit et la borne :
agents:
delegation:
max_depth: 2 # agents au plus ; 0 : aucun jeton délégué accepté
max_age: 15m # depuis l'authentification de l'humain (auth_time, sinon iat)
introspection:
cache: 10s # délai maximal pour voir une révocation# contrat - une opération sensible resserre, jamais n'élargit
x-flexway-delegation: { max_depth: 1, max_age: 5m }| Règle | Comportement |
|---|---|
Jeton direct (sans act) |
Pas une délégation : ni borne, ni introspection |
| Profondeur, âge | Au-delà de la borne -> 403. La borne d'une opération est le minimum de la configuration et de la sienne |
| Révocation | Un jeton délégué est vérifié auprès de son émetteur (introspection RFC 7662, avec le client de l'émetteur) ; révoqué -> 401 dans le délai cache ; émetteur injoignable -> 503, jamais un passage sans vérification |
| Rétrécissement | Un échange de jeton (Le jeton que reçoit l'amont) demande au plus les portées de l'appelant ; un fournisseur qui en accorde davantage -> 403, l'amont n'est pas appelé |
| Partout | REST, connexion à la surface MCP, outils projetés (bornes de l'opération) et fédérés |
| Traces | delegation.depth, delegation.actors - les agents, jamais l'humain |
Exemple complet : Chaîne d'agents bornée.
Autorisation, quota, observabilité
| Sujet | Règle |
|---|---|
| Jeton de l'agent | Émis pour l'URL de la surface (indicateur de ressource resource= à la demande du jeton) ; un 401 donne resource_metadata, et GET /.well-known/oauth-protected-resource/mcp dit où l'obtenir |
tools/list |
Ne montre que ce que l'appelant peut appeler |
| Outil projeté | Les exigences du contrat (security), comme en REST |
| Outil fédéré | scopes / tool_scopes ; une identité toujours |
| Quota | Le même seau par appelant, quelle que soit la porte - REST, outil projeté, outil fédéré |
| Métriques | flexway_mcp_calls_total (outil, issue, client, tenant, surveillé, cause de refus), flexway_mcp_connections_refused_total (motif), flexway_mcp_federated_duration_seconds (outil), flexway_mcp_discoveries_total (serveur, issue), flexway_token_exchanges_total (amont, issue), flexway_delegation_refused_total (raison : depth, age, revoked, widened, unverifiable) |
/config |
mcp_tools (outils projetés par hôte), mcp_federation (état de chaque serveur, en direct) ; agents.mcp.clients et tool_unattended |
Quels clients, et avec ou sans humain (1.0.0) - la surface admet par défaut tout client dont
le jeton est valide. agents.mcp.clients la ferme aux clients déclarés, nommés par l'URL de leur
document de métadonnées (révision MCP 2026-07-28) ; un outil peut être retiré aux agents non
surveillés - un client qui agit pour lui-même, dont le jeton a pour sujet le client :
agents:
mcp:
clients: ["https://agents.example.com/*", "https://partner.example/agent.json"] x-flexway-tool: { name: create_order, unattended: false } # caché et refusé sans humainUn fournisseur qui donne au compte de service un sujet à lui (Keycloak : un identifiant
d'utilisateur) se déclare : service_accounts: { claim: preferred_username, prefix: service-account- }
sur l'émetteur.
Déclarer agents.mcp.clients est aussi ce qui rend chaque agent visible dans la console : sans
liste, tous les clients comptent sous _undeclared.
Vérifier
GET /.well-known/oauth-protected-resource/mcprenvoie la ressource et son serveur d'autorisation ;- un client MCP sans jeton reçoit
401avecresource_metadata; - avec un jeton émis pour l'URL de la surface,
tools/listmontre les outils permis - projetés et fédérés ; - un appel compte dans le même quota que l'appel REST ;
/configmontremcp_toolsetmcp_federation;flexway_mcp_calls_totalporte leclientde l'agent et, sur un refus, sa cause.
vers l'amont ; la chaîne d'agents bornée. Les clés d'API arrivent avec le change suivant ; les ressources et les invites MCP d'un serveur fédéré ne sont pas relayées.*