Entwickler
Wie ich mit dem Sinch-Plugin einen Kommunikations-Stack in Cursor gebaut habe
Eine Kommunikations-Integration ist selten wegen des eigentlichen Versands langsam. Die meiste Zeit entfällt auf Nutzdaten-Strukturen, ein anderes Authentifizierungsschema für jedes Produkt und die Kluft zwischen etwas, das in der Staging-Umgebung funktioniert, und etwas, das auf dem Smartphone in Ihrer Hand funktioniert.
Ich habe genug davon mit den Sinch-APIs gebaut, um zu wissen, wo diese Zeit bleibt, und genau das macht das Sinch-Plugin für Cursor lohnt es sich, es richtig zu testen: Ich kann beurteilen, ob der Code, den ein Agent zurückgibt, richtig ist. Also nahm ich mir einen Nachmittag Zeit und eine Demo-Bank als Grundlage.
RockBank ist diese Demo-Bank: ein fiktiver Kreditgeber, dessen Kunden per Post versendete Zahlungserinnerungen ignorieren. Die Aufgabe bestand darin, die Erinnerungen in die Kanäle zu verlagern, die die Leute tatsächlich lesen, und das gesamte Messaging hinter einem Sendepfad und Webhook-Endpunkt zu belassen, sodass das Hinzufügen eines Kanals eine Konfigurationsänderung anstelle einer Neuschreibung ist.
Funktionsweise des Flows
- Eine SMS-Zahlungserinnerung, die über die Sinch Conversation API versendet wird
- Dieselbe Erinnerung mit einem Upgrade auf RCS, einem verifizierten Absender und einer Schaltfläche „Jetzt bezahlen“ im Thread
- Eine automatisierte Voice-Erinnerung, falls die Zahlung noch aussteht
- Identitätsverifizierung mit einem Einmalpasswort, bevor die Zahlung erfolgt
- Eine E-Mail-Bestätigung über Mailgun, sobald die Zahlung abgeschlossen ist
Das ist Messaging, Voice, Verifizierung und E-Mail hinter einem Sendepfad und eingehenden Webhook.
Voraussetzungen für den Build
Der Code von RockBank ist nicht veröffentlicht, daher ist dies kein Clone-and-Run. Im Folgenden wird die Kontoeinrichtung beschrieben, die die Snippets voraussetzen, und dieselbe Einrichtung, die Sie benötigen würden, um Ihre eigene Version zu bauen:
- Cursor mit installiertem Sinch-Plugin und eine Test-Telefonnummer, die Ihnen gehört
- Eine Conversation API-App: Projekt-ID, Schlüssel-ID, Key Secret und App-ID
- Separate Zugangsdaten pro Produkt: ein Voice-Anwendungsschlüssel und -Secret mit einer zugewiesenen Nummer sowie ein App-Schlüssel und -Secret für die Verifizierung, was wiederum eine eigene App ist
- Ein für Ihren Markt zugelassenes RCS-Profil, bei dem SMS in derselben App als Fallback konfiguriert ist. Die Zulassung durch den Mobilfunkanbieter nimmt Zeit in Anspruch, beginnen Sie also frühzeitig damit. Ein SMS-Absender ist kein Ersatz: Ohne zugelassenes Profil landet jede Erinnerung als Klartext.
- 10DLC-Registrierung im Gange, falls Sie an US-Nummern versenden. Mehr dazu unter Fallstricke.
Plugin einrichten
Installieren Sie es über den Cursor Marketplace oder über die Befehlspalette:
/add-plugin sinch-cursor-plugin
Das Plugin besteht aus drei Teilen, die unterschiedliche Aufgaben erfüllen:
- Skills laden Sinch-API-Wissen in den Kontext des Agenten, bevor dieser Code schreibt: Endpunkte, Authentifizierungsschemata, Formen der Nutzdaten, Kanaleigenschaften. Dies ist der Teil, der darüber entscheidet, ob der generierte Code für die echte API oder für eine plausibel aussehende Erfindung kompiliert wird.
- Befehle (wie
/send-messageund/list-webhooks) rufen Sinch-APIs direkt aus dem Chat auf, ohne dass Code dazwischenliegt. - Die MCP-Server, und zwar zwei davon.
sinchführtnpx -y @sinch/mcplokal aus und verbindet den Agenten mit Ihrem Live-Konto, damit er Nachrichten senden und die Konfiguration überprüfen kann, während Sie arbeiten.sinch-docsist remote unterdevelopers.sinch.com/mcpverfügbar, um die Dokumentation zu durchsuchen. Nur der erste benötigt Zugangsdaten.
Zugangsdaten sind der einzige Teil der Einrichtung, bei dem man präzise vorgehen sollte. Fünf Variablen kommen in einen env-Block in ~/.cursor/settings.json, und Cursor benötigt einen Neustart, um sie zu übernehmen:
{
"env": {
"CONVERSATION_PROJECT_ID": "your-project-id",
"CONVERSATION_KEY_ID": "your-key-id",
"CONVERSATION_KEY_SECRET": "your-key-secret",
"CONVERSATION_REGION": "us",
"CONVERSATION_APP_ID": "your-app-id"
}
}
Falls der Server weiterhin fehlende Zugangsdaten meldet, exportieren Sie dieselben fünf Variablen in Ihrem Shell-Profil und führen Sie ein vollständiges Beenden und einen Neustart durch: Es wird als npx -y @sinch/mcp ausgeführt und erbt die Umgebung der Shell, von der aus Cursor gestartet wurde.
Diese fünf gehören zum MCP-Server und decken die Conversation API ab, sodass der Voice-Schlüssel und das Secret niemals in Cursor gelangen. Telefonanrufe finden nur statt, wenn Ihr eigener Code ausgeführt wird.
Bestätigen Sie die Verbindung, bevor Sie etwas schreiben:
/send-message –to=+15551234567 –message=“RockBank test“
Derselbe Befehl nimmt eine Fallback-Kette entgegen, was eine nützliche Vorschau darauf bietet, was der Sendepfad später tun wird:
/send-message –to=+15551234567 –message=“RockBank test“ –fallback=RCS,SMS
Den Sendepfad auf der Conversation API aufbauen
Die Struktur des Sendepfads ist die Entscheidung, von der alles andere abhängt. Die Conversation API nimmt einen generischen Nachrichtenrumpf und transkodiert ihn pro Kanal, und sie behandelt die Kanalauswahl und das Fallback als Daten in der Anfrage anstatt als Verzweigungen in Ihrem Code. Für RockBank ist das das gesamte Design: Die Erinnerung wird über RCS mit SMS dahinter gesendet, Voice kommt später hinzu, und der Aufrufort erfährt nie etwas davon.
Es lohnt sich, dies in den Prompt aufzunehmen, anstatt es der Schlussfolgerung zu überlassen. Bitten Sie einen Agenten, „der Kundschaft zu schreiben, falls eine Zahlung fällig ist“, und Sie erhalten vernünftigerweise einen Sendepfad, der um einen Kanal herum aufgebaut ist, denn genau das haben Sie verlangt. Geben Sie stattdessen die Intention an und überlassen Sie den Mechanismus dem Skill:
Implementieren Sie den Sendepfad für Erinnerungen über die Sinch Conversation API. Senden Sie RCS mit SMS als Fallback und halten Sie die Schnittstelle stabil, sodass das spätere Hinzufügen eines Kanals keine Aufruforte ändert. Verarbeiten Sie außerdem Zustellbestätigungen und protokollieren Sie diese.
Keine Feldnamen, kein Endpunkt, kein Authentifizierungsschema. Der conversation-api-Skill gibt bereits vor, wie das Fallback funktioniert: Fügen Sie ein channel_priority_order-Array hinzu und listen Sie jede Kanalidentität beim Empfänger auf. Diese Aufteilung ist diejenige, die Sie in Ihren eigenen Prompts anstreben sollten. Sie beschreiben das gewünschte Verhalten und die Einschränkungen, an die es sich halten muss, und der Skill liefert die Nutzdaten.
Dieser letzte Nebensatz leistet echte Arbeit. Ohne ihn erhalten Sie einen Sendepfad ohne Beobachtbarkeit und finden erst heraus, wie es läuft, falls jemand aus der Kundschaft mitteilt, dass die Erinnerung nie angekommen ist. Damit muss der Agent berücksichtigen, was passiert, nachdem messages:send eine 200 zurückgibt, denn dort befinden sich die interessanten Fehler.
Was zurückkam, ist eine private Methode, die jeden Nachrichtenrumpf der Conversation API aufnimmt, wobei das Fallback als channel_priority_order und beide Kanalidentitäten beim Empfänger ausgedrückt werden:
async def _send(
self,
to: str,
message: dict[str, Any],
channel_properties: dict[str, str] | None = None,
) -> dict[str, Any]:
"""Send any Conversation API message over RCS, falling back to SMS."""
properties = {"SMS_SENDER": self.sms_sender} if self.sms_sender else {}
properties.update(channel_properties or {})
payload: dict[str, Any] = {
"app_id": self.app_id,
"recipient": {
"identified_by": {
"channel_identities": [
{"channel": "RCS", "identity": to},
{"channel": "SMS", "identity": to},
]
}
},
"channel_priority_order": ["RCS", "SMS"],
"message": message,
}
if properties:
payload["channel_properties"] = properties
path = f"/v1/projects/{self.project_id}/messages:send"
response = await self.client.post(path, auth=self._auth, json=payload)
if response.is_error:
raise SinchClientError(
f"messages:send failed ({response.status_code}): {response.text}"
)
return response.json()
async def send_reminder(self, to: str, text: str) -> dict[str, Any]:
return await self._send(to, {"text_message": {"text": text}})
Jeder Kanal nach diesem ändert den message-Rumpf, den er an _send übergibt, und sonst nichts.
Die Kanaleigenschaft SMS_SENDER ist vorhanden, da SMS einen Absender benötigt, sofern die App keinen Standardabsender festgelegt hat. Das kam ebenfalls vom Skill.
Testen im Editor
Dafür ist der MCP-Server da. Nachdem der Client geschrieben war und die Datei noch offen war, bat ich um einen echten Versand:
Senden Sie eine Testerinnerung über diesen Dienst an meine Nummer, mit einem Fälligkeitsdatum in drei Tagen und einem Betrag von 240 $.
Eine Live-Nachricht über ein Live-Konto, direkt aus dem Editor:
{
"message_id": "01KY292FGM58WNNBETKE1Q4NF8",
"accepted_time": "2026-07-21T12:05:38.964Z"
}
Diese Antwort bedeutet „akzeptiert“, nicht „zugestellt“. Die Zustellung erfolgt asynchron und wird auf dem Webhook angezeigt. Der nächste Schritt besteht also darin sicherzustellen, dass der Webhook ihn auch tatsächlich abonniert hat.
Eingehenden Webhook anbinden
Ein Endpunkt verarbeitet beide Verkehrsrichtungen: MESSAGE_DELIVERY für Empfangsbestätigungen und MESSAGE_INBOUND für Antworten der Kundschaft. Registrieren Sie ihn mit einem Befehl:
/create-webhook –target=https://rockbank.example.com/sinch/callbacks \
–triggers=MESSAGE_DELIVERY,MESSAGE_INBOUND –secret=$SINCH_WEBHOOK_SECRET
Das Ziel muss HTTPS sein und aus dem Internet erreichbar sein. Richten Sie für die lokale Arbeit also einen Tunnel vor Ihrem Handler ein und registrieren Sie die Tunnel-URL. Das Secret ist in der API optional, aber übergeben Sie eines: Es ist das, was die Callbacks signiert, es muss mit dem Wert übereinstimmen, gegen den Ihr Handler verifiziert, und ohne dieses gibt es keine Signatur zur Überprüfung. Eine App unterstützt bis zu fünf Webhooks, und das erneute Registrieren desselben Ziels gibt eine 400 zurück.
Prüfen Sie dann, was die App tatsächlich abonniert hat, denn das ist nicht immer das, worum Sie gebeten haben:
/list-webhooks
/list-webhook-triggers
Es lohnt sich, dies zu überprüfen, bevor Sie nachgelagerten Systemen vertrauen. Ein Webhook, der nur mit MESSAGE_INBOUND registriert wurde, akzeptiert Ihre Registrierung problemlos, und Ihr Handler für die Zustellbestätigung bleibt ungenutzt und wird nie ausgeführt. Der Sendepfad sieht fehlerfrei aus und das Zustellungsprotokoll bleibt leer.
Der Handler selbst benötigt eine Signaturüberprüfung, bevor er sich den Rumpf ansieht:
Schreiben Sie den FastAPI-Handler für den /sinch/callbacks-Endpunkt. Verifizieren Sie die HMAC-SHA256-Signatur, die Sinch bei jedem Callback sendet, bevor Sie den Rumpf verarbeiten, lehnen Sie Anfragen mit einem veralteten oder wiederverwendeten Zeitstempel ab und routen Sie Zustellbestätigungen sowie eingehende Nachrichten an separate Handler.
Sinch signiert Callbacks mit HMAC-SHA256 über den rohen Rumpf, die Nonce und den Zeitstempel:
import base64
import hashlib
import hmac
import time
MAX_CALLBACK_AGE_SECONDS = 300
@router.post("/sinch/callbacks")
async def handle_callback(request: Request) -> Response:
raw_body = await request.body()
signature = request.headers.get("x-sinch-webhook-signature", "")
nonce = request.headers.get("x-sinch-webhook-signature-nonce", "")
timestamp = request.headers.get("x-sinch-webhook-signature-timestamp", "")
try:
age = abs(time.time() - int(timestamp))
except ValueError:
raise HTTPException(status_code=401, detail="Bad timestamp")
if age > MAX_CALLBACK_AGE_SECONDS:
raise HTTPException(status_code=401, detail="Stale callback")
signed_data = raw_body + b"." + nonce.encode() + b"." + timestamp.encode()
expected = base64.b64encode(
hmac.new(
settings.webhook_secret.encode(), signed_data, hashlib.sha256
).digest()
).decode()
if not hmac.compare_digest(expected, signature):
raise HTTPException(status_code=401, detail="Invalid signature")
payload = json.loads(raw_body)
if "message_delivery_report" in payload:
await record_delivery(payload["message_delivery_report"])
elif "message" in payload:
await record_inbound_message(payload["message"])
return Response(status_code=200)
Hierbei gilt es einige Dinge zu beachten. Die signierte Zeichenfolge besteht aus dem rohen Rumpf, der Nonce und dem Zeitstempel, die in dieser Reihenfolge durch Punkte verbunden sind, und der Digest ist Base64 statt Hex. Berechnen Sie es über die rohen Bytes, nicht über ein neu serialisiertes Dict, sonst wird es niemals übereinstimmen. Der Schlüssel ist das Secret, das Sie festgelegt haben, als Sie den Webhook erstellt haben, und x-sinch-webhook-signature-algorithm teilt Ihnen mit, welcher Algorithmus verwendet wurde (aktuell HmacSHA256).
Eine gültige Signatur allein besagt nicht, dass die Anfrage aktuell ist. Jeder, der einen signierten Callback abfängt, kann ihn erneut senden, und der HMAC ist weiterhin gültig. Genau dafür sind der Zeitstempel und die Nonce da: Sinch beschreibt die Nonce aus genau diesem Grund als einzigartig pro Callback. Das obige Zeitfenster von fünf Minuten ist meine eigene Wahl, kein dokumentierter Wert, und es ist der einfachere Teil. Falls Sie eine strikte einmalige Verarbeitung benötigen, cachen Sie die bereits akzeptierten Nonces für die Dauer dieses Zeitfensters und lehnen Sie Wiederholungen ab. Eine idempotente Handhabung ist ebenfalls sinnvoll, da Sinch erneute Versuche mit exponentiellem Backoff durchführt und Sie legitimerweise dieselbe Bestätigung zweimal sehen werden. Idempotenz ist jedoch kein Replay-Schutz: Sie verhindert, dass ein wiederholter Callback Ihren Status beschädigt, ohne Ihnen jemals mitzuteilen, dass er wiederholt wurde.
RCS hinzufügen
Da der Sendepfad bereits kanalunabhängig ist, ist RCS ein Nachrichtentyp und eine Kanaleigenschaft:
Führen Sie für die Erinnerung ein Upgrade auf eine RCS-Rich Card mit dem verifizierten Absender der RockBank, einer Zahlungsübersicht und einer Schaltfläche “Jetzt bezahlen” durch, die unsere gehostete Zahlungsseite öffnet. Behalten Sie das SMS-Fallback für Geräte bei, die kein RCS unterstützen.
Die Card ist eine card_message mit einer url_message-Auswahl darauf, die an dasselbe _send wie zuvor übergeben wird:
async def send_reminder_card(
self,
to: str,
due_date: date,
amount: Decimal,
account_last4: str,
payment_url: str,
) -> dict[str, Any]:
card: dict[str, Any] = {
"title": f"Payment due {due_date:%d %b}",
"description": f"Amount: ${amount:,.2f}. Account ending {account_last4}.",
"choices": [
{"url_message": {"title": "Pay now", "url": payment_url}}
],
}
return await self._send(
to,
{"card_message": card},
channel_properties={"RCS_WEBVIEW_MODE": "TALL"},
)
channel_properties ist eine einzige flache Map, sodass der RCS-Schlüssel und der SMS_SENDER, den das Fallback benötigt, im selben Dictionary landen. Diese Zusammenführung ist der Grund, warum _send die Eigenschaften als Argument annimmt, anstatt sie inline zu erstellen. RCS_WEBVIEW_MODE steuert, wie viel vom Bildschirm die Zahlungsseite einnimmt, wenn sie geöffnet wird: FULL, HALF oder TALL.
Zwei Details zu RCS, die man leicht falsch machen kann:
RCS verarbeitet die Zahlung nicht. Die Schaltfläche “Jetzt bezahlen” ist eine URL-Aktion. Sie öffnet Ihre gehostete Zahlungsseite in einer Webansicht über dem Thread, die Kundschaft bezahlt dort und die Steuerung kehrt zur Konversation zurück. Was RCS Ihnen bietet, ist der verifizierte Absender, die markenkonforme Card und eine Kundschaft, die niemals ihre Messaging-App verlässt. Die Zahlung läuft weiterhin über Ihren Zahlungsanbieter.
Fallback-Fehler kommen spät an. Jeder Kanal, den Sie in channel_priority_order angeben, muss in der App konfiguriert sein, andernfalls wird die Anfrage direkt abgelehnt, was der einfache Fall beim Debuggen ist. Der schwierigere Fall ist ein konfigurierter Kanal, dessen Zustellung fehlschlägt: messages:send gibt 200 zurück, und das Ergebnis taucht später als MESSAGE_DELIVERY-Callback auf, wobei SWITCHING_CHANNEL den Punkt markiert, an dem das Fallback eingesetzt hat. Ein weiterer Grund, die Webhook-Trigger richtig einzurichten, bevor man sich auf den Fallback-Pfad verlässt.
Voice, Verifizierung und E-Mail
Für die restlichen vier Teile war jeweils ein Prompt erforderlich.
Voice. Falls die Erinnerung unbeantwortet bleibt und die Zahlung weiterhin aussteht, ruft die RockBank an. Ein Text-to-Speech-Anruf erfordert keinen Call-Flow-Server, sondern nur einen POST an die Voice-API:
Fügen Sie eine Voice-Erinnerung für Zahlungen hinzu, die zwei Tage, nachdem die Nachricht versendet wurde, noch ausstehen. Verwenden Sie einen Text-to-Speech-Anruf über die Sinch Voice-API mit den Zugangsdaten für unsere Voice-Anwendung und setzen Sie die Anrufer-ID auf unsere registrierte Nummer.
async def place_reminder_call(self, to: str, message: str) -> dict[str, Any]:
"""Place a text-to-speech reminder call via the Voice API."""
tts: dict[str, Any] = {
"destination": {"type": "number", "endpoint": to},
"cli": self.caller_id,
"locale": "en-US",
"text": message,
}
response = await self.client.post(
"/calling/v1/callouts",
auth=(self.voice_app_key, self.voice_app_secret),
json={"method": "ttsCallout", "ttsCallout": tts},
)
if response.is_error:
raise SinchClientError(
f"Voice callout failed ({response.status_code}): {response.text}"
)
return response.json()
Beachten Sie den Unterschied bei der Authentifizierung: Voice verwendet einen Anwendungsschlüssel und ein Secret, nicht die Zugangsdaten auf Projektebene, welche die Conversation API nutzt. Anderes Produkt, anderes Authentifizierungsschema, und die Skills decken beides ab, sodass der Prompt keines von beiden erklären musste.
destination für einen Telefonanruf lautet {"type": "number", "endpoint": "+46..."} in E.164, und die Antwort liefert Ihnen eine callId. Die API-Referenz führt cli als optional auf, aber setzen Sie es dennoch auf eine verifizierte Nummer oder eine aus Ihrem Dashboard zugewiesene: Ohne eine verwendbare Anrufer-ID könnten Sie eine Call-ID für einen Anruf zurückerhalten, der das Telefon niemals erreicht.
Verifizierung. Ein Einmalpasswort, bevor die Zahlungsseite irgendetwas akzeptiert:
Bevor die Zahlungsseite irgendetwas akzeptiert, verifizieren Sie die Kundschaft mit einem Einmalpasswort per SMS über die Sinch Verifizierungs-API. Machen Sie die Methode zugänglich, damit wir sie pro Anfrage wechseln können.
async def start(self, phone: str, method: str = "sms") -> dict[str, Any]:
response = await self.client.post(
"/verification/v1/verifications",
auth=self._verification_auth,
json={"identity": {"type": "number", "endpoint": phone}, "method": method},
)
...
async def verify(self, reference: str, code: str) -> dict[str, Any]:
path = f"/verification/v1/verifications/id/{quote(reference, safe='')}"
response = await self.client.put(
path,
auth=self._verification_auth,
json={"method": "sms", "sms": {"code": code}},
)
...
_verification_auth ist ein drittes Set von Zugangsdaten: Die Verifizierung authentifiziert sich mit dem Schlüssel und dem Secret ihrer eigenen App im Dashboard, es sind also weder die Schlüssel auf Projektebene, welche die Conversation API verwendet, noch die für die Voice-Anwendung.
start gibt eine id zurück, und das ist die ID, für die Sie den Code melden. Die Methodenwerte sind der Teil, den man kennen sollte: sms für ein Passwort per Nachricht, callout für eines, das bei einem Telefonanruf vorgelesen wird, plus flashcall, seamless und whatsapp. Der Rumpf des Berichts erhält dieselbe Methode als Schlüssel, also {"method": "sms", "sms": {"code": "1234"}} für die Nachricht und {"method": "callout", "callout": {"code": "1234"}} für den Anruf.
Verifizierungen laufen nach wenigen Minuten ab. Behandeln Sie daher eine 400 beim Bericht eher als „eine neue starten“ anstatt als fehlgeschlagenen Versuch. Eine Person aus der Kundschaft auf callout umzustellen, falls die Nachricht nicht ankommt, ist ein zweiter start-Aufruf mit einer anderen Methode. Die Entscheidung, wann dies angeboten wird, ist Anwendungslogik: Wie lange Sie warten, wie viele Versuche Sie zulassen und ob die Kundschaft danach fragt oder Sie für sie entscheiden. Es lohnt sich, dies bewusst zu entwerfen, anstatt es einer Wiederholungsschleife zu überlassen.
E-Mail. Die Bestätigung wird über Mailgun versendet, das sich im selben Plugin befindet:
Falls eine Zahlung oder ein Zahlungs-Abonnement bestätigt wird, senden Sie eine Bestätigungs-E-Mail über Mailgun mit dem Betrag, dem Datum und einer Referenznummer.
Mailgun ist ein viertes Set von Zugangsdaten und der Außenseiter: Es authentifiziert sich per Domain und Schlüssel anstatt per App, wie die anderen drei. Das ist eine Basic-Authentifizierung gegen POST /v3/{domain}/messages, mit api als Benutzername und einem API-Schlüssel als Passwort. o:testmode=yes akzeptiert einen Sendevorgang, ohne ihn zuzustellen, was wissenswert ist, während Sie die Bestätigung anbinden.
Fallstricke
- Starten Sie die 10DLC-Registrierung, bevor Sie den Code für den Versand schreiben. Der Versand von SMS an US-Nummern erfordert dies. Die Genehmigung dauert Tage, und keine Agenten-Geschwindigkeit der Welt kann das abkürzen. Das ist der häufigste Grund, warum aus einem Nachmittags-Build ein Build für nächste Woche wird.
- Eine falsche Region liest sich wie ein Authentifizierungsproblem. Alle URLs der Conversation API sind regionsspezifisch. Falls
CONVERSATION_REGIONnicht mit dem Speicherort der App übereinstimmt, erhalten Sie eine 404-Meldung oder einen Fehler, der auf Ihre Zugangsdaten hinweist. Überprüfen Sie die Region im Dashboard, bevor Sie einen vollkommen intakten Schlüssel neu generieren. - Testen Sie den Fallback-Pfad genauso intensiv wie den Happy Path. Die Verfügbarkeit von RCS variiert je nach Markt, Mobilfunkanbieter und Gerät, und Ihr eigenes Telefon belegt lediglich den Happy Path. Senden Sie an ein Gerät ohne RCS und bestätigen Sie, dass die SMS tatsächlich ankommt.
messages:transcodebietet eine Vorschau darauf, wie eine Card herabgestuft wird, ohne sie zu versenden. - Der MCP-Server führt echte Aktionen auf einem echten Konto aus. Genau das ist der Sinn der Sache, und es bedeutet, ihn auf eine Testnummer statt auf eine Liste auszurichten und darüber nachzudenken, welche Zugangsdaten sich in Ihrem Shell-Profil befinden.
- Belassen Sie den Agenten im Projektstamm. Bei einem Durchlauf suchte der Agent außerhalb des geöffneten Ordners, fand eine ältere Implementierung derselben Funktion in einem benachbarten Verzeichnis und spiegelte diese wider, anstatt die Skills zu verwenden. Der Code funktionierte. Es war aber auch der falsche Code. Öffnen Sie das Projekt, in dem Sie arbeiten möchten.
- Lesen Sie, was der Agent geschrieben hat. Der Wert eines Agenten, der echten Integrations-Code schreibt, anstatt ein undurchsichtiges Ergebnis zurückzugeben, liegt darin, dass der Code direkt zur Überprüfung vorliegt. Die Lücke beim Webhook-Trigger und die fehlende Anrufer-ID waren beide im Diff sichtbar.
Mein Endergebnis
Aufseiten des Kunden: Eine Erinnerung trifft drei Tage vor Fälligkeit der Zahlung ein. Auf einem Gerät, das RCS unterstützt, ist es eine markenkonforme Card von einem verifizierten Absender mit einer Schaltfläche „Jetzt bezahlen“. Sie tippen darauf, verifizieren sich mit einem Passcode, bezahlen, ohne den Thread zu verlassen, und erhalten eine E-Mail-Bestätigung. Ist die Zahlung weiterhin ausstehend, folgt in der Journey eine automatisierte Voice-Erinnerung.
Am Ende erstreckt sich die Journey über Messaging, Verifizierung, Voice und E-Mail, und alles wurde über denselben Cursor-Workflow erstellt. Die Zeit, die mir der Agent zurückgegeben hat, war die Zeit zum Lesen der Dokumentation: das Ausarbeiten der Formen von Nutzdaten und Authentifizierungsschemata über vier Produkte hinweg sowie die Deploy-and-Check-Zyklen, die normalerweise zwischen falschen Nutzdaten und meiner Feststellung darüber liegen. Das Tippen war nie der langsame Teil.
Probieren Sie es selbst aus
Installieren Sie das Plugin über den Cursor Marketplace, exportieren Sie die Zugangsdaten, starten Sie Cursor neu und geben Sie dem Agenten den obigen Prompt für die Conversation API gegen eine Testnummer. Das Plugin ist Open Source unter sinch-plugins auf GitHub.