walspro-ai
Headless OAuth device connector for wals.pro AI 4 weclapp.
Install it on an agent host that cannot receive a browser callback — a CLI
agent, an SSH session, a container, a remote desktop. It performs an RFC 8628
device authorization against the hosted issuer and then forwards JSON-RPC
frames between a local stdio MCP client and the hosted /v1/mcp runtime.
It is a transport and authentication adapter: it registers no MCP tools, implements no business logic, and never sees weclapp API credentials. Its optional local file transport supplies bytes to the server-owned preview/execute flow. The tool catalog, the user policy, the write-approval flow and the audit pipeline all stay on the server.
"weclapp" is the ERP this connector talks to. This distribution is published by Wals.pro GmbH and is not a weclapp SE product.
Before you start: wals.pro Support must enable device connections
Device authorization is off by default and is never derived from a plan or a tier. wals.pro Support enables it for each workspace.
Until then, the browser approval step explains that device connections are not enabled and asks you to contact wals.pro Support. The CLI reports expiration and timeouts as what they are; those outcomes alone do not prove a capacity problem.
Install
pipx install walspro-ai # recommended: isolated, on PATH
pip install walspro-ai # or into an existing environment
Python 3.10 or newer. The only runtime dependencies are the MCP SDK, httpx
and anyio.
Use
walspro-ai login # authorize this host (prints a URL and a user code)
walspro-ai status # non-secret local connection status
walspro-ai config # token-free MCP client configuration snippet
walspro-ai run # stdio <-> /v1/mcp transport (started by the MCP client)
walspro-ai logout # remove local credentials only
walspro-ai revoke # revoke the server grant, then remove local credentials
python -m walspro_ai <command> is equivalent and works when the console
script is not on PATH.
Add the output of walspro-ai config to your MCP client configuration. It
contains no tokens; the credentials stay in the local credential file. Use
--server-name to change the generated entry key.
Global options: --credentials PATH (alternate credential file),
--verbose (print the underlying traceback on failure), --version.
login opens a browser only when one can plausibly be reached (a BROWSER
variable, macOS/Windows, or DISPLAY/WAYLAND_DISPLAY). On a headless Linux
host it prints the URL instead of blocking on a terminal browser. Force either
behaviour with --open-browser or --no-browser.
Exit codes
| Code | Meaning |
|---|---|
0 |
Success |
1 |
Error (the message names the cause) |
2 |
Local credentials were removed, but the server grant may still be active — revoke the connection in the dashboard |
130 |
Interrupted (Ctrl-C) |
Where credentials live
| Platform | Default path |
|---|---|
| Linux / BSD | $XDG_CONFIG_HOME/walspro-ai/device.json, otherwise ~/.config/walspro-ai/device.json |
| macOS | ~/.config/walspro-ai/device.json |
| Windows | %APPDATA%\walspro-ai\device.json |
The credential file is created exclusively (O_EXCL, O_NOFOLLOW) with mode
0600, rotated by fsync + atomic replace, and serialized across processes by
an OS file lock on a sidecar. On POSIX the connector refuses to read a
credential file that is group- or world-accessible or owned by another user.
Windows, stated plainly. Windows reports 0o666 for every writable file
regardless of its ACL, so the mode assertion is skipped there, and there is no
os.getuid() to compare against, so the ownership assertion is skipped too.
This connector does not read the Windows ACL as a substitute. What remains
on Windows is the symlink/regular-file check, the exclusive create and the
atomic replace; the file's actual confidentiality is whatever the containing
directory grants. Keep the credential file inside your own user profile (the
default location does) and do not point --credentials at a shared directory.
The POSIX checks themselves are never relaxed. Windows support is implemented
and unit-tested (the platform branches are exercised directly) but continuous
integration runs on Linux only, so please report anything that behaves
differently there — support@wals.pro.
Ambient network configuration is trusted. Both HTTP stacks honour the
process environment's proxy and CA settings (HTTPS_PROXY, SSL_CERT_FILE,
and the platform trust store), which is what makes the connector work behind a
corporate proxy. Treat the environment of the process your MCP client starts as
part of the trust boundary, and review any env block you paste into a client
configuration.
status and config never print bearer or refresh tokens.
Direct local file transfers
When an existing DeviceOAuth connection is already configured, enable local files on that same connector; no second login or browser handoff is needed:
walspro-ai config --file-root /absolute/path/to/upload-files
# The emitted client entry runs:
walspro-ai run --file-root /absolute/path/to/upload-files
The root belongs to the host running the connector. A path on another computer, a browser chat attachment, or a different container is not a local source for this process. Claude Code and Codex can invoke the connector through STDIO; they must see the updated local tool catalog. This setting does not enable local files for a remote-only Claude.ai or ChatGPT connection.
- Call
create_upload_intentwith its existing target fields andfile_path, either relative to the configured root or an absolute path inside it. The local connector adds this argument totools/listand removes it before forwarding the request. Use exactly one file source. - The connector reads the file and calculates its size, MIME and SHA-256. It sends only metadata for the server's normal permission/target preview. No ERP upload occurs at this step. Review and approve the returned action.
- Call
execute_approvedwith the returned token and unchanged execution payload. The connector sends the approved file bytes directly over its existing authenticated HTTP connection. Do not put base64 or another file source in the execute arguments.
Use kind="document" plus the exact entity_name for any of the 17 supported
document owners. The local transport accepts the server-supported PDF, image,
Office, ZIP and UTF-8 text formats, nonempty and at most 20,000,000 bytes.
The legacy purchase-invoice kind stays PDF-only; article-image create/replace
stays JPEG/PNG with a 12,000,000-byte limit. Filename hints are checked against
actual bytes locally and again by the server. All uploads start inside agents;
there is no upload landing page or manual file-picker flow. The
transport never overrides permissions, approval, ownership or replace identity.
Existing small inline-file tools retain their server-side contract.
Local files require POSIX directory-descriptor and no-follow support, available
on supported macOS/Linux hosts. A platform lacking these primitives rejects
--file-root explicitly; normal DeviceOAuth/STDIO remains available. The root,
its ancestors and every file-path component must be real directories/files,
not symbolic links. Use canonical absolute paths (for example /private/tmp
instead of the macOS /tmp symlink). Parent traversal, directories, devices,
FIFOs, unsupported file types and oversized files are refused.
The connector holds the original directory and file descriptors through preview/execute: replacing a pathname cannot substitute another file. Changes to the original file contents fail the hash check before transfer. Up to 32 prepared transfers are held for ten minutes, released on the next request or session exit. File bytes are not put in JSON-RPC, persisted to a staging file, or written to logs; only the filename and approved metadata reach the model.
A binary POST is attempted once, including when an HTTP 401 is returned.
There is no automatic retry after a lost response. Within the same session a
confirmed result can be returned again without another POST; an uncertain
result requires get_upload_intent_status with the returned reference. After a
connector restart or expiry the open file binding is gone: inspect the existing
intent status before preparing anything again. This is not an instruction to
repeat a possibly completed ERP write.
The package's real STDIO/HTTP tests prove the local transport and its guards. They are not a proof of an actual Claude Code/Codex session, the hosted backend, or a weclapp upload; those client and ERP acceptance checks remain separate.
Recovering from a broken credential file
logout removes the local credential file without validating it, so a
truncated, hand-edited or wrongly-permissioned file never locks you out. It
does not end the connection on the server — the device grant stays active
and keeps occupying an enrollment slot until you revoke it or remove the
connection in the dashboard.
revoke calls the RFC 7009 revocation endpoint. If the stored file no longer
validates, it still recovers the client id, refresh token and revocation
endpoint from it and revokes with those, as long as the endpoint is the
canonical one on the origin the file itself names. The outcomes are:
| Situation | Exit | Local file |
|---|---|---|
| Grant revoked | 0 |
removed |
| File invalid, but a token was recoverable and revoked | 0 |
removed |
| File unreadable, nothing revocable | 2 |
removed — revoke in the dashboard |
| Revocation call failed (issuer down, network) | 1 |
kept — use logout to remove it |
After any of these, login works again.
Migrating from the connector that shipped inside the server
Until this release the command was weclapp-mcp-device, installed by the
proprietary weclapp-mcp distribution, and its credentials lived in
~/.config/weclapp-mcp/device.json (%APPDATA%\weclapp-mcp\device.json on
Windows). That file holds a live refresh-token family that this connector
cannot revoke, and the command that could is gone.
Before or right after switching: revoke that connection in the dashboard under
Connections, then delete the old directory. login prints a reminder when it
finds one.
Support
There is no public issue tracker. Report defects and questions to
support@wals.pro; include the connector version (walspro-ai --version) and
the exact message, never the credential file.
Security properties
- Strict origin binding: issuer, token, revocation and resource URLs must all live on the same canonical origin, on their exact canonical paths.
- The OAuth calls refuse redirects, time out, and cap responses at 64 KiB.
- Access tokens rotate with the refresh token; a
401triggers exactly one refresh and one retry. A rotation whose local write fails reports that the connection is gone rather than keeping a token the server already retired. - Tokens are never printed, logged or passed on the command line,
--verboseincluded. MCP SDK andhttpxlog records are silenced unless you ask for them, because their tracebacks quote raw upstream response bytes. runfails loudly. The transport read is bounded, and a transport failure answers every unanswered request with a JSON-RPC error naming the sanitized cause and exits non-zero — it never leaves your MCP client waiting.- One stable public client identity per local profile; it is re-registered only after the authorization server explicitly retires it.
License
Apache-2.0. See LICENSE. Copyright 2026 Wals.pro GmbH.
The complete source of this distribution — including its test suite and the
changelog — ships in the sdist (pip download --no-binary :all: walspro-ai).
The repository it is developed in is private, so the package deliberately
declares no Source or Changelog URL rather than a link that 404s.
Release note for maintainers
The PyPI distribution name was registered before any install instruction naming it reached a customer-facing surface. That ordering is deliberate and not optional: an unregistered name printed in a dashboard, a README or a doc is claimable by anyone, and the first person to claim it would be shipping the package customers install as their OAuth device connector. Register the name first, then merge the instruction.
Metadata
Release files for walspro-ai 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| walspro_ai-0.2.1.tar.gz | 71.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| walspro_ai-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 112.9 kB
Release files / walspro_ai-0.2.1.tar.gz
| Download URL | walspro_ai-0.2.1.tar.gz |
|---|---|
| Size | 71.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
45c5faa73501b46df9955bf37d63f35fddea72f529396f07c8ba29731542654f
|
|
BLAKE2b-256 checksum How to use checksums |
39672b4b3c9170d5e8558405fda0b358a83184dfeca2d0696e4052664783b9f3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency logRelease files / walspro_ai-0.2.1-py3-none-any.whl
| Download URL | walspro_ai-0.2.1-py3-none-any.whl |
|---|---|
| Size | 41.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b593af4beb2b93ff399ad84181df1f2c8a880fd3f816e5e94bbec70e2714ac9f
|
|
BLAKE2b-256 checksum How to use checksums |
9bdb8f9b80e625da16f3801a33180cb4029baadd213bd83e7efc357c78c02acc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency log