DeskLink
Turn your local computer into a remotely accessible MCP endpoint for cloud AI agents.
DeskLink is a thin authenticated bridge. It starts the local STDIO MCP servers you already use (or the official filesystem server), discovers their tools, and exposes everything through a single Streamable HTTP MCP URL. Optionally tunnels that URL with ngrok so Claude, ChatGPT, Cursor, Manus, or any remote MCP client can reach your machine securely.
Package name on PyPI: desklink
CLI commands: desklink and devlink (alias)
What it does
- Loads MCP servers from a standard
mcp.json(or injects the official@modelcontextprotocol/server-filesystemif none are defined). - Starts a local Streamable HTTP server (default
http://127.0.0.1:8765/mcp). - Proxies every discovered tool under a namespaced name (
servername__toolname). - Protects the endpoint with a device bearer token and/or OAuth 2.1 (Authorization Code + PKCE).
- Optionally opens an ngrok tunnel so the same endpoint is reachable from the public internet.
- Gives you a simple web dashboard + consent screen for OAuth apps.
You keep full control: tools stay on your machine, approvals can prompt you, and tokens never leave your machine unless you choose.
Quick start
pip install desklink
# Optional but recommended for public tunnels
export NGROK_AUTHTOKEN=your_ngrok_token
desklink start
On first run DeskLink will:
- Create
~/.desklink/token(device bearer token) - Start the official filesystem MCP server scoped to your home directory (unless you provide an
mcp.json) - Print the local and public MCP URLs plus the auth token
Copy the public URL + token into any MCP-compatible client.
Useful flags
desklink start \
--mcp-config ./mcp.json \
--config ./desklink.config.json \
--dir ~/Documents --dir ~/projects \
--port 8765 \
--tunnel ngrok # or "none" for local-only
--token my-static-token # optional override
-v # verbose logging
Configuration
DeskLink looks for two files (first match wins):
| File | Search order |
|---|---|
| DeskLink settings | --config → ./desklink.config.json → ~/.desklink/config.json |
| MCP servers | --mcp-config → ./mcp.json → ~/.desklink/mcp.json |
Example mcp.json
{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents", "/Users/you/projects"]
},
"my-custom-server": {
"command": "python",
"args": ["-m", "my_mcp_server"],
"env": { "API_KEY": "..." }
}
}
}
Any STDIO MCP server works. Tools appear as filesystem__read_file, my-custom-server__do_something, etc.
Example desklink.config.json
{
"logging": { "level": "info" },
"tracing": {
"enabled": true,
"level": "basic",
"redact_sensitive": true
},
"auth": {
"enabled": true
},
"approvals": {
"default": "auto",
"tools": {
"filesystem__write_file": "prompt",
"filesystem__create_directory": "prompt"
}
},
"server": {
"host": "127.0.0.1",
"port": 8765,
"mcp_path": "/mcp"
},
"tunnel": {
"provider": "ngrok"
}
}
Approvals
"auto"– run immediately"prompt"– ask in the terminal before the tool executes
Tracing levels
none/basic/full(full logs args + truncated results; sensitive keys are redacted by default)
Environment variables use the prefix DESKLINK_ (e.g. DESKLINK_SERVER__PORT=9000). NGROK_AUTHTOKEN is also picked up automatically.
Authentication
1. Device bearer token (always available)
Stored at ~/.desklink/token. Sent as:
Authorization: Bearer <token>
Use this for simple scripts or clients that just need a static secret.
2. OAuth 2.1 (recommended for cloud agents)
DeskLink acts as a full OAuth 2.1 authorization server so agents never see your device token.
Create an app (CLI)
desklink create oauth-app claude
desklink create oauth-app chatgpt
desklink create oauth-app manus
desklink create oauth-app cursor
desklink create oauth-app custom --name "My Agent" --public
Presets come with sensible default scopes. Confidential clients get a Client Secret; public clients rely on PKCE.
Endpoints (when desklink start is running)
| Purpose | URL |
|---|---|
| Metadata | http://127.0.0.1:8765/.well-known/oauth-authorization-server |
| Authorize | http://127.0.0.1:8765/oauth/authorize |
| Token | http://127.0.0.1:8765/oauth/token |
| Revoke | http://127.0.0.1:8765/oauth/revoke |
| Dynamic registration | POST http://127.0.0.1:8765/oauth/register |
| Dashboard | http://127.0.0.1:8765/dashboard |
| MCP | http://127.0.0.1:8765/mcp |
When using a tunnel the same paths work on the public URL.
Manage apps
desklink oauth list
desklink oauth revoke <client_id>
Or open /dashboard in a browser – create apps, view Client IDs, revoke access, and see recent activity.
Scopes
| Scope | Meaning |
|---|---|
mcp:tools |
Call tools |
mcp:resources |
Read resources |
filesystem:read |
Read files |
filesystem:write |
Write / create |
admin |
Full control |
All OAuth state lives as JSON under ~/.desklink/oauth/ (no database).
Customer usage flow
- Install & start DeskLink on the machine whose tools/files you want to expose.
- (Optional) Create an OAuth app for the agent you will connect.
- Give the agent the public MCP URL + either the device token or the OAuth Client ID (and secret if confidential).
- The agent discovers tools via normal MCP
tools/listand calls them. DeskLink proxies the call to the local STDIO server, applies your approval policy, and returns the result. - Stop with
Ctrl+C. The tunnel and local servers shut down cleanly.
Extensibility
- Any STDIO MCP server – just add it to
mcp.json. No code changes required. - Approvals & tracing – configure per-tool policies and log levels without touching the bridge.
- Tunnel providers – currently
ngrokandnone. The interface is abstract; additional providers (Cloudflare Tunnel, etc.) can be added later. - Auth – static bearer + full OAuth 2.1 (PKCE, refresh tokens, dynamic registration, consent screen). Device identity stays separate from OAuth application credentials.
- Programmatic use – import
desklinkand driveDeskLinkServer/McpBridgefrom your own Python process if you need tighter integration.
Development
git clone https://github.com/sam-14uel/desklink-py.git
cd desklink-py
pip install -e ".[dev]"
pytest
License
MIT © Samuel Obinna Chimdi
Metadata
Release files for desklink 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| desklink-0.3.0.tar.gz | 20.5 kB | Without provenance | Details for desklink-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| desklink-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for desklink-0.3.0-py3-none-any.whl |
Total release size: 43.7 kB
Release files / desklink-0.3.0.tar.gz
| Download URL | desklink-0.3.0.tar.gz |
|---|---|
| Size | 20.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
051eafc4c54bd7744d16ce067ed4ea00ec55b90112225e02ae53219c8c642fbc
|
|
BLAKE2b-256 checksum How to use checksums |
fe5c98bb87c143cb187a1f02f60d0ee74db95e4073e6fca8625053407e06a548
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.8
|
Release files / desklink-0.3.0-py3-none-any.whl
| Download URL | desklink-0.3.0-py3-none-any.whl |
|---|---|
| Size | 23.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6ff0fd80b8a85faefc16efde9a960917a58d750fbdc4f8b8fe832db04601e226
|
|
BLAKE2b-256 checksum How to use checksums |
235a7b21750e7ed168bf706c5bd39019db6e5c3aae2f4a389bfe698045f97a07
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.8
|
Security for desklink 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.