Skip to main content
Glama

apic

Ein App-zu-API-Compiler. Richte ihn auf eine Web-App ohne API für Agenten. Ein Computer-Use-Agent erkundet die UI, verifiziert das Gefundene durch Ausführung und erzeugt einen typisierten MCP-Server für die App.

Playwright MCP interpretiert die App bei jedem Aufruf. apic kompiliert sie einmal.

An einem Tag solo gebaut beim {Tech: Europe} × VEED Hackathon, London, 22. August 2026.

apic-ui.vercel.app — dort gibt es das Demo-Video, was jedes Partnermodell entscheidet und was der Compiler gemessen hat.

Öffentliche Verbraucher-Websites

apic --read https://example.com kompiliert die öffentliche, nur lesbare Oberfläche jeder Verbraucher-Website zu MCP-Tools. Es beginnt an dieser Website (plus optionalen Same-Site-Seeds), entdeckt Suchfelder, Filter und wiederkehrende Ergebniskarten und erzeugt nur Tools, deren Zeilen einen kalten Replay überstehen. Es nimmt keine Deliveroo-Routen, kein Restaurant-Vokabular, kein Konto, keinen Warenkorb und keinen Checkout an.

Für eine bekannte Sammlungs-/Artikelseite übergib sie explizit als Same-Site-Direkt-Seed: APIC_READ_DIRECT_URL=https://example.com/catalog/item apic --read https://example.com. Vom Agenten gelieferte URLs sind auf den Ursprung beschränkt, der in das Rezept kompiliert wurde.

Ein Prompt, keine Ziel-URL

Wenn APIC als MCP-Server verbunden ist, verwende für eine normale Verbraucherfrage fulfill_request statt compile_app:

{ "request": "Find me the cheapest pizza near 17 & 18 Clere Street" }

Der Server verwendet Tavily, um öffentliche Kandidatendienste zu finden, OpenAI, um den kompilierten Ablauf auszuwählen und zu betreiben, h, um mehrdeutige Lese-Steuerungen zu priorisieren, Pioneer, um zu klassifizieren, ob Sonden sinnvolle Ergebnisse geliefert haben, und fal nur dort, wo diese Klassifizierung eine visuelle Entscheidung braucht. Er versucht eine kleine, ursprungsverschiedene Fallback-Menge, wenn ein Kandidat angefochten wird oder keinen abspielbaren öffentlichen Ablauf hat. Er loggt sich nie ein, bestellt nie, führt keinen Checkout durch und umgeht keine Challenge. Die überlebenden Tools werden kalt verifiziert, als Beleg zurückgegeben und auf demselben MCP-Server für spätere Aufrufe registriert.


Related MCP server: mcp-apps-demo-engine

Demo

Auf der Seite ansehen: apic-ui.vercel.app — zwei Minuten, ungeschnitten: die Kompilierung, die generierten Tools in einer Live-Sitzung und der Beobachter, der eine UI-Änderung von selbst erkennt.

Dieselbe Seite enthält die Zahlen, die dieses README berichtet, die Aufschlüsselung pro Partner und das Installations-Snippet für jeden MCP-Client.

Das Problem

Computer-Use-Agenten skalieren wirtschaftlich nicht. Jeder Lauf leitet dasselbe Wissen aus Pixeln erneut ab: ein Modell-Roundtrip pro Schritt, ein Seiten-Snapshot pro Schritt, der den Kontextfenster füllt, und Zuverlässigkeit, die sich über eine Kette abwärts potenziert. Deshalb werden sie ständig demonstriert und selten eingesetzt.

Die Software, die Agenten am meisten steuern müssen, ist genau die Software, die am wenigsten wahrscheinlich jemals eine API bekommt — interne Tools, Altsysteme, alles, dessen Anbieter nicht mehr existiert. Du kannst keinen Netzwerk-Tab abhören, auf dem nichts ist, und du kannst eine Line-of-Business-App von 2011 nicht bitten, ein neues Protokoll zu übernehmen.

apic nutzt den teuren Agenten einmal, um die Schnittstelle zu schreiben. Danach ist es ein Funktionsaufruf.

So funktioniert es

Phase

Aktion

Technik

Verankern

Liest die eigene Dokumentation des Ziels und lernt die Substantive dieser App, sodass das Vokabular nicht auf das von Vikunja hartcodiert ist

Tavily + OpenAI, pro Host gecacht — nur CLI-Pfad

Erkunden

Steuert die App, priorisiert Affordanzen, sodass Erstellen-Aktionen zuerst kommen, öffnet Formulare und sendet sie ab

Playwright + h (Eskalationsstufe für Steuerelemente, die das Vokabular nicht benennen kann)

