VDK guidance for coding assistants
Save this as AGENTS.md in an app that uses the Visor Development Kit (visor agents > AGENTS.md), or point a
rules file at it. It tells a coding assistant how to build on the VDK. For any detail, search the docs: run
visor docs QUERY, or add the docs MCP server with claude mcp add vdk-docs -- visor docs-mcp and call
search_vdk_docs and read_vdk_doc.
What the VDK is
The VDK lets an Android app, or a program on a computer, use Ray-Ban Meta glasses, the Meta Ray-Ban Display and the
Meta Neural Band through Visor, the Android app that runs them. It is an alternative to Meta's Wearables Device
Access Toolkit (DAT): no Meta AI app, no Meta account, no developer centre. Porting from DAT: read
porting-from-dat.
- Android:
implementation("com.macedonga.visor:kit:<version>"), thenval visor = Visor.connect(context). - Tests:
com.macedonga.visor:kit-testinggivesFakeVisorandfake.connect()in a plain JVM test. - Other languages: JSON-RPC over adb or Visor's network door; Python
visor_kitand TypeScript clients. - A web app on the Display: the TypeScript client's
Visor.connectGlasses(), guarded byonGlasses(). On arefusedwith reasonnot_allowed, tell the person to allow the app in Visor > Settings > Web apps.
Rules
- Ask for scopes before using them:
registerForActivityResult(RequestScopes()), thenlaunch(setOf(...)). A call without its scope fails withKitExceptioncodescope_denied, naming the missingscope. - Every call is a
suspend fun; every stream is a coldFlowthat runs while collected. Collect streams in a lifecycle-aware scope and cancel them to stop: there are no start and stop pairs. - Handle
KitExceptionby itscode(unsupported,not_connected,busy,invalid_params,failed,scope_denied) andreason. Treat an unknown code asfailedand unknown enum-like values as unknown: the value sets (scopes, input actions, gesture actions, layout types) grow. - Layouts for the Display are whole screens: build one with
Layout { }and show it withvisor.display.layout.show(...); a new show replaces it. Clicks come ondisplay.layout.clicksby the element'sid;display.layout.statesays when the wearer went back. For something the wearer should see all day (a timer, a delivery, a score), prefer a Briefing card:visor.display.cards.set(id, name, icon) { header(...); stat(...); detail { } }, kept withcards.keep(id, name, icon, flow) { }, with clicks in its detail ondisplay.cards.clicks. A card takes at most 256 KiB. - A voice phrase reaches the app while it collects
visor.voice.requests(phrases), or starts it when its activity declares the phrases (VoiceLaunch). Reply within about 20 seconds withvisor.voice.reply. band.motionneeds a band the person shares with the phone: checkband.statussharedfirst, and onunsupportedwith reasonnot_shared, show the person the exception's message, which names the setting.- Do not invent methods. The methods and topics are in
kit-protocol(Methods,Topics) and in the docs' JSON-RPC tables;visor methodslists what a Visor answers.
Testing without glasses
FakeVisorin unit tests: setfake.glasses.*andfake.band.shared, sendfake.glasses.input(...),fake.band.gesture(...),fake.band.motion(...),fake.voice.say(...),fake.display.click(id),fake.display.back(),fake.display.clickCard(card, id).visor fakeon a computer: JSON-RPC onws://127.0.0.1:7333/rpc, MCP on/mcp, the Display simulator onhttp://127.0.0.1:7333/display, and a console that plays the glasses.visor record FILEandvisor replay FILEcapture and play back what the wearer did.FakeGlasses.playMotionplays DAT's motion CSV;FakeLive.playplays a recorded H.264 or H.265 stream.