Metadata-Version: 2.5
Name: mcp-appium
Version: 0.1.0
Summary: Serveur MCP qui donne à un assistant les éléments réels d'une application Appium, au lieu de le laisser les inventer.
Project-URL: Homepage, https://github.com/julien-becheny/mcp-appium
Project-URL: Issues, https://github.com/julien-becheny/mcp-appium/issues
Author: Julien Becheny
License: MIT
License-File: LICENSE
Keywords: android,appium,ios,llm,mcp,mobile-testing,test-automation
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Testing
Requires-Python: >=3.10
Requires-Dist: appium-python-client>=3.0.0
Requires-Dist: lxml>=4.9.0
Requires-Dist: mcp>=2.0.0
Requires-Dist: requests>=2.28.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.5.0; extra == 'dev'
Provides-Extra: image
Requires-Dist: pillow>=10.0.0; extra == 'image'
Description-Content-Type: text/markdown

# mcp-appium

Un assistant qui écrit des tests mobiles invente des sélecteurs. Il propose
`accessibility_id=bouton_valider` parce que c'est ce qu'un développeur aurait
écrit, et le test échoue parce que l'application expose autre chose.

Ce serveur MCP lui donne **l'écran réel**.

```
pip install mcp-appium
```

## Ce qu'il fait

Onze outils, exposés à l'assistant via le Model Context Protocol :

| Outil | Rôle |
|---|---|
| `connect_to_session` | Se rattache à une session Appium **déjà ouverte** |
| `get_page_source` | L'arbre de l'écran, simplifié ou brut |
| `find_elements` | Recherche par sélecteur ou par texte |
| `suggest_locators` | Des sélecteurs qui existent, classés par robustesse |
| `get_element_info` | Attributs, position, état d'un élément |
| `screenshot` | L'écran, réduit avant envoi |
| `tap_element` | Clic, avec vérification que l'écran a bougé |
| `type_text` | Saisie dans un champ |
| `go_back` | Retour arrière |
| `get_session_info` | Plateforme, appareil, identifiant de session |
| `close_session` | Libère l'appareil, **si ce serveur a ouvert la session** |

Android, iOS, iPadOS et Windows.

## Le cas courant : observer une session existante

Un test tourne, il échoue sur un élément. Tu demandes à l'assistant ce que
l'écran contient vraiment.

```
connect_to_session()
```

Sans argument, le serveur cherche une session active sur
`http://127.0.0.1:4723` et s'y rattache. **Il ne crée rien, ne redémarre rien**,
et aucune configuration n'est nécessaire.

C'est le mode à privilégier : l'assistant voit exactement ce que le test voit,
au moment où il le voit.

## Créer une session

Si aucune session n'existe, le serveur peut en ouvrir une. Il lui faut alors des
capabilities, déclarées dans `appium-caps.json` à la racine de ton projet :

```json
{
  "platformName": "Android",
  "automationName": "UiAutomator2",
  "appPackage": "com.exemple.app",
  "appActivity": ".MainActivity"
}
```

Les clés sont préfixées par `appium:` automatiquement quand il le faut.

Plusieurs plateformes dans le même fichier :

```json
{
  "android": { "platformName": "Android", "automationName": "UiAutomator2", "appPackage": "com.exemple.app" },
  "ios":     { "platformName": "iOS", "automationName": "XCUITest", "bundleId": "com.exemple.app" }
}
```

La variable `MCP_APPIUM_PLATFORM` choisit laquelle. À défaut, la première
déclarée. Deux autres variables existent : `MCP_APPIUM_CAPS` pour passer le JSON
directement, et `MCP_APPIUM_CAPS_FILE` pour désigner un autre fichier.

## Déclarer le serveur

Dans VS Code, `.vscode/mcp.json` :

```json
{
  "servers": {
    "appium": {
      "type": "stdio",
      "command": "mcp-appium"
    }
  }
}
```

Le format est le même pour les autres clients MCP : une commande, transport
standard.

## Le parti pris qui compte : borner les sorties

Un arbre de vue Appium brut dépasse couramment les cinquante mille caractères.
Envoyé tel quel, il sature la fenêtre de contexte du modèle avant de lui avoir
appris quoi que ce soit. Pire : ce qui entre dans le contexte y reste, et se
repaie à chaque échange suivant de la conversation.

Toutes les sorties sont donc plafonnées, et le serveur le dit quand il coupe :

- arbre simplifié à 400 lignes, avec les seuls attributs qui servent à cibler ;
- source brute à 40 000 caractères ;
- 15 éléments détaillés au maximum dans une recherche ;
- captures réduites à 1280 pixels de large.

Un outil d'inspection qui ne borne pas ses sorties est inutilisable en
conversation, quelle que soit la qualité de ce qu'il expose.

## Deux autres partis pris

**Un tap vérifie son effet.** `tap_element` compare l'écran avant et après, et
signale explicitement un clic resté sans conséquence. Un élément désactivé ou
recouvert répond à `click()` sans rien faire : sans cette vérification,
l'assistant croit avoir avancé et enchaîne dans le vide.

**Une session ne se ferme que si on l'a ouverte.** `close_session` libère
l'appareil quand le serveur a créé la session, et se contente de s'en détacher
sinon. Fermer la session d'un test en cours couperait ce test.

**`suggest_locators` classe par robustesse.** L'identifiant d'accessibilité
d'abord, le XPath sur le texte en dernier, avec la mention qu'il cassera au
prochain changement de libellé.

## Ce qu'il ne fait pas

Il **n'écrit pas de tests** et n'impose aucun framework. Il expose l'état de
l'application, l'assistant fait le reste avec les outils que tu utilises déjà.

Il ne dépend d'**aucun service d'IA**. Ni clé d'API, ni compte, ni appel sortant :
le seul réseau qu'il touche est ton serveur Appium local.

Il ne **remplace pas Appium Inspector** pour l'exploration manuelle. Il sert à
donner ces informations à un modèle, ce qu'une interface graphique ne sait pas
faire.

## Licence

MIT.