Wahrnehmen

Entscheidet, ob sich etwas Bedeutsames geändert hat

DOM-Diff, auf dem CLI-Pfad zu fal eskalierend

Synthetisieren

Wandelt eine Trajektorie in ein typisiertes Tool-Schema um

deterministisch — kein Modellaufruf

Verifizieren

Spielt das Tool kalt mit Argumenten ab, die die App nie gesehen hat

schlüssellose Diff-Untergrenze, dann der feinabgestimmte Pioneer-Richter, mit OpenAI im Bereitschaftsmodus

Erzeugen

Schreibt einen lauffähigen MCP-Server, seine Schemas und seinen Beleg

Beobachten

Führt die Suite in einem Intervall erneut aus

Heilen

Ein rotes Tool tritt an seinem eigenen Seed erneut in die Discovery ein

Der Reparaturpfad ist der Erstellungspfad. Heilen patcht keinen Selektor — es führt die Discovery erneut aus, die das Tool ursprünglich gefunden hat, und gleicht über den Namen ab, den die Synthese erzeugt. Ein umbenannter Button ergibt weiterhin createProject.

Ein Tool existiert nur, wenn die App den Schreibvorgang bestätigt hat

Das Zählen von DOM-Knoten erzeugt für jeden Button auf der Seite ein plausibel aussehendes Tool. apic gibt eines nur aus, wenn die App selbst behauptet, dass sich der Zustand geändert hat, über drei Signale, die drei verschiedene App-Verhaltensweisen abdecken:

Verhalten

Beispiel

Signal

Ankündigen-und-Bleiben

ein Label erstellen

Erfolgsbanner in einer Statusregion

Ankündigen-und-Navigieren

ein Projekt erstellen

Banner übersteht die URL-Änderung

Stilles Anhängen

Kanban-Schnellhinzufügen

der übermittelte Wert erscheint als gerenderter Inhalt

Verschiebung

eine Karte zwischen Spalten ziehen

die Karte hat den Container gewechselt

Die Verschiebung ist wichtig, denn ein Drag hat kein Banner und gibt nichts zurück — die Karte existierte bereits. Die Containment-Änderung ist der Beleg, und kein kosmetisches Re-Rendering kann sie erzeugen.

Rezepte binden an Identität, nicht an Ort

Vikunja regeneriert Element-IDs bei jedem Seitenaufruf, daher ist ein gespeicherter Selektor bei der Ankunft tot. Ein Rezept hält fest, was ein Feld ist — sein Label, Platzhalter, Name — und die Wiedergabe löst es live erneut auf, mit Rückgriff auf eine stabil-zuerst-Selektorkette (name → stabile id → Platzhalter → generierte id zuletzt).

Ergebnisse

Aus der UI kompiliert. Die OpenAPI-Spezifikation des Ziels wird während der Kompilierung nie gelesen — sie wird nur als Ground Truth für die Bewertung verwendet, weshalb die Recall-Zahl überhaupt etwas bedeutet.

Der Nenner, vor der Zahl genannt: 18 ist jede Schreiboperation (POST/PUT/DELETE) auf /projects, /tasks und /labels in Vikunjas eigener OpenAPI-Spezifikation, nachdem entfernt wurde, was keine Board-Geste ist — Teams, projektweite Berechtigungen, Link-Sharing, Anhänge, Aufgabenbeziehungen, Duplizierung, Bulk-Endpunkte und Lesebestätigungen. Vikunja veröffentlicht insgesamt 105 Schreiboperationen; 18 ist die Teilmenge, die eine Person auf einem Kanban-Board ausführen kann, und jedes ausgegebene Tool darf höchstens eine davon beanspruchen, sodass Recall nicht durch lockeres Matching aufgebläht werden kann.

RECALL     8/18    of the board write-ops in the target's own API
PRECISION  9/9     emitted tools that map to a real operation
VERIFIED   9/9     survived a cold replay with arguments never seen before

Neun Tools entdeckt, neun ausgeliefert. Abgelehnte Tools werden nicht gelöscht — sie bleiben in tools.json mit verified: false, denn ein abgelehntes Tool ist ein Beleg über den Compiler, kein Müll.

markTask ist flaky und das ist mehr wert als die 9/9. Zwei aufeinanderfolgende Verify-Läufe gegen dasselbe Bundle, ohne Änderungen dazwischen, ergaben 8/9 und dann 9/9: Es scheiterte mit „beobachtete Mutation, aber nichts bestätigte einen Schreibvorgang“ und bestand dann mit „Erfolg — die Aufgabe wurde erfolgreich gespeichert.“ Die wahrscheinliche Ursache ist der Zustand der geseedeten Aufgabe — landet man auf einer bereits erledigten, liest sich das Steuerelement als MARK AS UNDONE und bestätigt anders. Es nimmt keine Parameter entgegen, kann also auch per Argument nicht disambiguieren.

