feat(setup): persist PowerContext Agent authorization - #1548
Conversation
| authorization_state = configure_stored_authorization( | ||
| "codex", | ||
| server_url=DEFAULT_CLAUDE_CODE_SERVER_URL, | ||
| value=os.environ.get("POWERCONTEXT_CLIENT_API_TOKEN"), |
There was a problem hiding this comment.
[P1] Wire saved credentials into native MCP
Saving this file only updates the hook credential source. Codex's .mcp.json still reads Authorization from the environment; Claude Code and WorkBuddy have the same gap. With a matching saved credential and no auth environment variable, the native Codex CLI sent /mcp without Authorization. Adding the environment override completed initialize and tools/list. Please connect native MCP to the same URL-bound credential source so setup actually removes the need for per-session exports.
| if path.is_symlink() or not path.is_file() or stat.S_IMODE(path.stat().st_mode) & 0o077: | ||
| return None | ||
| payload = json.loads(path.read_text(encoding="utf-8")) | ||
| if payload.get("version") != 1 or _http_base_url(payload["server_url"]) != _http_base_url(server_url): |
There was a problem hiding this comment.
[P1] Validate saved base URLs without requiring /mcp
_http_base_url() requires its input to end in /mcp, but both the saved URL and the server_url argument are already base URLs. A normal setup therefore raises ValueError here and silently discards the saved credential. I reproduced setup returning configured while CodexPluginSettings.authorization remained None. Please use base-URL normalization for this comparison and cover the setup-to-load path.
| data_dir=str(data_dir), | ||
| authorization_state=configure_stored_authorization( | ||
| "workbuddy", | ||
| server_url="http://127.0.0.1:8000", |
There was a problem hiding this comment.
[P2] Bind the credential to the configured server
This hardcodes port 8000 even when the host uses another endpoint. Running the WorkBuddy installer with POWERCONTEXT_WORKBUDDY_SERVER_URL=http://127.0.0.1:18765 still returned configured and saved port 8000; the hook then rejected the credential because the URLs differed. DSH, OpenCode, and Pi also hardcode this value. Please resolve the effective host endpoint before persisting the credential.
| def _stored_authorization(*, server_url: str, root: Path) -> str | None: | ||
| path = root / "powercontext" / "credentials.json" | ||
| try: | ||
| if path.is_symlink() or not path.is_file() or stat.S_IMODE(path.stat().st_mode) & 0o077: |
There was a problem hiding this comment.
[P2] Restrict the POSIX permission check to POSIX
All six plugin loaders apply mode & 0o077 unconditionally, although the shared reader explicitly excludes Windows. Windows emulates these mode bits without separate owner/group/other permissions, so this rejects normally saved credentials there. Please use platform-appropriate permission handling. This finding is based on static analysis, not a Windows run; see the Python and Node documentation.
| stored_url = normalize_server_url(payload["server_url"]) | ||
| authorization = normalize_authorization(payload["authorization"]) | ||
| effective_url = normalize_server_url(server_url) | ||
| except (OSError, TypeError, ValueError, json.JSONDecodeError): |
There was a problem hiding this comment.
[P2] Handle malformed credential records consistently
A file containing {"version":1} raises an uncaught KeyError when the required fields are read, so rerunning setup fails instead of reporting invalid. Separately, a file containing [] raises AttributeError in all three Python plugin loaders at payload.get(). Both cases were reproduced. Please validate the object and field types and return the documented invalid/no-credential result for malformed records.
| return AuthorizationResolution("configured", authorization) | ||
|
|
||
|
|
||
| def clear_stored_authorization(path: Path) -> Literal["cleared", "not_configured"]: |
There was a problem hiding this comment.
[P2] Expose credential clearing through setup
This helper has no production callers, and the setup commands do not expose --clear-authorization. Running setup workbuddy --clear-authorization --json returns No such option; rerunning without a token preserves the saved credential. Please wire the explicit clear option into the supported setup commands, as required by #1538, while preserving unrelated host configuration.
4f021ce to
66ce067
Compare
fd5eb3d to
17a4916
Compare
Closes #1538.
Summary
Validation
This PR supersedes the closed draft PR #1539 and contains implementation, tests, and user-facing documentation only.
AI usage
OpenAI Codex was used to inspect the repository, implement the change, and run validation. The human selected the host-owned credential persistence policy.