Skip to content
Visor Developers

The network door

Visor can serve the kit over the network: JSON-RPC over a WebSocket for programs, and MCP for AI agents. It is off until the person turns it on, and every client needs a token the person made for it.

Turning it on

In Visor, open Settings, Advanced settings, Network access:

  1. Turn on Open to clients. Visor listens on 127.0.0.1:7333, so only programs on the phone itself reach it (Termux, for example).
  2. Turn on From the local network to let computers on the same Wi-Fi in too. The row shows the phone's address.
  3. Add a client: give it a name and its scopes. Visor shows the client's token once; copy it then. Visor keeps only its hash.

Remove a client to take its access back: its streams end at once.

To reach the door from a computer without the local network, forward the port over adb: adb forward tcp:7333 tcp:7333.

JSON-RPC over WebSocket

ws://<address>:7333/rpc, with the token as Authorization: Bearer <token>, or as ?token=<token> where a client cannot set headers, as in a browser. The messages are the same as on the adb socket: see JSON-RPC. The client holds only its own scopes, and the shell.* methods are not there.

MCP for AI agents

http://<address>:7333/mcp is an MCP server over Streamable HTTP, with the same token. Each method the client may call is a tool, named with _ for .: glasses_status, camera_photo, display_prompter_show, and so on. A tool's answer is the method's result as JSON text. Streams are not tools; use JSON-RPC for them.

With Claude Code:

claude mcp add --transport http visor http://127.0.0.1:7333/mcp --header "Authorization: Bearer <token>"

To try an agent with no phone, run visor fake and point the agent at it, with no token:

claude mcp add --transport http visor-fake http://127.0.0.1:7333/mcp

Give an agent's client only the scopes it needs. An agent with camera takes photos when it decides to; the glasses' capture light still turns on for each one.

Webhooks

For a service that cannot hold a connection open, such as a home automation hub, Visor can post events instead. When you add a client, give it a webhook URL and pick its topics: those its scopes allow, without params. While the network door is open, Visor subscribes as that client and posts each event, in order, as JSON:

{"client": "home", "topic": "glasses.status", "event": {"paired": true, "link": "connected"}, "time": 1794072397747}

Each post carries X-Visor-Signature: sha256=<hex>, the HMAC-SHA256 of the body. The key is the hex SHA-256 of the client's token, which Visor keeps instead of the token:

key = hashlib.sha256(token.encode()).hexdigest().encode()
valid = hmac.compare_digest("sha256=" + hmac.new(key, body, hashlib.sha256).hexdigest(), signature)

Visor waits 5 seconds for an answer and does not retry. When the receiver keeps failing, Visor's event log says so once a minute.

Clients for other languages

  • Python: clients/python. connect_adb() or connect_network(url, token), then await visor.call(...) and async for event in visor.subscribe(...).
  • TypeScript: clients/typescript, for browsers and Node 22 or later. await Visor.connect(url, token), then visor.call(...) and visor.subscribe(...).
const visor = await Visor.connect("ws://192.168.1.20:7333/rpc", token);
visor.subscribe("glasses.status", {}, (status) => console.log(status));

Raw access

Scope: raw. For research into the glasses' protocol: send a message to any service of the glasses and read what a service sends, as they are.

Name Params Answer
Method raw.send service, type, payload (Base64) {}
Topic raw.messages service {service, type, payload, time}

Visor answers only while its Developer mode is on, and refuses the services it keeps to itself: identity and ownership (36), the link's authentication (54 and 79) and firmware updates (29). A wrong message can still upset the glasses until they restart.