Das ist ein lebendes Beispiel für das Flake-vs-Drift-Problem, das unten als ungelöst aufgeführt ist: watch würde diesen Fehler als Drift zählen und heal aufrufen, obwohl überhaupt nichts gedriftet ist.

Kontinuierliche Verifizierung über einen Live-Nachmittag:

327 checks · 118 breaks · 3 automatic repairs · MTTR 20s

(out/watch-stats.json, 38 Zyklen ab 11:33 BST, während dies geschrieben wurde noch aktiv — die Zähler bewegen sich.)

Verstehe diese Break-Zählung richtig. stats.breaks++ feuert bei jeder roten Wiedergabe in jedem Zyklus, daher ergeben drei Tools, die über 38 Zyklen rot bleiben, ~114 Breaks — es ist eine Rot-Tool-Zyklus-Zählung, nicht 118 separate Drift-Ereignisse. Und dieser Watcher wurde um 11:33 gestartet, vor dem Fix an heal(), das ein repariertes Rezept ohne die frische provenance zurückgab, auf die der Opener von replay() tatsächlich klickt; ein Tool, dessen Steuerelement umbenannt worden war, wurde daher in jedem Zyklus geheilt und in keinem grün. Das ist der Großteil der 6/9. Im Code behoben, nicht über ein vergleichbares Fenster neu erfasst.

Partner-Technologien

Jede hat eine Stufe, und jede verschlechtert sich, statt zu blockieren: Die gesamte Pipeline läuft mit überhaupt keinen API-Schlüsseln, bei reduzierter Genauigkeit. Diese Eigenschaft ist der Grund, warum der Compiler gebaut werden konnte, bevor irgendwelche Anmeldedaten eintrafen — und auch der Grund, warum eine Integration aufhören kann beizutragen, ohne dass die Kompilierung es bemerkt, was die Statusspalte festhält.

Tech

Stage

Warum es seinen Platz verdient

Status

OpenAI

Verify

Ein unabhängiges Urteil darüber, ob der vorhergesagte Effekt eingetreten ist, auf der Grundlage des keyless-Diff-Floors. Es kann eine Ablehnung bestätigen, niemals eine aufheben

in Verwendung — es entschied über das eine Tool, das verify abgelehnt hatte

fal

Perceive

Schnelles VLM für die Beurteilung bedeutsam-vs-kosmetisch, nur eskaliert, wenn der DOM-Diff mehrdeutig ist

in Verwendung — 4/4 eskalierte Schritte beim letzten Compile beurteilt, 2 davon als kosmetisch eingestuft. Nur CLI-Pfad; compile_app eskaliert nicht

Pioneer

Verify, Distil

Ein GLiNER2-Encoder, der auf apics eigenen Verify-Evidenzen feinabgestimmt ist, ersetzt den GPT-4.1-mini-Judge — und schlägt ihn auf zurückgehaltenen Tools (unten). Außerdem der Diff-Text-Klassifikator in distill.js

in VerwendungPIONEER_JUDGE_MODEL gesetzt: Der Live-Verify-Durchlauf oben wurde vom feinabgestimmten Encoder beurteilt, 8/9, jedes Urteil in 106–183 ms

h

Explore

Liest die Seite und benennt die Schreibaktionen, die das keyless-Vokabular verweigerte

in Verwendung — läuft einmal pro Seed auf den Restposten; benennt 0 von 3 auf Vikunja, korrekterweise

Tavily

Ground

App-Dokumentation → Domänenvokabular, sodass Tools createIssue heißen, nicht btn_submit_2

in Verwendungground.js läuft vor dem ersten Seed; additiv zur eingebauten Tabelle, pro Host gecacht, nur CLI-Pfad

Die zweistufige Aufteilung ist die eigene These des Produkts, auf sich selbst angewandt: fal ist die günstige hochfrequente Wahrnehmungsschicht, OpenAI ist die teure niederfrequente Reasoning-Schicht. Bei Fehlern eskalieren, nicht bei jedem Aufruf.

Wie jedes einzelne tatsächlich aufgerufen wird

h — holo3-1-35b-a3b, api.hcompany.ai/v1 (OpenAI-kompatibel). gesture() bildet den sichtbaren Text eines Steuerelements mit Regexes auf ein <Verb, Ressource>-Paar ab und gibt für alles andere null zurück. Dieses null ist das Präzisions-Gate und zugleich der Ort, an dem Recall verloren geht: Ein rein ikonisches Schaltflächenelement, ein Steuerelement, das nicht mit einem Verb beginnt, oder eine App, deren Formulierungen das Vokabular nie antizipiert hat, wird verworfen, so deutlich es auch beschriftet ist. h ist die Eskalationsstufe genau für diese Menge — discover.js classify() sendet ein JPEG der Seite und der verweigerten Steuerelemente, einmal pro Seed, und fragt, welche davon schreiben.

