as-mcp-server 0.1.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.
- as_mcp_server-0.1.0/LICENSE +21 -0
- as_mcp_server-0.1.0/PKG-INFO +394 -0
- as_mcp_server-0.1.0/README.md +295 -0
- as_mcp_server-0.1.0/pyproject.toml +146 -0
- as_mcp_server-0.1.0/pyproject.toml.orig +129 -0
- as_mcp_server-0.1.0/src/as_mcp_server/__init__.py +14 -0
- as_mcp_server-0.1.0/src/as_mcp_server/__main__.py +10 -0
- as_mcp_server-0.1.0/src/as_mcp_server/cli.py +232 -0
- as_mcp_server-0.1.0/src/as_mcp_server/client.py +877 -0
- as_mcp_server-0.1.0/src/as_mcp_server/config.py +162 -0
- as_mcp_server-0.1.0/src/as_mcp_server/limits.py +63 -0
- as_mcp_server-0.1.0/src/as_mcp_server/profiles.py +93 -0
- as_mcp_server-0.1.0/src/as_mcp_server/secrets.py +71 -0
- as_mcp_server-0.1.0/src/as_mcp_server/server.py +100 -0
- as_mcp_server-0.1.0/src/as_mcp_server/settings.py +38 -0
- as_mcp_server-0.1.0/src/as_mcp_server/tools.py +685 -0
- as_mcp_server-0.1.0/src/as_mcp_server/untrusted.py +217 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenVPN Inc
|
|
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.
|
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: as-mcp-server
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local MCP server that lets AI agents monitor an OpenVPN Access Server through its Web API (read-only)
|
|
5
|
+
Keywords: openvpn,access-server,mcp,model-context-protocol,vpn
|
|
6
|
+
Author: OpenVPN Inc
|
|
7
|
+
Author-email: OpenVPN Inc <as-mcp-maintainers@openvpn.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: System Administrators
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Topic :: System :: Networking
|
|
19
|
+
Classifier: Topic :: System :: Systems Administration
|
|
20
|
+
Requires-Dist: aiofile==3.12.3
|
|
21
|
+
Requires-Dist: annotated-types==0.8.0
|
|
22
|
+
Requires-Dist: anyio==4.15.1
|
|
23
|
+
Requires-Dist: attrs==26.1.0
|
|
24
|
+
Requires-Dist: authlib==1.8.0
|
|
25
|
+
Requires-Dist: beartype==0.22.9
|
|
26
|
+
Requires-Dist: cachetools==7.1.8
|
|
27
|
+
Requires-Dist: caio==0.12.4
|
|
28
|
+
Requires-Dist: certifi==2026.7.22
|
|
29
|
+
Requires-Dist: cffi==2.1.1 ; platform_python_implementation != 'PyPy'
|
|
30
|
+
Requires-Dist: click==8.5.0
|
|
31
|
+
Requires-Dist: cryptography==50.0.1
|
|
32
|
+
Requires-Dist: cyclopts==4.25.2
|
|
33
|
+
Requires-Dist: dnspython==2.8.0
|
|
34
|
+
Requires-Dist: docstring-parser==0.18.0
|
|
35
|
+
Requires-Dist: email-validator==2.3.0
|
|
36
|
+
Requires-Dist: exceptiongroup==1.3.1
|
|
37
|
+
Requires-Dist: fastmcp==4.0.3
|
|
38
|
+
Requires-Dist: fastmcp-slim==4.0.3
|
|
39
|
+
Requires-Dist: griffelib==2.3.0
|
|
40
|
+
Requires-Dist: h11==0.16.0
|
|
41
|
+
Requires-Dist: httpcore==1.0.9
|
|
42
|
+
Requires-Dist: httpcore2==2.12.0 ; sys_platform != 'emscripten'
|
|
43
|
+
Requires-Dist: httpx==0.28.1
|
|
44
|
+
Requires-Dist: httpx2==2.12.0
|
|
45
|
+
Requires-Dist: httpx2-jsfetch==1.0 ; sys_platform == 'emscripten'
|
|
46
|
+
Requires-Dist: idna==3.19
|
|
47
|
+
Requires-Dist: jaraco-classes==3.4.0
|
|
48
|
+
Requires-Dist: jaraco-context==6.1.2
|
|
49
|
+
Requires-Dist: jaraco-functools==4.6.0
|
|
50
|
+
Requires-Dist: jeepney==0.9.0 ; sys_platform == 'linux'
|
|
51
|
+
Requires-Dist: joserfc==1.7.5
|
|
52
|
+
Requires-Dist: jsonref==1.1.0
|
|
53
|
+
Requires-Dist: jsonschema==4.26.0
|
|
54
|
+
Requires-Dist: jsonschema-path==0.5.0
|
|
55
|
+
Requires-Dist: jsonschema-specifications==2025.9.1
|
|
56
|
+
Requires-Dist: keyring==25.7.0
|
|
57
|
+
Requires-Dist: markdown-it-py==4.2.0
|
|
58
|
+
Requires-Dist: mcp==2.2.0
|
|
59
|
+
Requires-Dist: mcp-types==2.2.0
|
|
60
|
+
Requires-Dist: mdurl==0.1.2
|
|
61
|
+
Requires-Dist: more-itertools==11.1.0
|
|
62
|
+
Requires-Dist: openapi-pydantic==0.5.1
|
|
63
|
+
Requires-Dist: opentelemetry-api==1.44.0
|
|
64
|
+
Requires-Dist: packaging==26.3
|
|
65
|
+
Requires-Dist: pathable==0.6.0
|
|
66
|
+
Requires-Dist: platformdirs==4.11.8
|
|
67
|
+
Requires-Dist: py-key-value-aio==0.4.5
|
|
68
|
+
Requires-Dist: pycparser==3.0 ; implementation_name != 'PyPy' and platform_python_implementation != 'PyPy'
|
|
69
|
+
Requires-Dist: pydantic==2.13.5
|
|
70
|
+
Requires-Dist: pydantic-core==2.46.5
|
|
71
|
+
Requires-Dist: pydantic-settings==2.15.0
|
|
72
|
+
Requires-Dist: pygments==2.21.0
|
|
73
|
+
Requires-Dist: pyjwt==2.13.0
|
|
74
|
+
Requires-Dist: pyperclip==1.11.0
|
|
75
|
+
Requires-Dist: python-dotenv==1.2.3
|
|
76
|
+
Requires-Dist: python-multipart==0.0.32
|
|
77
|
+
Requires-Dist: pywin32==312 ; sys_platform == 'win32'
|
|
78
|
+
Requires-Dist: pywin32-ctypes==0.2.3 ; sys_platform == 'win32'
|
|
79
|
+
Requires-Dist: pyyaml==6.0.3
|
|
80
|
+
Requires-Dist: referencing==0.37.0
|
|
81
|
+
Requires-Dist: rich==15.0.0
|
|
82
|
+
Requires-Dist: rich-rst==2.1.0
|
|
83
|
+
Requires-Dist: rpds-py==2026.6.3
|
|
84
|
+
Requires-Dist: secretstorage==3.5.0 ; sys_platform == 'linux'
|
|
85
|
+
Requires-Dist: sse-starlette==3.4.11
|
|
86
|
+
Requires-Dist: starlette==1.6.0
|
|
87
|
+
Requires-Dist: truststore==0.10.4 ; sys_platform != 'emscripten'
|
|
88
|
+
Requires-Dist: typing-extensions==4.16.0
|
|
89
|
+
Requires-Dist: typing-inspection==0.4.4
|
|
90
|
+
Requires-Dist: uncalled-for==0.4.0
|
|
91
|
+
Requires-Dist: uvicorn==0.52.4
|
|
92
|
+
Requires-Dist: watchfiles==1.2.0
|
|
93
|
+
Requires-Dist: websockets==17.1
|
|
94
|
+
Requires-Python: >=3.12
|
|
95
|
+
Project-URL: Homepage, https://github.com/OpenVPN/as-mcp-server
|
|
96
|
+
Project-URL: Issues, https://github.com/OpenVPN/as-mcp-server/issues
|
|
97
|
+
Project-URL: Changelog, https://github.com/OpenVPN/as-mcp-server/blob/main/CHANGELOG.md
|
|
98
|
+
Description-Content-Type: text/markdown
|
|
99
|
+
|
|
100
|
+
# as-mcp-server
|
|
101
|
+
|
|
102
|
+
A local [MCP](https://modelcontextprotocol.io) server that lets your own AI agent (Claude Code,
|
|
103
|
+
Codex, Cursor, VS Code, OpenCode, Claude Desktop, ...) look at your **OpenVPN Access Server**
|
|
104
|
+
through its Web API. This release is **read-only**: it answers questions such as "is the VPN
|
|
105
|
+
healthy", "who is connected", "who failed to log in today", "what can user X reach" and never
|
|
106
|
+
changes anything on the server.
|
|
107
|
+
|
|
108
|
+
It runs on your machine, is started by your agent, and talks to Access Server with your own
|
|
109
|
+
admin credentials. Nothing is hosted by anyone else.
|
|
110
|
+
|
|
111
|
+
Copyright (c) 2026 OpenVPN Inc. Licensed under the MIT License, see `LICENSE`.
|
|
112
|
+
|
|
113
|
+
## Requirements
|
|
114
|
+
|
|
115
|
+
- OpenVPN Access Server **3.1 or newer** (Web API v0.2). Tested on 3.1.0 and 3.2.2.
|
|
116
|
+
- An **admin** account on that server. Use a dedicated one for the agent.
|
|
117
|
+
- [uv](https://docs.astral.sh/uv/) on your machine. It downloads Python if needed; nothing
|
|
118
|
+
else has to be installed.
|
|
119
|
+
|
|
120
|
+
## Install (about two minutes)
|
|
121
|
+
|
|
122
|
+
These steps are written so you can hand them to your agent ("set this up for me") or follow
|
|
123
|
+
them yourself.
|
|
124
|
+
|
|
125
|
+
1. Install uv.
|
|
126
|
+
- macOS / Linux: `curl -LsSf https://astral.sh/uv/install.sh | sh`
|
|
127
|
+
- Windows (PowerShell): `powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"`
|
|
128
|
+
- or `brew install uv`, `winget install astral-sh.uv`, `pipx install uv`.
|
|
129
|
+
2. Store the connection once. This asks for the server URL, the admin username and the
|
|
130
|
+
password (typed hidden, never shown to the agent), checks them against the server, then
|
|
131
|
+
saves URL and username to `~/.config/as-mcp-server/profiles.toml` and the password in your
|
|
132
|
+
OS keyring (macOS Keychain, Windows Credential Manager, Linux Secret Service):
|
|
133
|
+
|
|
134
|
+
uvx as-mcp-server setup
|
|
135
|
+
|
|
136
|
+
If the account needs a second sign-in step you are asked for it as well, with the server's
|
|
137
|
+
own prompt (the authenticator code, or for example a PIN from a RADIUS back end).
|
|
138
|
+
3. Register the server with your agent: run its command from the table in
|
|
139
|
+
[Agent setup](#agent-setup), or add the entry to its configuration file. There is no
|
|
140
|
+
secret in this configuration.
|
|
141
|
+
4. Ask the agent something: "Is my VPN healthy?" It will call `get_status_overview`.
|
|
142
|
+
|
|
143
|
+
Pin a version if you want upgrades to be explicit: `uvx as-mcp-server@0.1.0`. `uvx` keeps
|
|
144
|
+
using the version it first cached until you pass `@latest` or run `uv cache clean`.
|
|
145
|
+
|
|
146
|
+
### Agent setup
|
|
147
|
+
|
|
148
|
+
| Agent | Command | Configuration file |
|
|
149
|
+
| --- | --- | --- |
|
|
150
|
+
| Claude Code | `claude mcp add openvpn-as -- uvx as-mcp-server` | `~/.claude.json` (add `--scope user` to use it in every project) |
|
|
151
|
+
| Codex CLI | `codex mcp add openvpn-as -- uvx as-mcp-server` | `~/.codex/config.toml` |
|
|
152
|
+
| Gemini CLI | `gemini mcp add -s user openvpn-as uvx as-mcp-server` (no `--`) | `~/.gemini/settings.json` |
|
|
153
|
+
| OpenCode | `opencode mcp add openvpn-as -- uvx as-mcp-server` | `~/.config/opencode/opencode.json` (or `.jsonc`) |
|
|
154
|
+
| VS Code | `code --add-mcp '{"name":"openvpn-as","command":"uvx","args":["as-mcp-server"]}'` | the user profile's `mcp.json` ("MCP: Open User Configuration"), or `.vscode/mcp.json` in a project |
|
|
155
|
+
| Cursor | - | `~/.cursor/mcp.json`, or `.cursor/mcp.json` in a project |
|
|
156
|
+
| Claude Desktop | - | macOS `~/Library/Application Support/Claude/claude_desktop_config.json`, Windows `%APPDATA%\Claude\claude_desktop_config.json` (Settings, Developer, Edit Config) |
|
|
157
|
+
|
|
158
|
+
In Windows PowerShell type `claude.cmd` instead of `claude`: PowerShell drops the `--` when it
|
|
159
|
+
runs the `claude.ps1` wrapper, and the command then fails.
|
|
160
|
+
|
|
161
|
+
The entry has a different shape per agent. Claude Desktop, Cursor and Gemini CLI (and Claude
|
|
162
|
+
Code's project file `.mcp.json`):
|
|
163
|
+
|
|
164
|
+
{
|
|
165
|
+
"mcpServers": {
|
|
166
|
+
"openvpn-as": { "command": "uvx", "args": ["as-mcp-server"] }
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
VS Code:
|
|
171
|
+
|
|
172
|
+
{
|
|
173
|
+
"servers": {
|
|
174
|
+
"openvpn-as": { "type": "stdio", "command": "uvx", "args": ["as-mcp-server"] }
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
Codex CLI:
|
|
179
|
+
|
|
180
|
+
[mcp_servers.openvpn-as]
|
|
181
|
+
command = "uvx"
|
|
182
|
+
args = ["as-mcp-server"]
|
|
183
|
+
|
|
184
|
+
OpenCode 1.x (the command is one array, the server sits directly under `mcp`):
|
|
185
|
+
|
|
186
|
+
{
|
|
187
|
+
"$schema": "https://opencode.ai/config.json",
|
|
188
|
+
"mcp": {
|
|
189
|
+
"openvpn-as": { "type": "local", "command": ["uvx", "as-mcp-server"] }
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
OpenCode 2 (the server sits under `mcp.servers`):
|
|
194
|
+
|
|
195
|
+
{
|
|
196
|
+
"mcp": {
|
|
197
|
+
"servers": {
|
|
198
|
+
"openvpn-as": { "type": "local", "command": ["uvx", "as-mcp-server"] }
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
Both versions have the `opencode mcp add openvpn-as -- uvx as-mcp-server` command; 1.x writes
|
|
204
|
+
the first form.
|
|
205
|
+
|
|
206
|
+
### Alternative: environment variables (headless Linux, CI, or by preference)
|
|
207
|
+
|
|
208
|
+
Skip `setup` and give the agent the settings as environment variables of the server entry.
|
|
209
|
+
With a command, add one flag per variable: `--env KEY=value` for Codex CLI and OpenCode, `-e
|
|
210
|
+
KEY=value` for Gemini CLI and Claude Code. Claude Code reads every word after `-e` as another
|
|
211
|
+
variable, so put the server name first:
|
|
212
|
+
|
|
213
|
+
claude mcp add openvpn-as -e OPENVPN_AS_URL=https://vpn.example.com:943 \
|
|
214
|
+
-e OPENVPN_AS_USER=mcp-admin -e OPENVPN_AS_PASSWORD=... -- uvx as-mcp-server
|
|
215
|
+
|
|
216
|
+
In a configuration file the variables go into `env` next to `command` (Claude Desktop, Cursor,
|
|
217
|
+
Gemini CLI, VS Code, Claude Code), into `environment` for OpenCode, and into a
|
|
218
|
+
`[mcp_servers.openvpn-as.env]` table for Codex CLI:
|
|
219
|
+
|
|
220
|
+
{
|
|
221
|
+
"mcpServers": {
|
|
222
|
+
"openvpn-as": {
|
|
223
|
+
"command": "uvx",
|
|
224
|
+
"args": ["as-mcp-server"],
|
|
225
|
+
"env": {
|
|
226
|
+
"OPENVPN_AS_URL": "https://vpn.example.com:943",
|
|
227
|
+
"OPENVPN_AS_USER": "mcp-admin",
|
|
228
|
+
"OPENVPN_AS_PASSWORD": "..."
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
The password then lives in plain text in that file, like any API key in an MCP configuration.
|
|
235
|
+
Environment variables override the stored profile field by field.
|
|
236
|
+
|
|
237
|
+
### Check it works
|
|
238
|
+
|
|
239
|
+
uvx as-mcp-server check
|
|
240
|
+
|
|
241
|
+
prints the URL, user, where each value came from, TLS mode and the server version. Inside a
|
|
242
|
+
conversation the equivalent is the `connection_info` tool, which never fails and reports any
|
|
243
|
+
problem in its `error` field.
|
|
244
|
+
|
|
245
|
+
## Tools
|
|
246
|
+
|
|
247
|
+
All tools are read-only except `login`, which only creates a session.
|
|
248
|
+
|
|
249
|
+
| Tool | What it answers | Parameters |
|
|
250
|
+
| --- | --- | --- |
|
|
251
|
+
| `connection_info` | Which server, which user, TLS mode, session, server version; problems as text | - |
|
|
252
|
+
| `login` | Answer the second sign-in step: the authenticator code or the back end's prompt | `totp_code` |
|
|
253
|
+
| `get_status_overview` | One-call health check: version, EULA, DCO, chosen config values | `config_names` (optional) |
|
|
254
|
+
| `get_server_status` | Internal services and auth modules, last restart | - |
|
|
255
|
+
| `get_server_info` | Version, build, OS, architecture, hostname | - |
|
|
256
|
+
| `get_active_vpn_connections` | Connected clients and daemons | - |
|
|
257
|
+
| `get_log_reports` | Connection / authentication log with filters | `page_size`, `offset`, `username`, `since`, `until`, `errors_only`, `active_only`, `search`, `order_by`, `sort` |
|
|
258
|
+
| `get_license_info` | License type, concurrent connections, subscription state | - |
|
|
259
|
+
| `list_users` | Users and their properties (MFA secrets redacted) | `page_size`, `offset`, `name_contains`, `group`, `admins_only`, `autologin_only`, `usernames`, `order_by`, `sort` |
|
|
260
|
+
| `list_groups` | Groups, member counts or members | `page_size`, `offset`, `name_contains`, `include_members`, `group_names`, `order_by`, `sort` |
|
|
261
|
+
| `get_default_user` | The `__DEFAULT__` profile everyone inherits from | - |
|
|
262
|
+
| `list_access_rulesets` | Rulesets assigned to a user, a group or `__DEFAULT__` | `owner`, `name_contains` |
|
|
263
|
+
| `list_access_rules` | The rules (destination, action) inside rulesets | `ruleset_ids`, `rule_type`, `match_data_contains` |
|
|
264
|
+
|
|
265
|
+
Responses are the Access Server API's own JSON. `since`/`until` are relative durations such
|
|
266
|
+
as `30m`, `24h`, `7d`, `15d 20m`. "What can user X reach": rulesets for the user, for each of
|
|
267
|
+
the user's groups, and for `__DEFAULT__`, then `list_access_rules` with the collected ids.
|
|
268
|
+
|
|
269
|
+
## Configuration reference
|
|
270
|
+
|
|
271
|
+
| Variable | Default | Meaning |
|
|
272
|
+
| --- | --- | --- |
|
|
273
|
+
| `OPENVPN_AS_URL` | from `setup` | `https://host[:943]` |
|
|
274
|
+
| `OPENVPN_AS_USER` | from `setup` | admin username |
|
|
275
|
+
| `OPENVPN_AS_PASSWORD` | from the OS keyring | password |
|
|
276
|
+
| `OPENVPN_AS_CA_CERT` | - | PEM file of a private CA that signed the server certificate |
|
|
277
|
+
| `OPENVPN_AS_INSECURE` | `false` | `true` disables TLS verification. Loud: a warning is logged and `connection_info` reports it. Test servers only. |
|
|
278
|
+
| `OPENVPN_AS_TIMEOUT` | `30` | request timeout in seconds |
|
|
279
|
+
| `OPENVPN_AS_LOG_LEVEL` | `WARNING` | `DEBUG`, `INFO`, `WARNING`, `ERROR`; logs go to stderr and never contain secrets |
|
|
280
|
+
| `OPENVPN_AS_CONFIG_DIR` | `~/.config/as-mcp-server` | where `profiles.toml` lives |
|
|
281
|
+
|
|
282
|
+
Reserved for later releases, not implemented yet: `OPENVPN_AS_PROFILE`, `OPENVPN_AS_TOOLS`,
|
|
283
|
+
`OPENVPN_AS_READ_ONLY`, `OPENVPN_AS_EXTRA_HEADERS`.
|
|
284
|
+
|
|
285
|
+
Remove stored credentials with `uvx as-mcp-server setup --clear`.
|
|
286
|
+
|
|
287
|
+
## Logs and debugging
|
|
288
|
+
|
|
289
|
+
- The server logs to stderr only; `OPENVPN_AS_LOG_LEVEL=DEBUG` adds one line per request
|
|
290
|
+
(`METHOD /path -> status in N ms`) and the background token renewals. Bodies, headers,
|
|
291
|
+
passwords and tokens are never logged.
|
|
292
|
+
- The reliable way to read the log is a terminal: `OPENVPN_AS_LOG_LEVEL=DEBUG uvx
|
|
293
|
+
as-mcp-server check` performs the same login and `GET /server/info` as the
|
|
294
|
+
`connection_info` tool and prints the log next to the result.
|
|
295
|
+
- Inside an agent, where stderr ends up is the agent's business. Claude Code has no log view
|
|
296
|
+
in its `/mcp` panel (it offers View tools, Reconnect and Disable); it writes each server's
|
|
297
|
+
stderr as `Server stderr: ...` records into
|
|
298
|
+
`~/Library/Caches/claude-cli-nodejs/<project>/mcp-logs-<server>/*.jsonl` on macOS
|
|
299
|
+
(`~/.cache/claude-cli-nodejs/...` on Linux), one file per session. `claude --debug` (or
|
|
300
|
+
`--debug-file <path>`) turns on Claude Code's own debug log, which includes its MCP
|
|
301
|
+
connection messages.
|
|
302
|
+
- `OPENVPN_AS_TIMEOUT=60` if the server is slow (default 30 s).
|
|
303
|
+
|
|
304
|
+
## MFA and other second sign-in steps
|
|
305
|
+
|
|
306
|
+
If the admin account needs a second step, the first tool call reports it and quotes the
|
|
307
|
+
server's own prompt. For Access Server's built-in authenticator that is the 6-digit TOTP code;
|
|
308
|
+
for a RADIUS, LDAP or PAM back end it is whatever the back end asks for (a PIN, a Duo passcode,
|
|
309
|
+
an answer to a question), passed through word for word. The agent shows you the prompt, asks
|
|
310
|
+
for the answer and calls `login`. The session then lasts for the lifetime of the MCP process:
|
|
311
|
+
Access Server issues short-lived tokens (10 minutes by default), and the server renews its
|
|
312
|
+
token in the background before it expires, so you are asked again only after the limit Access
|
|
313
|
+
Server puts on renewals (4 hours by default), or if the machine slept past a token's expiry.
|
|
314
|
+
|
|
315
|
+
The answer goes to the challenge the server already issued, so a push- or SMS-based back end
|
|
316
|
+
is not triggered a second time, as long as you answer within about 90 seconds (Access Server
|
|
317
|
+
forgets a challenge after two to three minutes). After a wrong answer or a longer pause the
|
|
318
|
+
server issues a new challenge; if it asks a different question, the agent quotes it before
|
|
319
|
+
anything is sent. Authenticator codes are single-use and must be exactly six digits (a
|
|
320
|
+
mistyped code is refused locally and does not count as a failed attempt); five wrong answers
|
|
321
|
+
lock the account for 15 minutes (Access Server default).
|
|
322
|
+
|
|
323
|
+
### SAML-only accounts
|
|
324
|
+
|
|
325
|
+
as-mcp-server signs in with a username and password, plus the answer to a second step when
|
|
326
|
+
asked. It cannot
|
|
327
|
+
complete the browser-based SAML flow. When the Access Server's default authentication system is
|
|
328
|
+
SAML, an admin account that follows that default is refused with an error that says so, and no
|
|
329
|
+
MFA code is requested (a code could never succeed). Use an administrator whose authentication
|
|
330
|
+
method is set per user to something else: `list_users` shows it as `auth_method` (`sacli` calls
|
|
331
|
+
it `user_auth_type`), and the built-in `openvpn` administrator uses `local`.
|
|
332
|
+
|
|
333
|
+
## Security notes
|
|
334
|
+
|
|
335
|
+
- Your password never enters the conversation: `setup` is a terminal command. Only the
|
|
336
|
+
answer to a second sign-in step passes through the agent: a 30-second, single-use
|
|
337
|
+
authenticator code, or whatever a RADIUS, LDAP or PAM back end asks for. A static PIN or
|
|
338
|
+
passcode given this way reaches the model provider like any other tool argument; if that
|
|
339
|
+
is not acceptable, use an admin account whose second step is a one-time code.
|
|
340
|
+
- Tool responses go to your agent's model provider. The server redacts TOTP secrets, which
|
|
341
|
+
Access Server otherwise includes in user listings, the subscription key from the license
|
|
342
|
+
information, and configuration values whose key name looks like a secret (passwords,
|
|
343
|
+
bind passwords, tokens, private keys, the subscription bundle; a trailing version or
|
|
344
|
+
index such as `.2.9.0` does not hide the name) or that Access Server itself marks as
|
|
345
|
+
redacted, in `get_status_overview`; everything else in a response (user names, addresses,
|
|
346
|
+
log entries, other configuration values) is visible to the model.
|
|
347
|
+
- Log records and the list of connected clients contain text that anyone on the internet
|
|
348
|
+
can choose: a failed login is logged with the username exactly as typed, and VPN clients
|
|
349
|
+
report their own platform and version. Such text can be written to look like log lines
|
|
350
|
+
or instructions for the model. `get_log_reports` and `get_active_vpn_connections` show a
|
|
351
|
+
client-chosen username, certificate name, platform, version or proxied address only when
|
|
352
|
+
it looks like a plain value; anything else (for example newlines, control or invisible
|
|
353
|
+
characters, sentence punctuation, more than three words, more than 64 characters or
|
|
354
|
+
16 East Asian characters) is replaced by
|
|
355
|
+
`{"withheld": true, "length": ..., "flags": [...], "fingerprint": ...}` without the text.
|
|
356
|
+
The same fingerprint means the same text. Read the full records in the Access Server
|
|
357
|
+
Admin UI if you need them. This is a heuristic: a short plain-looking string such as
|
|
358
|
+
`ignore_previous_instructions` still gets through, so treat a model's security summary
|
|
359
|
+
as a draft all the same.
|
|
360
|
+
- Tool calls are rate limited: 30 in a row, then 30 per minute (one every two seconds). An
|
|
361
|
+
agent stuck in a loop gets a tool error that tells it how long to wait instead of sending
|
|
362
|
+
Access Server a request per call. Listing the tools and connecting are never limited.
|
|
363
|
+
- Use a dedicated admin account so its lockout or revocation affects only the agent.
|
|
364
|
+
- The keyring item is bound to the Python binary on macOS; after a `uvx` upgrade the system
|
|
365
|
+
may ask once whether the new binary may read it.
|
|
366
|
+
- See `SECURITY.md` for reporting.
|
|
367
|
+
|
|
368
|
+
## Compatibility
|
|
369
|
+
|
|
370
|
+
| Access Server | Result |
|
|
371
|
+
| --- | --- |
|
|
372
|
+
| 3.2.2 | all tools verified |
|
|
373
|
+
| 3.1.0 | all tools verified |
|
|
374
|
+
| 3.0.x | not supported (Web API v0.1) |
|
|
375
|
+
|
|
376
|
+
The Web API is declared unstable by OpenVPN; an Access Server upgrade that changes it needs a
|
|
377
|
+
matching `as-mcp-server` release. Please open an issue with the server version if a tool
|
|
378
|
+
stops working.
|
|
379
|
+
|
|
380
|
+
## Roadmap and feedback
|
|
381
|
+
|
|
382
|
+
This first release only reads. Later releases may add safe write operations (users, groups, access
|
|
383
|
+
rules, profiles) and server administration, each behind explicit opt-in. Tell us what is
|
|
384
|
+
missing with the **Tool request** issue template and report problems with **Bug report**.
|
|
385
|
+
|
|
386
|
+
## Development
|
|
387
|
+
|
|
388
|
+
make install # uv sync
|
|
389
|
+
make check # ruff + pytest (no network)
|
|
390
|
+
make stand-up # two real Access Servers and FreeRADIUS in Docker, see dev/README.md
|
|
391
|
+
make live # tests against them
|
|
392
|
+
make release-build # the package as published: dependency versions pinned to uv.lock
|
|
393
|
+
|
|
394
|
+
Agent instructions: `AGENTS.md`.
|