teleport-mcp
teleport-mcp
An MCP server that lets an LLM agent drive a TeleportXR headless client: connect to Teleport servers, move an avatar, send input, and inspect the geometry a server has streamed.
Why this exists
The headless client is two processes. teleportd is a service that owns live streaming
connections; teleport_cli is a thin front end that sends it commands and exits. That split
is what makes agent control possible at all: an agent issues a command, reports to the user,
waits for feedback, and acts again — three tool calls that may be minutes apart. A session
that died with its terminal could not support that flow.
This package is the adapter. It speaks MCP over stdio to the agent, and the
local control protocol over a loopback socket
to teleportd.
Requirements
Node 22 or later.
A
teleportdbinary. Build it from theTeleport/repo:cd ../Teleport && cmake -B build_pc_client -S . && cmake --build build_pc_client
Install
npm installConfigure
Claude Code
Add to .mcp.json at the root of the workspace:
{
"mcpServers": {
"teleport": {
"command": "node",
"args": ["teleport-mcp/src/index.js"]
}
}
}Claude Desktop
Absolute paths, since the working directory is not the workspace:
{
"mcpServers": {
"teleport": {
"command": "node",
"args": ["/home/you/teleport/teleport-mcp/src/index.js"],
"env": {
"TELEPORTD_PATH": "/home/you/teleport/Teleport/build_pc_client/bin/teleportd"
}
}
}
}Environment
Variable | Purpose |
| Control port. Default 10510. |
| Path to the |
| Set to |
Tools
Tool | Does |
| Service version, all connections, current identity — start here |
| Open a connection; returns its id |
| Every open connection with its state |
| State, server, latency, inputs and mode for one connection |
| Close one connection |
| Move and/or turn the avatar |
| Inputs the server has declared |
| Send a binary, analogue or motion input event |
|
|
| Summary, node list, or pointer resources |
| Show, start or clear the sign-in |
| Any control command verbatim — the escape hatch |
Every tool returns the service's JSON data object as structuredContent, alongside a text
rendering of the same object.
The agent flow
teleport_overview → is anything already connected?
teleport_connect address=host:8080 → { id: 1 }
teleport_connection_status id=1 → poll until state is CONNECTED
teleport_set_mode id=1 mode=simulated
teleport_get_geometry id=1 what=nodes → what the server sentTwo things that are not guessable, and that the tool descriptions repeat:
teleport_connectreturns before the connection completes. The id is valid immediately; the connection is still negotiating. Pollteleport_connection_statusuntilstateisCONNECTED.Connections outlive the conversation. A connection opened in one session is still there in the next. Call
teleport_overviewbefore assuming you need a new one.
Signing in
Sign-in is an OAuth device-code flow, and the service has no browser. teleport_identity with
action: "signin" starts it; poll action: "show" and read pendingSignIn for the URL and
code to relay to the user.
Shutting down
shutdown stops the service and every live stream with it. It is deliberately not a tool —
reach it through teleport_raw_command if you mean it.
Development
npm test # framing, dot-stuffing, reconnect, and the command mapping
node smoke.mjs # end-to-end: spawns the server, autostarts teleportd, calls every toolsmoke.mjs honours TELEPORT_SERVICE_PORT, so run it on a scratch port (10599) rather than
against a service you are using.
How it talks to the service
The control protocol is one request line in, dot-stuffed lines out, terminated by a lone .,
with an OK / ERROR <message> status header. Three properties shape src/control-client.js:
No message ids. A socket carries one request at a time, so concurrent tool calls queue rather than interleave.
Session state is per-socket. The output format and the selected connection are lost if the socket drops, so the client re-sends
format jsonon every connect and re-selects the connection before every connection-scoped command rather than trusting an earlieruse.uids are strings.
avs::uidis 64-bit and Teleport's uids exceed 2^53, so they cross the wire as decimal strings — the same conventionteleport-web-clientandteleport-nodejsuse.
src/daemon.js starts the service if it is not running and never stops it. That is the
point of the split.
Licence
MIT.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/teleportxr/teleport-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server