Drei Dinge verhindern, dass das Präzision kostet. Antworten werden gegen das geschlossene Vokabular — sechs Verben, vier Ressourcen — von plan.gestureFrom() validiert, sodass ein erfundenes Verb kein Tool benennen kann. Steuerelemente außerhalb des Slices werden zurückgehalten statt angeboten, denn ADD TO FAVORITES auszuschließen ist eine Scope-Entscheidung und keine Lücke, die ein Modell füllen soll. Und ein klassifiziertes Steuerelement muss die App trotzdem einen Schreibvorgang bestätigen lassen wie jeder andere Kandidat.

Gemessen, beim Compile, über den dieses README berichtet: h liest die drei Steuerelemente, die das Vokabular auf Vikunjas Aufgaben-Seite ungelöst lässt, und benennt eines davon — ein rein ikonisches Steuerelement, das die Regexes glatt verwerfen:

! h read 3 unresolved controls, named 1
! h: "Kanban bucket: To-Do" -> move task (Pencil icon allows changing task status)

Das ist die Eskalationsstufe, die genau den Job macht, für den sie existiert: ein Steuerelement ohne führendes Verb und ohne brauchbaren Text, aus seinem Icon zurückgewonnen und in das geschlossene Vokabular abgebildet.

Es hat kein Tool hinzugefügt, und wir behaupten das auch nicht. move task war zu diesem Zeitpunkt bereits zweimal gefunden worden — einmal durch den Board-Drag (Move card between columns), einmal durch das Bucket-Dropdown der Aufgabenseite (Kanban bucket: Doing) —, sodass h's Antwort in das moveTask deduplizierte, das der Drag erzeugt hatte. Auf diesem Ziel ist h Korrobation, nicht Recall: ein dritter unabhängiger Weg zu einer Aktion, die zwei andere Wege bereits erreicht hatten. Eine frühere Revision dieser Datei sagte, h sei nie erreicht worden und habe nichts benannt; beides war falsch.

Ob h Recall hinzufügt, ist hier ungetestet, weil Vikunjas Schreibaktionen ungewöhnlich gut beschriftet sind. Der Fall, für den es gebaut ist — eine App, deren Schaltflächen Icons sind — ist genau der Fall, den dieses Ziel nicht bietet. Ohne den Schlüssel verliert der Compile diese Korrobation und sonst nichts.

fal — google/gemini-2.5-flash-lite über fal-ai/any-llm/vision. Der DOM-Differ sagt, ob sich die Seite geändert hat. Er kann eine Änderung nicht beurteilen, die der Text nicht beschreibt — eine Karte, die die Spalte gewechselt hat, ein Steuerelement, das nur aufgeleuchtet ist. perceive.js adjudicate() eskaliert diese Schritte, und nur diese, an Pixel.

Gemessen, vom letzten vollständigen Compile: vision: 4/4 eskalierte Schritte von fal beurteilt, 1 Drag korroboriert, 2 als kosmetisch befunden. Die zwei kosmetischen Urteile sind die interessante Hälfte — fal entfernt Kandidaten, die sonst als Schreibvorgänge untersucht worden wären. Es läuft von cli.js; ein Compile, der über compile_app auf dem MCP-Server angestoßen wird, eskaliert nicht.

OpenAI — gpt-4.1-mini, strukturierte Ausgabe. verify.js spielt jedes emittierte Tool kalt mit Argumenten ab, die die App nie gesehen hat, und beurteilt das Ergebnis zweimal: zuerst einen deterministischen Diff-Floor, dann das Modell. Das Modell kann eine Ablehnung bestätigen und niemals eine aufheben — ein Tool, das der Diff nicht bestätigen konnte, bleibt abgelehnt, so überzeugt der Judge auch ist.

Gemessen: Beim Lauf, bei dem markTask fehlschlug, lautet sein Eintrag openai/gpt-4.1-mini disagreed but cannot overturn a rejection. Diese Asymmetrie ist beabsichtigt: Ein Judge, der seine eigenen Vermutungen befördern kann, ist ein Präzisionsleck.

