@iwas/mcp 0.0.0-stage → 1.0.0
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.
- package/README.md +134 -2
- package/dist/auth/index.d.ts +631 -0
- package/dist/auth/index.js +2451 -0
- package/dist/bin.js +1833 -0
- package/dist/catalog/index.d.ts +639 -0
- package/dist/catalog/index.js +1118 -0
- package/dist/client/index.d.ts +14 -0
- package/dist/client/index.js +18 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +3458 -0
- package/dist/registrar-CNvQygSE.d.ts +192 -0
- package/package.json +69 -3
package/README.md
CHANGED
|
@@ -1,3 +1,135 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @iwas/mcp
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Intelligent WiFi Access & Presence Service (IWAS) Model Context Protocol (MCP) workspace package.
|
|
4
|
+
|
|
5
|
+
> **Status: Standalone Distribution Package.** This package is bundled via `tsup` into a self-contained
|
|
6
|
+
> binary (`dist/bin.js`) with zero monorepo dependencies, published to the NPM Registry under
|
|
7
|
+
> `@iwas/mcp`. It exposes the `iwas` (and `iwas-mcp`) CLI supporting RFC 8628 Device Authorization,
|
|
8
|
+
> native OS Keychain storage, and tool catalog definitions.
|
|
9
|
+
|
|
10
|
+
## Subpath Exports
|
|
11
|
+
|
|
12
|
+
- `@iwas/mcp` — root entrypoint (version plus re-exports).
|
|
13
|
+
- `@iwas/mcp/catalog` — re-exports the single permission vocabulary from `@iwas/auth/permissions`
|
|
14
|
+
(FR-011). The MCP capability census is generated by the API drift gate (INV-MCP-14) and is
|
|
15
|
+
deliberately not pinned here.
|
|
16
|
+
- `@iwas/mcp/client` — programmatic MCP client interfaces.
|
|
17
|
+
- `@iwas/mcp/auth` — MCP credential vault (`CredentialVault`), OS Keychain integration (`KeychainService`), profile normalization, and token redaction helpers (DEV-08).
|
|
18
|
+
|
|
19
|
+
## Credential Vault & Storage Security
|
|
20
|
+
|
|
21
|
+
`@iwas/mcp/auth` provides secure credential storage for RFC 8628 Device Flow:
|
|
22
|
+
|
|
23
|
+
- **Keychain Backend (Default on macOS & Linux):**
|
|
24
|
+
- **Service:** `iwas-cli` (default)
|
|
25
|
+
- **Account:** Profile key in canonical format `<origin>::<clientId>` (e.g. `https://getiwas.com::iwas-cli`).
|
|
26
|
+
- macOS: Uses `/usr/bin/security` (`add-generic-password -U`, `find-generic-password -w`, `delete-generic-password`).
|
|
27
|
+
- Linux: Uses `secret-tool` (`store`, `lookup`, `clear`) with secrets passed strictly via `stdin` (never exposed in `argv` or `ps`).
|
|
28
|
+
- **Manual Verification on macOS:**
|
|
29
|
+
```bash
|
|
30
|
+
# Check stored secret in macOS Keychain:
|
|
31
|
+
security find-generic-password -s iwas-cli -a "https://getiwas.com::iwas-cli" -w
|
|
32
|
+
```
|
|
33
|
+
- **File Fallback (`~/.config/iwas/credentials.json`):**
|
|
34
|
+
- Active in headless CI, Docker containers, and on Windows (`%USERPROFILE%\.config\iwas\credentials.json`).
|
|
35
|
+
- Respects `$XDG_CONFIG_HOME/iwas/credentials.json`.
|
|
36
|
+
- Enforces strict POSIX permissions: directory `0700` (`rwx------`), file `0600` (`rw-------`).
|
|
37
|
+
- Atomic write strategy: writes to temporary file, syncs to disk (`fsync`), and renames atomically.
|
|
38
|
+
- Protects corrupt files: if the JSON is malformed or missing fields, the vault fails closed and refuses to overwrite.
|
|
39
|
+
- **Security Considerations & Trade-offs:**
|
|
40
|
+
> [!WARNING]
|
|
41
|
+
> The file fallback stores serialized credentials in plaintext JSON protected only by OS file permissions (`0600`). On systems where native OS keychain utilities are available, the keychain backend is strongly preferred.
|
|
42
|
+
> On macOS workstations, `/usr/bin/security` accepts `-w <secret>` via arguments which is transiently visible in the process table `ps` to local processes running under the same user UID.
|
|
43
|
+
- **Conflict Resolution & Precedence:**
|
|
44
|
+
- Credentials include an internal `updatedAt` timestamp (`Date.now()`). When operating in `auto` mode with both keychain and file present, the vault compares timestamps (`updatedAt ?? expiresAt`) and automatically migrates newer file credentials into the keychain while purging stale copies (resolving split-backend desync).
|
|
45
|
+
- **Environment Override:**
|
|
46
|
+
- Set `IWAS_MCP_VAULT=auto` (default: keychain if available, fallback to file).
|
|
47
|
+
- Set `IWAS_MCP_VAULT=keychain` (strict keychain; throws if unavailable or locked).
|
|
48
|
+
- Set `IWAS_MCP_VAULT=file` (forces file storage; useful for headless CI).
|
|
49
|
+
- Set `IWAS_MCP_REAL_KEYCHAIN=1` (opt-in to execute real host macOS Keychain tests).
|
|
50
|
+
|
|
51
|
+
## Local CLI
|
|
52
|
+
|
|
53
|
+
Executable binary is `iwas` (with `iwas-mcp` provided as a backward-compatible alias):
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# Display help & version
|
|
57
|
+
iwas --help
|
|
58
|
+
iwas --version
|
|
59
|
+
|
|
60
|
+
# Log in using OAuth 2.0 Device Authorization Grant (RFC 8628)
|
|
61
|
+
iwas login
|
|
62
|
+
iwas login --server https://getiwas.com --no-browser
|
|
63
|
+
iwas login --scopes "analytics:read session:read offline_access" --json
|
|
64
|
+
|
|
65
|
+
# Inspect active authentication status for a server profile
|
|
66
|
+
iwas status
|
|
67
|
+
iwas status --server https://getiwas.com --json
|
|
68
|
+
|
|
69
|
+
# Log out and remove credentials from vault & keychain
|
|
70
|
+
iwas logout
|
|
71
|
+
iwas logout --server https://getiwas.com --json
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Auth server origin can also be set via the `IWAS_SERVER_URL` environment variable.
|
|
75
|
+
|
|
76
|
+
### OAuth Scopes & Least-Privilege Defaults
|
|
77
|
+
|
|
78
|
+
Default scopes requested during `iwas login` follow **strict least-privilege**: only read-only capabilities (`*:read`) plus `offline_access` are requested. Mutating or administrative actions (such as `:manage`, `:delete`, `:control`, `:issue`, `:write`) require explicitly passing the required permissions via `--scopes`.
|
|
79
|
+
|
|
80
|
+
> [!NOTE]
|
|
81
|
+
> Platform-only read scopes (such as `diagnostics:read`) are included in `DEFAULT_READ_ONLY_SCOPES` for platform administrators, while tenant callers have access strictly evaluated and gated by the server's RBAC engine at execution time.
|
|
82
|
+
|
|
83
|
+
### Machine-Readable JSON Mode (`--json`)
|
|
84
|
+
|
|
85
|
+
When `--json` is passed, machine consumers receive newline-delimited JSON events on `stdout`:
|
|
86
|
+
|
|
87
|
+
1. **Authorization initiated (`device_authorization` event):** Emitted immediately after obtaining the device code, providing headless runners and IDE extensions with the verification details without waiting for token approval:
|
|
88
|
+
```json
|
|
89
|
+
{"event":"device_authorization","user_code":"WDJB-MJHT","verification_uri":"https://getiwas.com/device","verification_uri_complete":"https://getiwas.com/device?user_code=WDJB-MJHT","expires_in":600,"interval":5}
|
|
90
|
+
```
|
|
91
|
+
*Security:* per RFC 8628 §3.2, `device_code` is strictly kept internal to the CLI and never included in this payload.
|
|
92
|
+
|
|
93
|
+
2. **Authorization completed (`login_success` event):**
|
|
94
|
+
```json
|
|
95
|
+
{"event":"login_success","success":true,"profileKey":"https://getiwas.com::iwas-cli","serverUrl":"https://getiwas.com","clientId":"iwas-cli","organizationId":"org_123","scope":"...","resource":"...","expiresAt":1792376265000}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
3. **Error event:**
|
|
99
|
+
```json
|
|
100
|
+
{"event":"error","error":"Invalid OAuth client: client not found"}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
*Stream Isolation:* In `--json` mode, `stdout` is strictly reserved for structured JSON events. Human-readable interactive prompts, code highlights, and browser warnings are routed to `stderr`.
|
|
104
|
+
|
|
105
|
+
## Automatic Token Refresh & Concurrency Control (DEV-10)
|
|
106
|
+
|
|
107
|
+
`@iwas/mcp` provides automatic, transparent token refresh and concurrency-safe credential management:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { CredentialVault, TokenRefreshManager } from '@iwas/mcp/auth';
|
|
111
|
+
|
|
112
|
+
const vault = new CredentialVault();
|
|
113
|
+
const refreshManager = new TokenRefreshManager({ vault });
|
|
114
|
+
|
|
115
|
+
// Automatically returns a valid access token, refreshing if within safety margin (60s)
|
|
116
|
+
const accessToken = await refreshManager.getValidAccessToken('https://getiwas.com::iwas-cli');
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Concurrency Guarantees
|
|
120
|
+
- **In-process Single-flight:** Multiple concurrent asynchronous calls (both automatic and explicit force `refresh()`) for the same profile collapse to exactly one HTTP refresh request.
|
|
121
|
+
- **Cross-process Mutual Exclusion (`VaultLock`):** Protects `<config-dir>/iwas/credentials.lock` via atomic `wx` creation (mode `0600`), dedicated app directory `0700` verification, and default 60s bounded timeout.
|
|
122
|
+
- **Double-Checked Locking:** After acquiring the cross-process lock, the vault is re-read. If another concurrent process has already rotated the token family, the refreshed token is used immediately without issuing a redundant request.
|
|
123
|
+
- **Active Process Immunity & Dead PID Reclaim (M1):** On the same host, a lock owned by a running process (`process.kill(pid, 0)`) is **never** reclaimed or stolen regardless of age, preventing token family destruction during slow network operations. Stale lock recovery occurs only when the holding PID is confirmed dead. *Cross-host/network FS tradeoff:* Across different machines or on shared network mounts (e.g. NFS/SMB), remote PIDs cannot be probed, so locks older than `staleTimeoutMs = 60s` are reclaimed based on lease age; configure a higher `staleTimeoutMs` for multi-host setups with operations exceeding 60s.
|
|
124
|
+
- **TOCTOU-Safe Reclamation & Inode Verification:** Stale locks are verified via hardlink (`linkSync`) + `nlink` checks and inode matching before unlinking, ensuring racing processes acquiring new locks are never unlinked or overwritten. If the filesystem does not support hardlinks, reclamation fails closed (the caller times out) rather than risk deleting a live lock; remove the stale lock manually in that environment.
|
|
125
|
+
- **Strictly Scoped Terminal Errors (M2):** ONLY `invalid_grant` (revoked/expired refresh token) and `invalid_client` purge local vault credentials and raise `ReauthenticationRequiredError`. Non-terminal 4xx errors (e.g. `invalid_request`, `invalid_scope`) throw sanitized errors while safely preserving credentials in the vault.
|
|
126
|
+
- **Transient Retries & 30s Reuse Grace Window:** Network errors and 5xx server errors retry with bounded exponential backoff and never wipe stored credentials. Single retry on POST is protected by Better Auth's 30s refresh token reuse grace window (`MCP-OAUTH-04`).
|
|
127
|
+
- **Atomic Persistence Failure Handling:** If saving rotated credentials to the vault fails, the profile is purged and raises `ReauthenticationRequiredError` ('persistence_failure') to avoid repeated replay of stale refresh tokens. Secret tokens are strictly redacted from diagnostic logs and error messages.
|
|
128
|
+
|
|
129
|
+
## Development
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
pnpm --filter @iwas/mcp build # emits dist/*.js and dist/*.d.ts
|
|
133
|
+
pnpm --filter @iwas/mcp test # compiles, then runs test suite
|
|
134
|
+
pnpm --filter @iwas/mcp exec tsc --noEmit
|
|
135
|
+
```
|