desklink 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- desklink-0.3.0/.gitignore +17 -0
- desklink-0.3.0/ARCHITECTURE.md +32 -0
- desklink-0.3.0/LICENSE +21 -0
- desklink-0.3.0/PKG-INFO +267 -0
- desklink-0.3.0/README.md +228 -0
- desklink-0.3.0/examples/config.yaml +23 -0
- desklink-0.3.0/examples/desklink.config.json +28 -0
- desklink-0.3.0/examples/mcp.json +13 -0
- desklink-0.3.0/pyproject.toml +77 -0
- desklink-0.3.0/src/desklink/__init__.py +3 -0
- desklink-0.3.0/src/desklink/auth/__init__.py +48 -0
- desklink-0.3.0/src/desklink/bridge.py +211 -0
- desklink-0.3.0/src/desklink/cli/__init__.py +4 -0
- desklink-0.3.0/src/desklink/cli/main.py +133 -0
- desklink-0.3.0/src/desklink/config/__init__.py +152 -0
- desklink-0.3.0/src/desklink/models/__init__.py +27 -0
- desklink-0.3.0/src/desklink/oauth/__init__.py +291 -0
- desklink-0.3.0/src/desklink/oauth/server.py +254 -0
- desklink-0.3.0/src/desklink/server/__init__.py +145 -0
- desklink-0.3.0/src/desklink/tunnel/__init__.py +91 -0
- desklink-0.3.0/tests/test_basic.py +32 -0
- desklink-0.3.0/tests/test_oauth.py +47 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# DeskLink – Project layout
|
|
2
|
+
|
|
3
|
+
```
|
|
4
|
+
desklink/
|
|
5
|
+
├── pyproject.toml
|
|
6
|
+
├── README.md
|
|
7
|
+
├── LICENSE
|
|
8
|
+
├── .gitignore
|
|
9
|
+
├── examples/
|
|
10
|
+
│ └── config.yaml
|
|
11
|
+
├── src/
|
|
12
|
+
│ └── desklink/
|
|
13
|
+
│ ├── __init__.py
|
|
14
|
+
│ ├── cli/
|
|
15
|
+
│ │ └── main.py
|
|
16
|
+
│ ├── server/
|
|
17
|
+
│ │ └── __init__.py
|
|
18
|
+
│ ├── filesystem/
|
|
19
|
+
│ │ └── __init__.py
|
|
20
|
+
│ ├── tunnel/
|
|
21
|
+
│ │ └── __init__.py
|
|
22
|
+
│ ├── auth/
|
|
23
|
+
│ │ └── __init__.py
|
|
24
|
+
│ ├── config/
|
|
25
|
+
│ │ └── __init__.py
|
|
26
|
+
│ └── models/
|
|
27
|
+
│ └── __init__.py
|
|
28
|
+
└── tests/
|
|
29
|
+
└── test_basic.py
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
All core components are modular for future expansion (additional MCP servers, tunnel providers, stronger auth, DeskLink cloud platform).
|
desklink-0.3.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DeskLink Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
desklink-0.3.0/PKG-INFO
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: desklink
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Turn your local computer into a remotely accessible MCP endpoint for cloud AI agents.
|
|
5
|
+
Project-URL: Homepage, https://github.com/sam-14uel/desklink-py
|
|
6
|
+
Project-URL: Repository, https://github.com/sam-14uel/desklink-py
|
|
7
|
+
Project-URL: Issues, https://github.com/sam-14uel/desklink-py/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/sam-14uel/desklink-py#readme
|
|
9
|
+
Author-email: Samuel Obinna Chimdi <sam@chat-atp.com>
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: ai-agents,desklink,mcp,mcp-bridge,model-context-protocol,ngrok,oauth,tunnel
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
22
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: httpx>=0.27.0
|
|
25
|
+
Requires-Dist: mcp<2,>=1.10.0
|
|
26
|
+
Requires-Dist: pydantic-settings>=2.0.0
|
|
27
|
+
Requires-Dist: pydantic>=2.0.0
|
|
28
|
+
Requires-Dist: pyngrok>=7.0.0
|
|
29
|
+
Requires-Dist: pyyaml>=6.0
|
|
30
|
+
Requires-Dist: rich>=13.0.0
|
|
31
|
+
Requires-Dist: starlette>=0.40.0
|
|
32
|
+
Requires-Dist: typer>=0.12.0
|
|
33
|
+
Requires-Dist: uvicorn>=0.30.0
|
|
34
|
+
Provides-Extra: dev
|
|
35
|
+
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: pytest>=8.0.0; extra == 'dev'
|
|
37
|
+
Requires-Dist: ruff>=0.6.0; extra == 'dev'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# DeskLink
|
|
41
|
+
|
|
42
|
+
**Turn your local computer into a remotely accessible MCP endpoint for cloud AI agents.**
|
|
43
|
+
|
|
44
|
+
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.
|
|
45
|
+
|
|
46
|
+
Package name on PyPI: **`desklink`**
|
|
47
|
+
CLI commands: `desklink` and `devlink` (alias)
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## What it does
|
|
52
|
+
|
|
53
|
+
1. Loads MCP servers from a standard `mcp.json` (or injects the official `@modelcontextprotocol/server-filesystem` if none are defined).
|
|
54
|
+
2. Starts a local Streamable HTTP server (default `http://127.0.0.1:8765/mcp`).
|
|
55
|
+
3. Proxies every discovered tool under a namespaced name (`servername__toolname`).
|
|
56
|
+
4. Protects the endpoint with a device bearer token **and/or** OAuth 2.1 (Authorization Code + PKCE).
|
|
57
|
+
5. Optionally opens an ngrok tunnel so the same endpoint is reachable from the public internet.
|
|
58
|
+
6. Gives you a simple web dashboard + consent screen for OAuth apps.
|
|
59
|
+
|
|
60
|
+
You keep full control: tools stay on your machine, approvals can prompt you, and tokens never leave your machine unless you choose.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Quick start
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
pip install desklink
|
|
68
|
+
|
|
69
|
+
# Optional but recommended for public tunnels
|
|
70
|
+
export NGROK_AUTHTOKEN=your_ngrok_token
|
|
71
|
+
|
|
72
|
+
desklink start
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
On first run DeskLink will:
|
|
76
|
+
|
|
77
|
+
- Create `~/.desklink/token` (device bearer token)
|
|
78
|
+
- Start the official filesystem MCP server scoped to your home directory (unless you provide an `mcp.json`)
|
|
79
|
+
- Print the **local** and **public** MCP URLs plus the auth token
|
|
80
|
+
|
|
81
|
+
Copy the public URL + token into any MCP-compatible client.
|
|
82
|
+
|
|
83
|
+
### Useful flags
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
desklink start \
|
|
87
|
+
--mcp-config ./mcp.json \
|
|
88
|
+
--config ./desklink.config.json \
|
|
89
|
+
--dir ~/Documents --dir ~/projects \
|
|
90
|
+
--port 8765 \
|
|
91
|
+
--tunnel ngrok # or "none" for local-only
|
|
92
|
+
--token my-static-token # optional override
|
|
93
|
+
-v # verbose logging
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Configuration
|
|
99
|
+
|
|
100
|
+
DeskLink looks for two files (first match wins):
|
|
101
|
+
|
|
102
|
+
| File | Search order |
|
|
103
|
+
|------|--------------|
|
|
104
|
+
| **DeskLink settings** | `--config` → `./desklink.config.json` → `~/.desklink/config.json` |
|
|
105
|
+
| **MCP servers** | `--mcp-config` → `./mcp.json` → `~/.desklink/mcp.json` |
|
|
106
|
+
|
|
107
|
+
### Example `mcp.json`
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"mcpServers": {
|
|
112
|
+
"filesystem": {
|
|
113
|
+
"command": "npx",
|
|
114
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents", "/Users/you/projects"]
|
|
115
|
+
},
|
|
116
|
+
"my-custom-server": {
|
|
117
|
+
"command": "python",
|
|
118
|
+
"args": ["-m", "my_mcp_server"],
|
|
119
|
+
"env": { "API_KEY": "..." }
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Any STDIO MCP server works. Tools appear as `filesystem__read_file`, `my-custom-server__do_something`, etc.
|
|
126
|
+
|
|
127
|
+
### Example `desklink.config.json`
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"logging": { "level": "info" },
|
|
132
|
+
"tracing": {
|
|
133
|
+
"enabled": true,
|
|
134
|
+
"level": "basic",
|
|
135
|
+
"redact_sensitive": true
|
|
136
|
+
},
|
|
137
|
+
"auth": {
|
|
138
|
+
"enabled": true
|
|
139
|
+
},
|
|
140
|
+
"approvals": {
|
|
141
|
+
"default": "auto",
|
|
142
|
+
"tools": {
|
|
143
|
+
"filesystem__write_file": "prompt",
|
|
144
|
+
"filesystem__create_directory": "prompt"
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"server": {
|
|
148
|
+
"host": "127.0.0.1",
|
|
149
|
+
"port": 8765,
|
|
150
|
+
"mcp_path": "/mcp"
|
|
151
|
+
},
|
|
152
|
+
"tunnel": {
|
|
153
|
+
"provider": "ngrok"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Approvals**
|
|
159
|
+
- `"auto"` – run immediately
|
|
160
|
+
- `"prompt"` – ask in the terminal before the tool executes
|
|
161
|
+
|
|
162
|
+
**Tracing levels**
|
|
163
|
+
- `none` / `basic` / `full` (full logs args + truncated results; sensitive keys are redacted by default)
|
|
164
|
+
|
|
165
|
+
Environment variables use the prefix `DESKLINK_` (e.g. `DESKLINK_SERVER__PORT=9000`). `NGROK_AUTHTOKEN` is also picked up automatically.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Authentication
|
|
170
|
+
|
|
171
|
+
### 1. Device bearer token (always available)
|
|
172
|
+
|
|
173
|
+
Stored at `~/.desklink/token`. Sent as:
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
Authorization: Bearer <token>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Use this for simple scripts or clients that just need a static secret.
|
|
180
|
+
|
|
181
|
+
### 2. OAuth 2.1 (recommended for cloud agents)
|
|
182
|
+
|
|
183
|
+
DeskLink acts as a full OAuth 2.1 authorization server so agents never see your device token.
|
|
184
|
+
|
|
185
|
+
**Create an app (CLI)**
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
desklink create oauth-app claude
|
|
189
|
+
desklink create oauth-app chatgpt
|
|
190
|
+
desklink create oauth-app manus
|
|
191
|
+
desklink create oauth-app cursor
|
|
192
|
+
desklink create oauth-app custom --name "My Agent" --public
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Presets come with sensible default scopes. Confidential clients get a Client Secret; public clients rely on PKCE.
|
|
196
|
+
|
|
197
|
+
**Endpoints (when `desklink start` is running)**
|
|
198
|
+
|
|
199
|
+
| Purpose | URL |
|
|
200
|
+
|---------|-----|
|
|
201
|
+
| Metadata | `http://127.0.0.1:8765/.well-known/oauth-authorization-server` |
|
|
202
|
+
| Authorize | `http://127.0.0.1:8765/oauth/authorize` |
|
|
203
|
+
| Token | `http://127.0.0.1:8765/oauth/token` |
|
|
204
|
+
| Revoke | `http://127.0.0.1:8765/oauth/revoke` |
|
|
205
|
+
| Dynamic registration | `POST http://127.0.0.1:8765/oauth/register` |
|
|
206
|
+
| Dashboard | `http://127.0.0.1:8765/dashboard` |
|
|
207
|
+
| MCP | `http://127.0.0.1:8765/mcp` |
|
|
208
|
+
|
|
209
|
+
When using a tunnel the same paths work on the public URL.
|
|
210
|
+
|
|
211
|
+
**Manage apps**
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
desklink oauth list
|
|
215
|
+
desklink oauth revoke <client_id>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Or open `/dashboard` in a browser – create apps, view Client IDs, revoke access, and see recent activity.
|
|
219
|
+
|
|
220
|
+
**Scopes**
|
|
221
|
+
|
|
222
|
+
| Scope | Meaning |
|
|
223
|
+
|-------|---------|
|
|
224
|
+
| `mcp:tools` | Call tools |
|
|
225
|
+
| `mcp:resources` | Read resources |
|
|
226
|
+
| `filesystem:read` | Read files |
|
|
227
|
+
| `filesystem:write` | Write / create |
|
|
228
|
+
| `admin` | Full control |
|
|
229
|
+
|
|
230
|
+
All OAuth state lives as JSON under `~/.desklink/oauth/` (no database).
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Customer usage flow
|
|
235
|
+
|
|
236
|
+
1. Install & start DeskLink on the machine whose tools/files you want to expose.
|
|
237
|
+
2. (Optional) Create an OAuth app for the agent you will connect.
|
|
238
|
+
3. Give the agent the **public MCP URL** + either the device token or the OAuth Client ID (and secret if confidential).
|
|
239
|
+
4. The agent discovers tools via normal MCP `tools/list` and calls them. DeskLink proxies the call to the local STDIO server, applies your approval policy, and returns the result.
|
|
240
|
+
5. Stop with `Ctrl+C`. The tunnel and local servers shut down cleanly.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Extensibility
|
|
245
|
+
|
|
246
|
+
- **Any STDIO MCP server** – just add it to `mcp.json`. No code changes required.
|
|
247
|
+
- **Approvals & tracing** – configure per-tool policies and log levels without touching the bridge.
|
|
248
|
+
- **Tunnel providers** – currently `ngrok` and `none`. The interface is abstract; additional providers (Cloudflare Tunnel, etc.) can be added later.
|
|
249
|
+
- **Auth** – static bearer + full OAuth 2.1 (PKCE, refresh tokens, dynamic registration, consent screen). Device identity stays separate from OAuth application credentials.
|
|
250
|
+
- **Programmatic use** – import `desklink` and drive `DeskLinkServer` / `McpBridge` from your own Python process if you need tighter integration.
|
|
251
|
+
|
|
252
|
+
---
|
|
253
|
+
|
|
254
|
+
## Development
|
|
255
|
+
|
|
256
|
+
```bash
|
|
257
|
+
git clone https://github.com/sam-14uel/desklink-py.git
|
|
258
|
+
cd desklink-py
|
|
259
|
+
pip install -e ".[dev]"
|
|
260
|
+
pytest
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
265
|
+
## License
|
|
266
|
+
|
|
267
|
+
MIT © Samuel Obinna Chimdi
|
desklink-0.3.0/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# DeskLink
|
|
2
|
+
|
|
3
|
+
**Turn your local computer into a remotely accessible MCP endpoint for cloud AI agents.**
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
Package name on PyPI: **`desklink`**
|
|
8
|
+
CLI commands: `desklink` and `devlink` (alias)
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## What it does
|
|
13
|
+
|
|
14
|
+
1. Loads MCP servers from a standard `mcp.json` (or injects the official `@modelcontextprotocol/server-filesystem` if none are defined).
|
|
15
|
+
2. Starts a local Streamable HTTP server (default `http://127.0.0.1:8765/mcp`).
|
|
16
|
+
3. Proxies every discovered tool under a namespaced name (`servername__toolname`).
|
|
17
|
+
4. Protects the endpoint with a device bearer token **and/or** OAuth 2.1 (Authorization Code + PKCE).
|
|
18
|
+
5. Optionally opens an ngrok tunnel so the same endpoint is reachable from the public internet.
|
|
19
|
+
6. Gives you a simple web dashboard + consent screen for OAuth apps.
|
|
20
|
+
|
|
21
|
+
You keep full control: tools stay on your machine, approvals can prompt you, and tokens never leave your machine unless you choose.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install desklink
|
|
29
|
+
|
|
30
|
+
# Optional but recommended for public tunnels
|
|
31
|
+
export NGROK_AUTHTOKEN=your_ngrok_token
|
|
32
|
+
|
|
33
|
+
desklink start
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
On first run DeskLink will:
|
|
37
|
+
|
|
38
|
+
- Create `~/.desklink/token` (device bearer token)
|
|
39
|
+
- Start the official filesystem MCP server scoped to your home directory (unless you provide an `mcp.json`)
|
|
40
|
+
- Print the **local** and **public** MCP URLs plus the auth token
|
|
41
|
+
|
|
42
|
+
Copy the public URL + token into any MCP-compatible client.
|
|
43
|
+
|
|
44
|
+
### Useful flags
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
desklink start \
|
|
48
|
+
--mcp-config ./mcp.json \
|
|
49
|
+
--config ./desklink.config.json \
|
|
50
|
+
--dir ~/Documents --dir ~/projects \
|
|
51
|
+
--port 8765 \
|
|
52
|
+
--tunnel ngrok # or "none" for local-only
|
|
53
|
+
--token my-static-token # optional override
|
|
54
|
+
-v # verbose logging
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## Configuration
|
|
60
|
+
|
|
61
|
+
DeskLink looks for two files (first match wins):
|
|
62
|
+
|
|
63
|
+
| File | Search order |
|
|
64
|
+
|------|--------------|
|
|
65
|
+
| **DeskLink settings** | `--config` → `./desklink.config.json` → `~/.desklink/config.json` |
|
|
66
|
+
| **MCP servers** | `--mcp-config` → `./mcp.json` → `~/.desklink/mcp.json` |
|
|
67
|
+
|
|
68
|
+
### Example `mcp.json`
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"mcpServers": {
|
|
73
|
+
"filesystem": {
|
|
74
|
+
"command": "npx",
|
|
75
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/you/Documents", "/Users/you/projects"]
|
|
76
|
+
},
|
|
77
|
+
"my-custom-server": {
|
|
78
|
+
"command": "python",
|
|
79
|
+
"args": ["-m", "my_mcp_server"],
|
|
80
|
+
"env": { "API_KEY": "..." }
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Any STDIO MCP server works. Tools appear as `filesystem__read_file`, `my-custom-server__do_something`, etc.
|
|
87
|
+
|
|
88
|
+
### Example `desklink.config.json`
|
|
89
|
+
|
|
90
|
+
```json
|
|
91
|
+
{
|
|
92
|
+
"logging": { "level": "info" },
|
|
93
|
+
"tracing": {
|
|
94
|
+
"enabled": true,
|
|
95
|
+
"level": "basic",
|
|
96
|
+
"redact_sensitive": true
|
|
97
|
+
},
|
|
98
|
+
"auth": {
|
|
99
|
+
"enabled": true
|
|
100
|
+
},
|
|
101
|
+
"approvals": {
|
|
102
|
+
"default": "auto",
|
|
103
|
+
"tools": {
|
|
104
|
+
"filesystem__write_file": "prompt",
|
|
105
|
+
"filesystem__create_directory": "prompt"
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"server": {
|
|
109
|
+
"host": "127.0.0.1",
|
|
110
|
+
"port": 8765,
|
|
111
|
+
"mcp_path": "/mcp"
|
|
112
|
+
},
|
|
113
|
+
"tunnel": {
|
|
114
|
+
"provider": "ngrok"
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Approvals**
|
|
120
|
+
- `"auto"` – run immediately
|
|
121
|
+
- `"prompt"` – ask in the terminal before the tool executes
|
|
122
|
+
|
|
123
|
+
**Tracing levels**
|
|
124
|
+
- `none` / `basic` / `full` (full logs args + truncated results; sensitive keys are redacted by default)
|
|
125
|
+
|
|
126
|
+
Environment variables use the prefix `DESKLINK_` (e.g. `DESKLINK_SERVER__PORT=9000`). `NGROK_AUTHTOKEN` is also picked up automatically.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Authentication
|
|
131
|
+
|
|
132
|
+
### 1. Device bearer token (always available)
|
|
133
|
+
|
|
134
|
+
Stored at `~/.desklink/token`. Sent as:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
Authorization: Bearer <token>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Use this for simple scripts or clients that just need a static secret.
|
|
141
|
+
|
|
142
|
+
### 2. OAuth 2.1 (recommended for cloud agents)
|
|
143
|
+
|
|
144
|
+
DeskLink acts as a full OAuth 2.1 authorization server so agents never see your device token.
|
|
145
|
+
|
|
146
|
+
**Create an app (CLI)**
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
desklink create oauth-app claude
|
|
150
|
+
desklink create oauth-app chatgpt
|
|
151
|
+
desklink create oauth-app manus
|
|
152
|
+
desklink create oauth-app cursor
|
|
153
|
+
desklink create oauth-app custom --name "My Agent" --public
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Presets come with sensible default scopes. Confidential clients get a Client Secret; public clients rely on PKCE.
|
|
157
|
+
|
|
158
|
+
**Endpoints (when `desklink start` is running)**
|
|
159
|
+
|
|
160
|
+
| Purpose | URL |
|
|
161
|
+
|---------|-----|
|
|
162
|
+
| Metadata | `http://127.0.0.1:8765/.well-known/oauth-authorization-server` |
|
|
163
|
+
| Authorize | `http://127.0.0.1:8765/oauth/authorize` |
|
|
164
|
+
| Token | `http://127.0.0.1:8765/oauth/token` |
|
|
165
|
+
| Revoke | `http://127.0.0.1:8765/oauth/revoke` |
|
|
166
|
+
| Dynamic registration | `POST http://127.0.0.1:8765/oauth/register` |
|
|
167
|
+
| Dashboard | `http://127.0.0.1:8765/dashboard` |
|
|
168
|
+
| MCP | `http://127.0.0.1:8765/mcp` |
|
|
169
|
+
|
|
170
|
+
When using a tunnel the same paths work on the public URL.
|
|
171
|
+
|
|
172
|
+
**Manage apps**
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
desklink oauth list
|
|
176
|
+
desklink oauth revoke <client_id>
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Or open `/dashboard` in a browser – create apps, view Client IDs, revoke access, and see recent activity.
|
|
180
|
+
|
|
181
|
+
**Scopes**
|
|
182
|
+
|
|
183
|
+
| Scope | Meaning |
|
|
184
|
+
|-------|---------|
|
|
185
|
+
| `mcp:tools` | Call tools |
|
|
186
|
+
| `mcp:resources` | Read resources |
|
|
187
|
+
| `filesystem:read` | Read files |
|
|
188
|
+
| `filesystem:write` | Write / create |
|
|
189
|
+
| `admin` | Full control |
|
|
190
|
+
|
|
191
|
+
All OAuth state lives as JSON under `~/.desklink/oauth/` (no database).
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Customer usage flow
|
|
196
|
+
|
|
197
|
+
1. Install & start DeskLink on the machine whose tools/files you want to expose.
|
|
198
|
+
2. (Optional) Create an OAuth app for the agent you will connect.
|
|
199
|
+
3. Give the agent the **public MCP URL** + either the device token or the OAuth Client ID (and secret if confidential).
|
|
200
|
+
4. The agent discovers tools via normal MCP `tools/list` and calls them. DeskLink proxies the call to the local STDIO server, applies your approval policy, and returns the result.
|
|
201
|
+
5. Stop with `Ctrl+C`. The tunnel and local servers shut down cleanly.
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## Extensibility
|
|
206
|
+
|
|
207
|
+
- **Any STDIO MCP server** – just add it to `mcp.json`. No code changes required.
|
|
208
|
+
- **Approvals & tracing** – configure per-tool policies and log levels without touching the bridge.
|
|
209
|
+
- **Tunnel providers** – currently `ngrok` and `none`. The interface is abstract; additional providers (Cloudflare Tunnel, etc.) can be added later.
|
|
210
|
+
- **Auth** – static bearer + full OAuth 2.1 (PKCE, refresh tokens, dynamic registration, consent screen). Device identity stays separate from OAuth application credentials.
|
|
211
|
+
- **Programmatic use** – import `desklink` and drive `DeskLinkServer` / `McpBridge` from your own Python process if you need tighter integration.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Development
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
git clone https://github.com/sam-14uel/desklink-py.git
|
|
219
|
+
cd desklink-py
|
|
220
|
+
pip install -e ".[dev]"
|
|
221
|
+
pytest
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT © Samuel Obinna Chimdi
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Example DeskLink configuration
|
|
2
|
+
# Copy to ~/.desklink/config.yaml and adjust as needed.
|
|
3
|
+
# Environment variables (DESKLINK_*) override these values.
|
|
4
|
+
|
|
5
|
+
filesystem:
|
|
6
|
+
allowed_directories:
|
|
7
|
+
- ~/Documents
|
|
8
|
+
- ~/projects
|
|
9
|
+
read_only: false
|
|
10
|
+
|
|
11
|
+
tunnel:
|
|
12
|
+
provider: ngrok
|
|
13
|
+
# authtoken: set via NGROK_AUTHTOKEN env var
|
|
14
|
+
region: null
|
|
15
|
+
|
|
16
|
+
auth:
|
|
17
|
+
enabled: true
|
|
18
|
+
# token: auto-generated and stored in ~/.desklink/token if omitted
|
|
19
|
+
|
|
20
|
+
server:
|
|
21
|
+
host: 127.0.0.1
|
|
22
|
+
port: 8765
|
|
23
|
+
mcp_path: /mcp
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"logging": {
|
|
3
|
+
"level": "info"
|
|
4
|
+
},
|
|
5
|
+
"tracing": {
|
|
6
|
+
"enabled": true,
|
|
7
|
+
"level": "basic",
|
|
8
|
+
"redact_sensitive": true
|
|
9
|
+
},
|
|
10
|
+
"auth": {
|
|
11
|
+
"enabled": true
|
|
12
|
+
},
|
|
13
|
+
"approvals": {
|
|
14
|
+
"default": "auto",
|
|
15
|
+
"tools": {
|
|
16
|
+
"filesystem__write_file": "prompt",
|
|
17
|
+
"filesystem__create_directory": "prompt"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"server": {
|
|
21
|
+
"host": "127.0.0.1",
|
|
22
|
+
"port": 8765,
|
|
23
|
+
"mcp_path": "/mcp"
|
|
24
|
+
},
|
|
25
|
+
"tunnel": {
|
|
26
|
+
"provider": "ngrok"
|
|
27
|
+
}
|
|
28
|
+
}
|