Pioneer — GLiNER2 (fastino/gliner2-base-v1), ein POST /inference pro Schritt. distill.js sendet den Diff-Text jedes Schritts in einer eigenen Anfrage und erhält eine Zustandsänderungsklasse, ein Destruktivitäts-Flag und die Domänennomen, oberhalb einer 0,6-Konfidenzschwelle. Früher wurde die gesamte Trajektorie gebatcht, und Batching ist genau das Thema der dritten hart erarbeiteten Lektion unten: Derselbe Text erzielte creation 0,777 allein, creation 1,000 an Position 0 und DELETION 0,600 an Position 2 des umgekehrten Batches — ein falsches Label, das die Schwelle überschreitet. Eine abgeschlossene Trainings-Job-ID in PIONEER_MODEL tauscht den Basis-Encoder gegen einen Checkpoint aus, der auf apics eigenen Labels feinabgestimmt ist — das System, das seine eigene Wahrnehmungsschicht kompiliert —, und sonst ändert sich nichts.

Pioneer — der feinabgestimmte Verify-Judge. Das ist der Pioneer-Seitenwettbewerbsbeitrag: ein Modell feinabzustimmen, das einen allgemeinen LLM-API-Aufruf übertrifft oder ersetzt. Der Aufruf, den es ersetzt, ist judgeModel() in verify.js — GPT-4.1-mini, ein 200-Wörter-Systemprompt, strukturierte Ausgabe, eine Frage pro abgespieltem Tool: ist dieser vorhergesagte Schreibvorgang angesichts dieses DOM-Diffs nachweislich eingetreten? Das ist eine Zwei-Label-Textklassifikation im Gewand eines Chat-Completions.

pioneer-train.js baut den Ersatz aus den eigenen Abgasen des Produkts, ohne manuelle Beschriftung:

  1. collect — jedes kompilierte Tool sechsmal mit frischen Argumenten über verifyAll() abspielen und die Evidenz sowie das Urteil des ausgelieferten Judges (Diff-Floor + GPT) aufzeichnen. 54 echte Zeilen.

  2. dataset — Negative ableiten, indem die Evidenz gelöscht wird, auf die der Floor sich stützt (Banner weg, Echo in das Eingabefeld verschoben, das es getippt hat, Argument unausgefüllt, Replay warf Fehler, nichts geändert), und Positive, die das Label erhalten (Knotenreihenfolge umgekehrt, unzusammenhängende Knoten hinzugefügt, Argumente in Werte umbenannt, die ein Mensch tippen würde). Jede abgeleitete Zeile wird vom selben deterministischen Floor neu beschriftet. 788 Zeilen; nach Tool zurückgehalten, sodass die Bench Tools misst, die der Encoder nie gesehen hat.

  3. upload / trainPOST /felix/datasets/upload/url → presigned PUT → POST /felix/training-jobs, fastino/gliner2-base-v1, LoRA, 12 Epochen. Trainiert in etwa vier Minuten.

  4. bench — die zurückgehaltenen Zeilen durch beide Judges. Das LLM wird über die unveränderte judgeModel() aufgerufen, sieht also genau das, was es in Produktion sieht.

judge

Genauigkeit

Präzision

Recall

falsch pos

falsch neg

ms/Zeile

Pioneer GLiNER2-Feintuning (Job 91370379…)

94,4 %

100 %

87,6 %

0

12

150

OpenAI GPT-4.1-mini

89,3 %

84,3 %

93,8 %

17

6

890

215 zurückgehaltene Zeilen, zwei Tools (createTask, assignLabel), die im Training fehlen. Der Encoder gibt etwas Recall für null falsch Positive auf — der richtige Trade-off für diesen Judge, der konstruktionsbedingt eine Ablehnung bestätigen, aber nie eine Vermutung befördern darf. Setzt man PIONEER_JUDGE_MODEL auf die Job-ID, verwendet verify es; OpenAI bleibt als Fallback in Bereitschaft, und ganz ohne Schlüssel läuft der Floor trotzdem.

Drei Dinge auf die harte Tour gelernt, alle live verifiziert und im Code festgehalten: multi_label/top_k innerhalb einer Klassifikations-Spezifikation lassen den vereinheitlichten /inference-Pfad für jeden Text categories: [] zurückgeben (das, nicht Guthaben, ist der Grund, warum die Distil-Stufe den ganzen Morgen still war); GLiNER2 trainiert nur als LoRA — training_type: "full" wird akzeptiert und schlägt in Modal ohne Log-Zeile fehl; und Batch-Inferenz (text: [...]) auf einem feinabgestimmten Modell liefert Labels, die nicht zu den Eingaben passen, also sendet der Judge einen Text pro Anfrage.

Tavily — api.tavily.com/search, fünf Ergebnisse, Antwort inklusive. ground.js läuft vor dem ersten Seed. plan.js bringt Vikunjas Nomen mit — bucket, task, label, project — und bei allem anderen, nach dem gesture() gefragt wird, gibt eine Tabelle, die nie davon gehört hat, null zurück, und das Steuerelement wird verworfen. Tavily holt die Dokumentation des Ziels selbst; OpenAI strukturiert diese Prosa unter einem strengen Schema in eine geschlossene Nomenmenge; jeder Begriff wird gegen /^[a-z][a-z-]{1,18}$/ validiert, auf 12 begrenzt und in die eingebaute Tabelle eingemischt, statt sie zu ersetzen, sodass Grounding Vokabular hinzufügen und Vikunjas nie wegnehmen kann. Pro Host unter .apic/ gecacht, sodass ein wiederholter Compile nichts kostet und eine Demo nicht vom Veranstaltungs-WLAN abhängt.

Es degradiert in drei Schritten — kein Tavily-Schlüssel, keine Evidenz; kein OpenAI-Schlüssel, die Evidenz kann nicht strukturiert werden; nichts übersteht die Validierung — und jeder Schritt loggt und lässt die eingebaute Tabelle stehen. Wie fal läuft es von cli.js: compile_app auf dem MCP-Server verwendet das eingebaute Vokabular.

Die Recall-Zahlen oben wurden also keyless erzeugt, mit fal auf den eskalierten Wahrnehmungsschritten und einem OpenAI-Judge im Verify-Durchlauf. Sie sind keine Demonstration des vollständigen Partner-Stacks, und dieses README wird das nicht behaupten.

Setup

git clone https://github.com/brwbo/apic && cd apic
npm install && npx playwright install chromium
cp .env.example .env      # fill in keys; .env is gitignored
npm run setup             # starts the target app, checks every credential

Ziel-App (selbst gehostet, wegwerfbar — niemals auf ein Produkt Dritter richten):

docker volume create vikunja-files
docker run --rm -v vikunja-files:/data alpine sh -c "chown -R 1000:0 /data"
docker run -d --name vikunja -p 3456:3456 -v vikunja-files:/app/vikunja/files \
  -e VIKUNJA_SERVICE_PUBLICURL=http://localhost:3456 \
  -e VIKUNJA_DATABASE_PATH=/app/vikunja/files/vikunja.db \
  -e VIKUNJA_RATELIMIT_ENABLED=false \
  vikunja/vikunja:latest

Befehl

Funktion

npm run doctor

Welche Anmeldedaten funktionieren, welche Ziele erreichbar sind

npm run compile

Erkunden → synthetisieren → ausgeben

npm run verify

Jedes Tool kalt erneut ausführen; nur Überlebende werden bereitgestellt

npm run watch

Kontinuierliche Verifikation mit automatischer Reparatur

npm run score

Recall und Präzision gegen die echte API des Ziels

npm run serve

apic selbst als MCP-Server ausführen — siehe unten

Jeder Befehl liest dieselben zwei Variablen, sodass ein kompletter Lauf auf ein alternatives Bundle zeigen kann, ohne das Live-Bundle anzufassen:

APIC_OUT_DIR=out/rescue APIC_APP=vikunja npm run verify

Variable

Standard

Bedeutung

APIC_OUT_DIR

generated

Wo kompilierte Bundles liegen. APIC_GENERATED wird als Alias akzeptiert

APIC_APP

vikunja

Welches Bundle darin

TARGET_URL

http://localhost:3456

Die App, die kompiliert wird

TARGET_USER / TARGET_PASS

apic / —

Anmeldedaten für das Ziel

TARGET_LOGIN_PATH

ermittelt

Nur nötig, wenn das Anmeldeformular nicht auf einem /login-artigen Pfad liegt

APIC_SEEDS

/projects,/labels

Seiten, von denen aus die Erkundung beginnt

Seeds und Ziel sind umgebungsgesteuert — nichts im Compiler kennt Vikunjas Routen:

APIC_APP=gitea TARGET_URL=http://localhost:3001 APIC_SEEDS=/repo/create,/issues npm run compile

Generierte Ausgabe

generated/vikunja/ — Server, Schemas und die Belege für jedes Tool. Nichts in diesem Verzeichnis wurde von einem Menschen geschrieben.

Als MCP-Server verwenden

claude mcp add apic -- node /path/to/apic/src/server.js

src/server.js startet mit einem Tool, compile_app. Zeigen Sie es auf eine URL, und es führt die Pipeline prozessintern aus, gibt generated/<app>/ aus, registriert die kompilierten Tools auf sich selbst und sendet notifications/tools/list_changed — sodass sie auf derselben Verbindung aufrufbar sind, ohne Neustart. Aus einem Kaltstart:

[apic] ready - 0 compiled tools + compile_app
BEFORE compile, tools/list = [ 'compile_app' ]
compile_app returned in 22.1s
list_changed notification: YES
AFTER compile = [compile_app, createProject, createLabel, updateLabel, createTask]
createLabel -> {"ok":true,"effect":"creation","expected":"creation"}

Ein Compiler, der einen Neustart des gerade erweiterten Systems erfordert, ist ein Build-Schritt. Einer, der das nicht tut, ist ein Live-Compiler. Hinweise zur Client-Kompatibilität und vollständige Transkripte: docs/mcp-client.md.

Beide Sackgassen werden beantwortet, nicht gemeldet

Ein Client trifft apic nur in dem Moment, in dem etwas fehlt, und beide dieser Momente beendeten früher das Gespräch.

Ein Tool, das nicht existiert, gibt die Kompilierung zurück, die es erstellen würde:

unknown tool: createIssue

No compiled tool exposes that action (compiled so far: vikunja). If the app has no API for it, make one:

    compile_app { "url": "http://localhost:3456", "goal": "createIssue" }

Ein Tool, das die App unter den Füßen weggezogen hat, wird auf dem Aufrufpfad repariert — watch heilt per Timer, der Server heilt auf Anfrage, über dieselbe heal(). Das Tool wird rot, der Compiler erkundet diese eine Aktion erneut, die Reparatur wird zurück in tools.json geschrieben, und der Aufruf wird erneut versucht, bevor der Aufrufer einen Fehler sieht:

[apic] createLabel is red (no control matched "ADD LABEL (RENAMED)") - re-exploring to heal it
[apic] createLabel healed in 13.6s (click "…" -> "create label"; selectors re-resolved); retry passed
{ "ok": true, "effect": "creation", "healed": { "ms": 13589, "persisted": true } }

Ein gesundes Tool wird von all dem nicht berührt: gleicher Aufruf, 4.4s, keine erneute Erkundung.

Verwandte Arbeiten und der Unterschied

Projekt

Was es tut

Der Unterschied

Playwright MCP

Browser-Automatisierung als typisierte MCP-Tools

Generische Verben (click(ref)) vs. app-spezifische Substantive (createTask(title)). Laufzeit vs. Kompilierzeit

Apify MCP Server

Generiert automatisch typisierte Tools aus Actor-Eingabeschemas

Actors sind von Menschen erstellt — es generiert den Wrapper aus einem von Menschen geschriebenen Vertrag

Apify AI Web Scraper

URL + einfaches Englisch → strukturierte Daten

Gibt Daten zurück, keine Schnittstelle, und führt das LLM bei jedem Aufruf erneut aus

cli-printing-press

URL/HAR/OpenAPI → CLI + MCP-Server, mit Verifikations-Gates

Belauscht den Netzwerkverkehr — die App muss bereits eine API haben. apic steuert die UI

Easy MCP

OpenAPI-Spezifikation → MCP-Tools

Erfordert, dass die API bereits existiert

Alita

Agent generiert und verwendet MCPs pro Aufgabe wieder

Generiert Tools durch Websuche. apic leitet sie durch Bedienung der Software ab

WebMCP

Seiten deklarieren ihre eigenen Tools in JavaScript

Erfordert, dass die Entwickler der App es übernehmen

Voyager

Fähigkeit schreiben, verifizieren, speichern, wiederverwenden

Der Vorfahre der Verifizieren-dann-Beh alten-Schleife

Die Schleife ist nicht neu — woher die Fähigkeit kommt, schon. Alita liest das Internet, um Tools zu erstellen; cli-printing-press liest das Netzwerk; Easy MCP liest eine Spezifikation. apic liest die App.

Was noch nicht funktioniert

Klartext, denn ein Compiler, der seine Fehlermodi verbirgt, ist keiner.

  • h ist im Pfad und trägt auf diesem Ziel nichts bei. Es liest die Steuerelemente, die das Vokabular abgelehnt hat, und benennt keines davon korrekt, weil Vikunjas Board-Bereich bereits von den Regexen abgedeckt ist. Die Eskalation ist real und gemessen; der Gewinn ist hier null, und ein Ziel, dessen Steuerelemente eher Icons als Verbphrasen sind, wäre der Fall, der es zeigen würde.

  • compile_app führt eine reduzierte Pipeline aus. compile.js ist die prozessinterne Kompilierung, die der MCP-Server aufruft, und es ist cli.js minus fünf Dinge: Grounding (Tavily/OpenAI), Seed-Erkennung, die dedizierte Formularseiten-Sonde, den Task-Detail-Seed und den Kanban-Drag, plus fals Vision-Ebene — adjudicate() läuft nur aus cli.js. Es behält Erkennung, Persistenz, Pioneer-Destillation, Synthese und Ausgabe. Deshalb zeigt das obige Transkript vier Tools, wo npm run compile neun erzeugt: die Live-Compiler-Demo und das 9-Tool-Bundle sind zwei verschiedene Pfade, und nur der CLI-Pfad ist das, was die Recall-Zahlen beschreiben.

  • Pioneer schien den ganzen Vormittag nicht verfügbar — zuerst 403 payment_method_required, und nach einem neuen Schlüssel categories: [] bei jedem Aufruf, was der Code als „keine Meinung" las und auf die Heuristik zurückfiel. Das Zweite war ein Request-Form-Bug (multi_label/top_k), nicht die API. Jede Integration wurde so geschrieben, dass sie still degradiert, und jede tat es — die Degradation ist das beabsichtigte Verhalten; es einen ganzen Vormittag lang nicht zu bemerken, ist es nicht.

  • Der feinabgestimmte Richter hat eine App gesehen. Seine 788 Trainingszeilen sind alle Vikunja. Der zurückgehaltene Split ist nach Tool, nicht nach App; eine Gitea- oder ParaBank-Bench ist der nächste ehrliche Test, und collect gegen ein zweites Ziel ist der Weg dorthin.

  • Das zweite Ziel kompiliert dünn. Gitea kompiliert jetzt Ende zu Ende — createRepository und createIssue, beide verifiziert, 2/13 auf seinem Issue-Bereich gegen swagger.v1.json. Es brauchte keine Änderung an der Erkennung: Die beiden Fixes waren eine Bestätigungsklasse (Gitea bestätigt einen Schreibvorgang, indem es das Ergebnis unter einer neuen URL ausliefert, die den übermittelten Wert trägt, und das Gate suchte nur nach Bannern und Body-Echos) und das Verschieben der Container/Item-URL-Muster aus cli.js in die Konfiguration, wo APIC_SEEDS bereits lebte. Label- und Kommentar-Aktionen werden weiterhin verpasst: Sie sitzen hinter Steuerelementen, die das Vokabular nicht benennt, und h benannte keines der 14, die ihm übergeben wurden.

  • 8/18 Recall auf Vikunja. Fehlend: Bucket-Erstellung, Kommentare, Beziehungen und Anhänge.

  • markTask schlägt etwa bei jedem zweiten Lauf fehl (siehe Ergebnisse). Der Effekt ist real und beobachtet; ob etwas ihn bestätigt, hängt vom bestehenden Zustand der Aufgabe ab. Jedes Live-npm run verify sollte erwartungsgemäß 8/9 oder 9/9 ausgeben.

  • Parallele Läufe kollidieren. Jeder Befehl teilt eine gespeicherte Sitzung unter .apic/session.json, sodass ein gleichzeitig gestarteter Compile und Verify den Browser-Kontext des jeweils anderen mitten im Lauf zerstören können (Error setting storage state: Execution context was destroyed). Übergeben Sie pro Lauf eine eigene APIC_SESSION als Workaround; die echte Lösung ist standardmäßig eine Sitzungsdatei pro Lauf.

  • Watch behandelt jeden Fehler als Drift. Eine echte Flake-vs.-Drift-Klassifikation existiert nicht. Drei False-Positive-Klassen wurden von Hand behoben — Rate-Limiting, Token-Ablauf und eine abgestürzte Seite — aber das allgemeine Problem besteht.

  • Semantische Änderung wird nicht erkannt und ist gefährlich. Wenn deleteProject statt zu löschen archiviert, ist das Heilen des Selektors die falsche Antwort. Verifikation prüft, dass ein Effekt aufgetreten ist, nicht, dass es derselbe Effekt ist.

  • Keine inversen Aktionen, daher verschmutzt die Suite ihre eigene Fixture. Wiederholte Läufe degradieren das Ziel, bis es zurückgesetzt wird.

  • Auth wird umgangen. Ein Login, ein Benutzer, keine Berechtigungsbereiche — was der schwierige Teil des Problems in realer Unternehmenssoftware ist.

Erklärung zu Vorarbeiten

Von Grund auf beim Hackathon geschrieben. Kein Boilerplate übernommen; das Repo wurde am Morgen der Veranstaltung leer erstellt. Playwright, das MCP-SDK und die OpenAI- und fal-Clients sind die einzigen Abhängigkeiten.

Lizenz

MIT

A
license - permissive license
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • MCP server for secureFlows: token-free URL builders and integration-linting tools for AI agents.

  • MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.

  • Read-only MCP server for the WebAssembly spec: instructions, types, sections, search, proposals.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/brwbo/apic'

If you have feedback or need assistance with the MCP directory API, please join our Discord server