agent-portal-2 0.1.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.
Files changed (120) hide show
  1. package/.continue/agents/new-config.yaml +22 -0
  2. package/AGENT_STEERING.md +36 -0
  3. package/ARCHITECTURE.md +13 -0
  4. package/CHANGELOG.md +97 -0
  5. package/CLI.md +38 -0
  6. package/CONTRIBUTING.md +55 -0
  7. package/INSTALLATION.md +58 -0
  8. package/LICENSE +60 -0
  9. package/PLUGIN_SYSTEM.md +33 -0
  10. package/PYTHON_SDK.md +22 -0
  11. package/QUICKSTART.md +19 -0
  12. package/README.md +385 -0
  13. package/RELEASE_NOTES_v0.1.0.md +281 -0
  14. package/ROADMAP.md +3 -0
  15. package/RUNTIME.md +44 -0
  16. package/SAFETY_MODEL.md +24 -0
  17. package/TESTING.md +35 -0
  18. package/TROUBLESHOOTING.md +30 -0
  19. package/UPGRADE_GUIDE.md +288 -0
  20. package/VS_CODE_EXTENSION.md +47 -0
  21. package/agent-portal.config.json +20 -0
  22. package/apps/desktop/agent-portal-desktop.zip +0 -0
  23. package/apps/desktop/fixtures/local-workflow.html +151 -0
  24. package/apps/desktop/package.json +18 -0
  25. package/apps/desktop/src/main.ts +117 -0
  26. package/apps/desktop/tsconfig.json +8 -0
  27. package/apps/vscode-extension/LICENSE +60 -0
  28. package/apps/vscode-extension/README.md +20 -0
  29. package/apps/vscode-extension/media/agent-portal-logo.png +0 -0
  30. package/apps/vscode-extension/package.json +149 -0
  31. package/apps/vscode-extension/src/extension.ts +614 -0
  32. package/apps/vscode-extension/tsconfig.json +12 -0
  33. package/assets/branding/agent-portal-logo.png +0 -0
  34. package/connectors/chatgpt-tools/README.md +9 -0
  35. package/connectors/claude-mcp-server/README.md +9 -0
  36. package/connectors/gemini-connector/README.md +9 -0
  37. package/connectors/rest-websocket-api/README.md +9 -0
  38. package/docs/MCP_SERVER.md +68 -0
  39. package/docs/architecture.md +214 -0
  40. package/docs/roadmap.md +125 -0
  41. package/package.json +21 -0
  42. package/packages/agent-portal-mcp/README.md +12 -0
  43. package/packages/agent-portal-mcp/agent_portal_mcp/__init__.py +3 -0
  44. package/packages/agent-portal-mcp/agent_portal_mcp/bridge/__init__.py +1 -0
  45. package/packages/agent-portal-mcp/agent_portal_mcp/bridge/runtime_client.py +180 -0
  46. package/packages/agent-portal-mcp/agent_portal_mcp/cli.py +32 -0
  47. package/packages/agent-portal-mcp/agent_portal_mcp/doctor.py +71 -0
  48. package/packages/agent-portal-mcp/agent_portal_mcp/schemas/__init__.py +1 -0
  49. package/packages/agent-portal-mcp/agent_portal_mcp/schemas/actions.py +17 -0
  50. package/packages/agent-portal-mcp/agent_portal_mcp/schemas/results.py +24 -0
  51. package/packages/agent-portal-mcp/agent_portal_mcp/schemas/risk.py +20 -0
  52. package/packages/agent-portal-mcp/agent_portal_mcp/security/__init__.py +1 -0
  53. package/packages/agent-portal-mcp/agent_portal_mcp/security/policy.py +27 -0
  54. package/packages/agent-portal-mcp/agent_portal_mcp/server.py +148 -0
  55. package/packages/agent-portal-mcp/agent_portal_mcp/tool_registry.py +58 -0
  56. package/packages/agent-portal-mcp/agent_portal_mcp/tools/__init__.py +1 -0
  57. package/packages/agent-portal-mcp/agent_portal_mcp/tools/browser.py +89 -0
  58. package/packages/agent-portal-mcp/agent_portal_mcp/tools/common.py +98 -0
  59. package/packages/agent-portal-mcp/agent_portal_mcp/tools/inspection.py +93 -0
  60. package/packages/agent-portal-mcp/agent_portal_mcp/tools/navigation.py +93 -0
  61. package/packages/agent-portal-mcp/agent_portal_mcp/tools/reports.py +34 -0
  62. package/packages/agent-portal-mcp/agent_portal_mcp/tools/steering.py +93 -0
  63. package/packages/agent-portal-mcp/pyproject.toml +20 -0
  64. package/packages/agent-portal-mcp/tests/test_doctor.py +20 -0
  65. package/packages/agent-portal-mcp/tests/test_mcp_server.py +161 -0
  66. package/packages/core/package.json +15 -0
  67. package/packages/core/src/index.ts +1842 -0
  68. package/packages/core/tsconfig.json +8 -0
  69. package/packages/mcp-server/package.json +15 -0
  70. package/packages/mcp-server/src/index.ts +73 -0
  71. package/packages/mcp-server/tsconfig.json +8 -0
  72. package/packages/sdk/package.json +15 -0
  73. package/packages/sdk/src/index.ts +544 -0
  74. package/packages/sdk/tsconfig.json +8 -0
  75. package/plugins/README.md +16 -0
  76. package/plugins/agent-portal-browser/plugin.json +19 -0
  77. package/plugins/agent-portal-python/plugin.json +16 -0
  78. package/plugins/agent-portal-skills/plugin.json +19 -0
  79. package/plugins/agent-portal-vscode/plugin.json +27 -0
  80. package/plugins/example-runtime-plugin/README.md +3 -0
  81. package/plugins/example-runtime-plugin/plugin.json +20 -0
  82. package/plugins/plugin.schema.json +53 -0
  83. package/python/README.md +18 -0
  84. package/python/agent_portal/__init__.py +5 -0
  85. package/python/agent_portal/__main__.py +5 -0
  86. package/python/agent_portal/browser.py +393 -0
  87. package/python/agent_portal/cli.py +164 -0
  88. package/python/agent_portal/config.py +31 -0
  89. package/python/agent_portal/doctor.py +165 -0
  90. package/python/agent_portal/exceptions.py +39 -0
  91. package/python/agent_portal/logging_utils.py +33 -0
  92. package/python/agent_portal/metrics.py +309 -0
  93. package/python/agent_portal/models.py +160 -0
  94. package/python/agent_portal/plugin_system.py +42 -0
  95. package/python/agent_portal/rate_limit.py +253 -0
  96. package/python/agent_portal/runtime.py +739 -0
  97. package/python/agent_portal/server.py +351 -0
  98. package/python/agent_portal/validation.py +299 -0
  99. package/python/pyproject.toml +29 -0
  100. package/python/tests/test_config.py +24 -0
  101. package/python/tests/test_doctor.py +19 -0
  102. package/python/tests/test_metrics.py +180 -0
  103. package/python/tests/test_rate_limit.py +237 -0
  104. package/python/tests/test_runtime.py +122 -0
  105. package/python/tests/test_server.py +53 -0
  106. package/python/tests/test_validation.py +170 -0
  107. package/releases/desktop/agent-portal-desktop/README.md +378 -0
  108. package/releases/desktop/agent-portal-desktop/RELEASE_NOTES.md +14 -0
  109. package/releases/desktop/agent-portal-desktop/assets/branding/agent-portal-logo.png +0 -0
  110. package/releases/desktop/agent-portal-desktop/fixtures/local-workflow.html +151 -0
  111. package/releases/desktop/agent-portal-desktop/launch-agent-portal.bat +4 -0
  112. package/releases/desktop/agent-portal-desktop.zip +0 -0
  113. package/releases/python/agent_portal-0.0.2-py3-none-any.whl +0 -0
  114. package/releases/python/agent_portal-0.0.2.tar.gz +0 -0
  115. package/scripts/package_desktop.mjs +117 -0
  116. package/scripts/release_python.py +46 -0
  117. package/tests/plugin-manifest.test.mjs +26 -0
  118. package/tests/runtime.test.mjs +41 -0
  119. package/tests/vscode-extension.test.mjs +22 -0
  120. package/tsconfig.base.json +16 -0
@@ -0,0 +1,68 @@
1
+ # MCP Server
2
+
3
+ ## What MCP Is
4
+
5
+ MCP, or Model Context Protocol, is a structured way for AI clients to discover and call tools through a standard interface.
6
+
7
+ ## Why Agent Portal Uses MCP
8
+
9
+ Agent Portal already has a local runtime, policy engine, browser control layer, reporting, and steering controls. MCP turns those capabilities into a consistent tool surface that other AI clients can connect to without custom one-off integrations.
10
+
11
+ ## Start The Runtime
12
+
13
+ ```bash
14
+ agent-portal start
15
+ ```
16
+
17
+ ## Start The MCP Server
18
+
19
+ ```bash
20
+ agent-portal mcp start
21
+ ```
22
+
23
+ or:
24
+
25
+ ```bash
26
+ agent-portal-mcp start
27
+ ```
28
+
29
+ ## Available Tool Groups
30
+
31
+ - Browser
32
+ - Navigation
33
+ - Inspection
34
+ - Agent Steering
35
+ - Reports
36
+
37
+ ## Security Model
38
+
39
+ - localhost-first runtime URL by default
40
+ - optional bearer token through `AGENT_PORTAL_TOKEN` or `agent-portal.config.json`
41
+ - risky actions are classified before execution
42
+ - blocked actions are never auto-executed
43
+ - approval-required actions stay in the queue until approved
44
+ - secret tokens are redacted from MCP errors
45
+
46
+ ## Approval Flow
47
+
48
+ 1. A risky MCP tool proposes an action through the Agent Portal runtime.
49
+ 2. The runtime applies its policy engine and assigns a risk level.
50
+ 3. Safe actions can auto-execute.
51
+ 4. Higher-risk actions become `pending_approval`.
52
+ 5. `approve_action` can approve and execute a queued action.
53
+ 6. `reject_action` rejects the queued action.
54
+
55
+ ## Example Tool Calls
56
+
57
+ - `browser_open`
58
+ - `navigate_to_url`
59
+ - `click_element`
60
+ - `capture_screenshot`
61
+ - `get_action_queue`
62
+ - `generate_report`
63
+
64
+ ## Troubleshooting
65
+
66
+ - If the runtime is offline, start it with `agent-portal start`.
67
+ - If token auth is enabled, set `AGENT_PORTAL_TOKEN`.
68
+ - If tools are not visible, run `agent-portal mcp doctor`.
@@ -0,0 +1,214 @@
1
+ # Agent Portal Architecture
2
+
3
+ ## Product Shape
4
+
5
+ Agent Portal is best treated as a platform, not a single app. The architecture needs to support:
6
+
7
+ - local-first execution
8
+ - visual awareness
9
+ - controlled actions
10
+ - multi-agent coordination
11
+ - replayable sessions
12
+ - durable memory
13
+ - explicit permissions
14
+
15
+ ## Core Layers
16
+
17
+ ### 1. Desktop Shell
18
+
19
+ Responsibilities:
20
+
21
+ - native window management
22
+ - embedded browser surfaces
23
+ - agent console UI
24
+ - session inspector
25
+ - settings and permissions
26
+
27
+ Recommended implementation path:
28
+
29
+ - start with Electron for speed and ecosystem reach
30
+ - evaluate Tauri later if footprint becomes a major constraint
31
+
32
+ ### 2. Agent Runtime
33
+
34
+ Responsibilities:
35
+
36
+ - spawn and isolate agents
37
+ - manage task state
38
+ - coordinate multi-agent execution
39
+ - stream events to the UI
40
+ - attach tools and permissions
41
+
42
+ Key concepts:
43
+
44
+ - agent definitions
45
+ - task queue
46
+ - execution policies
47
+ - event bus
48
+ - cancellation and recovery
49
+
50
+ ### 3. Vision Pipeline
51
+
52
+ Responsibilities:
53
+
54
+ - capture screenshots
55
+ - read DOM structure
56
+ - parse accessibility trees
57
+ - run OCR
58
+ - detect actionable UI elements
59
+ - emit state diffs over time
60
+
61
+ Output contract:
62
+
63
+ - normalized `VisualSnapshot`
64
+ - element list with labels, selectors, roles, and bounds
65
+ - changed/appeared/disappeared annotations
66
+
67
+ ### 3.5 Vision Core
68
+
69
+ Responsibilities:
70
+
71
+ - fuse screenshots, DOM, accessibility, console, and network evidence
72
+ - classify the current interface
73
+ - infer likely user intent
74
+ - generate root-cause hypotheses when flows fail
75
+ - hand structured context to goal planning and memory
76
+
77
+ Initial implementation path:
78
+
79
+ - heuristics and typed contracts first
80
+ - model-assisted reasoning layer second
81
+ - confidence scoring and self-healing action selection third
82
+
83
+ ### 4. Browser Control Layer
84
+
85
+ Responsibilities:
86
+
87
+ - navigation
88
+ - clicking
89
+ - typing
90
+ - waiting
91
+ - file upload and download
92
+ - script execution
93
+ - trace capture
94
+
95
+ Recommended implementation path:
96
+
97
+ - Playwright as the first execution backend
98
+ - adapter abstraction so Selenium or remote browsers can be added later
99
+
100
+ ### 5. Desktop Control Layer
101
+
102
+ Responsibilities:
103
+
104
+ - window discovery
105
+ - input simulation
106
+ - native app inspection
107
+ - application-specific adapters
108
+
109
+ Recommended implementation path:
110
+
111
+ - define the abstraction now
112
+ - ship browser-first before broad desktop automation
113
+ - add Windows-first adapters for VS Code, Terminal, and File Explorer
114
+
115
+ ### 6. Memory Engine
116
+
117
+ Responsibilities:
118
+
119
+ - persist project context
120
+ - store recent findings
121
+ - keep reusable workflows
122
+ - retain user and team preferences
123
+
124
+ Storage model:
125
+
126
+ - workspace metadata
127
+ - task history
128
+ - vector or semantic memory later
129
+ - durable file-backed logs immediately
130
+
131
+ ### 6.5 Portal Graph
132
+
133
+ Responsibilities:
134
+
135
+ - map pages, links, and recurring flows
136
+ - keep a remembered topology of the app
137
+ - support self-healing navigation when selectors change
138
+ - provide reusable structure for future agents
139
+
140
+ ### 7. Session Recorder
141
+
142
+ Responsibilities:
143
+
144
+ - append action timeline
145
+ - attach screenshots and console logs
146
+ - reconstruct replay views
147
+ - generate bug reports
148
+
149
+ Initial storage approach:
150
+
151
+ - append-only event log
152
+ - screenshot references
153
+ - per-session manifest JSON
154
+
155
+ ### 8. Security Manager
156
+
157
+ Responsibilities:
158
+
159
+ - tool permissions
160
+ - file and network boundaries
161
+ - secrets handling
162
+ - action approvals
163
+ - auditability
164
+
165
+ This is a product boundary, not a bolt-on feature.
166
+
167
+ ## Monorepo Strategy
168
+
169
+ `packages/core`
170
+
171
+ - shared contracts
172
+ - runtime state shapes
173
+ - orchestration primitives
174
+
175
+ `packages/sdk`
176
+
177
+ - external developer API
178
+ - stable ergonomic wrapper over runtime actions
179
+
180
+ `packages/mcp-server`
181
+
182
+ - tool-facing surface for agent frameworks
183
+ - command validation
184
+ - capability exposure
185
+
186
+ `apps/desktop`
187
+
188
+ - first-party desktop operator experience
189
+ - live state inspection
190
+ - future visual workspace UI
191
+
192
+ ## Suggested First Vertical Slice
193
+
194
+ Build the smallest end-to-end system that proves the concept:
195
+
196
+ 1. Launch desktop shell
197
+ 2. Open browser page
198
+ 3. Capture screenshot and DOM
199
+ 4. Detect clickable elements
200
+ 5. Execute one click or type action
201
+ 6. Record the session timeline
202
+ 7. Generate a simple report
203
+
204
+ If this loop feels great, the rest of the platform has a solid foundation.
205
+
206
+ ## Phase 3 Direction
207
+
208
+ The next platform shift is from action execution to understanding:
209
+
210
+ 1. Capture multimodal evidence
211
+ 2. Classify the interface and workflow
212
+ 3. Generate a goal plan from intent
213
+ 4. Write understanding into memory
214
+ 5. Reuse graph and memory across future sessions
@@ -0,0 +1,125 @@
1
+ # Agent Portal Roadmap
2
+
3
+ ## Phase 1: Foundation
4
+
5
+ Goal:
6
+ Ship a browser-first local runtime that can observe, act, and report.
7
+
8
+ Deliverables:
9
+
10
+ - desktop shell scaffold
11
+ - agent runtime contracts
12
+ - browser adapter abstraction
13
+ - Playwright integration
14
+ - session event log
15
+ - screenshot capture
16
+ - simple report generation
17
+
18
+ Success criteria:
19
+
20
+ - an agent can open a local app, inspect it, perform a small workflow, and produce a replayable action history
21
+
22
+ ## Phase 2: Visual Intelligence
23
+
24
+ Goal:
25
+ Make the agent reliably understand what it sees.
26
+
27
+ Deliverables:
28
+
29
+ - DOM and accessibility tree fusion
30
+ - OCR pipeline
31
+ - UI element classification
32
+ - visual diffing
33
+ - confidence scoring for actions
34
+
35
+ Success criteria:
36
+
37
+ - the agent can explain what changed on screen and choose the right target more consistently than raw selectors alone
38
+
39
+ ## Phase 3: Agent Intelligence Layer
40
+
41
+ Codename:
42
+ Project Aperture
43
+
44
+ Goal:
45
+ Move from browser control to interface understanding, goal planning, and durable project memory.
46
+
47
+ Deliverables:
48
+
49
+ - `VisionCore` for unified page understanding
50
+ - `GoalPlanner` for turning intent into execution steps
51
+ - `PortalGraph` for remembered application structure
52
+ - memory records for previous findings and plans
53
+ - project awareness detection for frameworks and services
54
+ - root-cause hypothesis generation from console and network evidence
55
+
56
+ Success criteria:
57
+
58
+ - the agent can explain what page it is on, what the user is trying to do, what likely failed, and what to do next
59
+
60
+ ## Phase 4: Multi-Agent Collaboration
61
+
62
+ Goal:
63
+ Allow specialized agents to work together inside one project workspace.
64
+
65
+ Deliverables:
66
+
67
+ - shared task board
68
+ - agent-to-agent event bus
69
+ - role-specific permission sets
70
+ - shared memory context
71
+ - result handoff model
72
+
73
+ Success criteria:
74
+
75
+ - a QA agent can find an issue, a frontend agent can fix it, and a reporter agent can summarize the result in one session
76
+
77
+ ## Phase 5: Desktop Control
78
+
79
+ Goal:
80
+ Expand beyond browser-only automation.
81
+
82
+ Deliverables:
83
+
84
+ - window discovery
85
+ - native input dispatch
86
+ - app adapters for VS Code, Terminal, and File Explorer
87
+ - screenshot plus accessibility inspection for desktop apps
88
+
89
+ Success criteria:
90
+
91
+ - agents can complete a mixed workflow spanning editor, terminal, browser, and local files
92
+
93
+ ## Phase 6: Platform Surface
94
+
95
+ Goal:
96
+ Turn Agent Portal into an ecosystem others can build on.
97
+
98
+ Deliverables:
99
+
100
+ - public SDKs
101
+ - MCP server
102
+ - CLI tools
103
+ - plugin marketplace
104
+ - API and WebSocket surfaces
105
+
106
+ Success criteria:
107
+
108
+ - third-party developers can create agents, plugins, and workflows without modifying the core app
109
+
110
+ ## Phase 7: Enterprise and Reality Mode
111
+
112
+ Goal:
113
+ Differentiate with trust, resilience, and adversarial testing.
114
+
115
+ Deliverables:
116
+
117
+ - shared team workspaces
118
+ - permissions and audit logs
119
+ - SSO and deployment modes
120
+ - reality mode stress behaviors
121
+ - policy-driven autonomous QA
122
+
123
+ Success criteria:
124
+
125
+ - teams can safely run persistent agents against real products with clear oversight and reproducible evidence
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "agent-portal-2",
3
+ "version": "0.1.0",
4
+ "private": false,
5
+ "description": "Desktop-native operating environment for AI agents.",
6
+ "workspaces": [
7
+ "apps/*",
8
+ "packages/*"
9
+ ],
10
+ "scripts": {
11
+ "build": "npm run build --workspace @agent-portal/core && npm run build --workspace @agent-portal/sdk && npm run build --workspace @agent-portal/mcp-server && npm run build --workspace @agent-portal/desktop && npm run build --workspace apps/vscode-extension",
12
+ "dev": "npm run dev --workspace @agent-portal/desktop",
13
+ "check": "npm run build --workspace @agent-portal/core && npm run typecheck --workspace @agent-portal/core && npm run typecheck --workspace @agent-portal/sdk && npm run typecheck --workspace @agent-portal/mcp-server && npm run typecheck --workspace @agent-portal/desktop && npm run typecheck --workspace apps/vscode-extension",
14
+ "test": "python -m unittest discover -s python/tests -v && python -m unittest discover -s packages/agent-portal-mcp/tests -v && npm run build && node --test tests/*.test.mjs",
15
+ "release:python": "python scripts/release_python.py",
16
+ "release:desktop": "node scripts/package_desktop.mjs"
17
+ },
18
+ "devDependencies": {
19
+ "typescript": "^5.9.2"
20
+ }
21
+ }
@@ -0,0 +1,12 @@
1
+ # Agent Portal MCP Server
2
+
3
+ This package exposes Agent Portal through a local MCP-compatible stdio server.
4
+
5
+ It connects to the local Agent Portal runtime and turns browser, inspection,
6
+ steering, and report operations into structured MCP tools.
7
+
8
+ Start it with:
9
+
10
+ ```bash
11
+ agent-portal-mcp start
12
+ ```
@@ -0,0 +1,3 @@
1
+ from .server import AgentPortalMcpServer
2
+
3
+ __all__ = ["AgentPortalMcpServer"]
@@ -0,0 +1,180 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import time
6
+ from pathlib import Path
7
+ from typing import Any
8
+ from urllib.error import HTTPError, URLError
9
+ from urllib.request import Request, urlopen
10
+
11
+
12
+ class RuntimeClientError(RuntimeError):
13
+ pass
14
+
15
+
16
+ class AgentPortalRuntimeClient:
17
+ def __init__(
18
+ self,
19
+ runtime_url: str = "http://127.0.0.1:8765",
20
+ token: str | None = None,
21
+ retries: int = 2,
22
+ timeout_seconds: float = 4.0,
23
+ ) -> None:
24
+ self.runtime_url = runtime_url.rstrip("/")
25
+ self.token = token or self._load_token()
26
+ self.retries = retries
27
+ self.timeout_seconds = timeout_seconds
28
+
29
+ def health(self) -> dict[str, Any]:
30
+ return self.get("/health")
31
+
32
+ def status(self) -> dict[str, Any]:
33
+ return self.get("/status")
34
+
35
+ def propose_action(
36
+ self,
37
+ action_type: str,
38
+ reason: str,
39
+ target: str | None = None,
40
+ payload: str | None = None,
41
+ risk_level: str = "low",
42
+ ) -> dict[str, Any]:
43
+ return self.post(
44
+ "/control/propose-action",
45
+ {
46
+ "actionType": action_type,
47
+ "reason": reason,
48
+ "target": target,
49
+ "payload": payload,
50
+ "riskLevel": risk_level,
51
+ },
52
+ )
53
+
54
+ def approve_action(self, action_id: str, execute: bool = False) -> dict[str, Any]:
55
+ return self.post("/control/approve-action", {"actionId": action_id, "execute": execute})
56
+
57
+ def reject_action(self, action_id: str, reason: str) -> dict[str, Any]:
58
+ return self.post("/control/reject-action", {"actionId": action_id, "reason": reason})
59
+
60
+ def action_queue(self) -> dict[str, Any]:
61
+ return self.post("/control/action-queue", {})
62
+
63
+ def set_goal(self, goal: str) -> dict[str, Any]:
64
+ return self.post("/control/goal", {"goal": goal})
65
+
66
+ def current_goal(self) -> dict[str, Any]:
67
+ return self.post("/control/goal/current", {})
68
+
69
+ def pause(self) -> dict[str, Any]:
70
+ return self.post("/control/pause", {})
71
+
72
+ def resume(self) -> dict[str, Any]:
73
+ return self.post("/control/resume", {})
74
+
75
+ def stop(self) -> dict[str, Any]:
76
+ return self.post("/control/stop", {})
77
+
78
+ def set_action_mode(self, mode: str) -> dict[str, Any]:
79
+ return self.post("/control/action-mode", {"mode": mode})
80
+
81
+ def browser_start(self) -> dict[str, Any]:
82
+ return self.post("/browser/start", {})
83
+
84
+ def browser_status(self) -> dict[str, Any]:
85
+ return self.post("/browser/status", {})
86
+
87
+ def browser_close(self) -> dict[str, Any]:
88
+ return self.post("/browser/close", {})
89
+
90
+ def browser_refresh(self) -> dict[str, Any]:
91
+ return self.post("/browser/refresh", {})
92
+
93
+ def browser_back(self) -> dict[str, Any]:
94
+ return self.post("/browser/back", {})
95
+
96
+ def browser_forward(self) -> dict[str, Any]:
97
+ return self.post("/browser/forward", {})
98
+
99
+ def capture(self, label: str = "capture") -> dict[str, Any]:
100
+ return self.post("/browser/capture", {"label": label})
101
+
102
+ def read_text(self, selector: str, reason: str) -> dict[str, Any]:
103
+ return self.post("/browser/read-text", {"selector": selector, "reason": reason})
104
+
105
+ def read_dom(self) -> dict[str, Any]:
106
+ return self.post("/browser/read-dom", {})
107
+
108
+ def read_accessibility_tree(self) -> dict[str, Any]:
109
+ return self.post("/browser/read-accessibility-tree", {})
110
+
111
+ def read_console_errors(self) -> dict[str, Any]:
112
+ return self.post("/browser/read-console-errors", {})
113
+
114
+ def read_network_failures(self) -> dict[str, Any]:
115
+ return self.post("/browser/read-network-failures", {})
116
+
117
+ def inspect(self) -> dict[str, Any]:
118
+ return self.post("/browser/inspect", {})
119
+
120
+ def inspect_element(self, selector: str) -> dict[str, Any]:
121
+ return self.post("/browser/inspect-element", {"selector": selector})
122
+
123
+ def generate_report(self) -> dict[str, Any]:
124
+ return self.post("/report/generate", {})
125
+
126
+ def list_reports(self) -> dict[str, Any]:
127
+ return self.post("/report/list", {})
128
+
129
+ def read_report(self, report_name: str) -> dict[str, Any]:
130
+ return self.post("/report/read", {"report": report_name})
131
+
132
+ def export_report(self, report_name: str, destination: str | None = None) -> dict[str, Any]:
133
+ return self.post("/report/export", {"report": report_name, "destination": destination})
134
+
135
+ def get(self, route: str) -> dict[str, Any]:
136
+ return self._request(route, method="GET")
137
+
138
+ def post(self, route: str, payload: dict[str, Any]) -> dict[str, Any]:
139
+ return self._request(route, method="POST", payload=payload)
140
+
141
+ def _request(
142
+ self,
143
+ route: str,
144
+ method: str,
145
+ payload: dict[str, Any] | None = None,
146
+ ) -> dict[str, Any]:
147
+ url = f"{self.runtime_url}{route}"
148
+ data = json.dumps(payload or {}).encode("utf8") if method == "POST" else None
149
+ headers = {"Content-Type": "application/json"}
150
+ if self.token:
151
+ headers["Authorization"] = f"Bearer {self.token}"
152
+
153
+ last_error: Exception | None = None
154
+ for attempt in range(self.retries + 1):
155
+ try:
156
+ request = Request(url, data=data, headers=headers, method=method)
157
+ with urlopen(request, timeout=self.timeout_seconds) as response:
158
+ return json.loads(response.read().decode("utf8"))
159
+ except (HTTPError, URLError) as exc:
160
+ last_error = exc
161
+ if attempt < self.retries:
162
+ time.sleep(0.2 * (attempt + 1))
163
+ continue
164
+ raise RuntimeClientError(
165
+ "Agent Portal runtime is not running. Start it with: agent-portal start"
166
+ ) from last_error
167
+
168
+ def _load_token(self) -> str | None:
169
+ env_token = os.getenv("AGENT_PORTAL_TOKEN")
170
+ if env_token:
171
+ return env_token
172
+ config_path = Path.cwd() / "agent-portal.config.json"
173
+ if not config_path.exists():
174
+ return None
175
+ try:
176
+ raw = json.loads(config_path.read_text(encoding="utf8"))
177
+ except json.JSONDecodeError:
178
+ return None
179
+ token = raw.get("api_token")
180
+ return token if isinstance(token, str) and token else None
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ import argparse
4
+ import json
5
+ from dataclasses import asdict
6
+
7
+ from agent_portal_mcp.doctor import run_doctor
8
+ from agent_portal_mcp.server import AgentPortalMcpServer
9
+
10
+
11
+ def main() -> None:
12
+ parser = argparse.ArgumentParser(prog="agent-portal-mcp")
13
+ parser.add_argument("--runtime-url", default="http://127.0.0.1:8765")
14
+ parser.add_argument("--json", action="store_true")
15
+ subparsers = parser.add_subparsers(dest="command", required=True)
16
+ subparsers.add_parser("start")
17
+ subparsers.add_parser("doctor")
18
+ args = parser.parse_args()
19
+
20
+ if args.command == "start":
21
+ AgentPortalMcpServer(runtime_url=args.runtime_url).serve_stdio()
22
+ return
23
+
24
+ result = run_doctor(runtime_url=args.runtime_url)
25
+ payload = {"checks": [asdict(check) for check in result.checks]}
26
+ if args.json:
27
+ print(json.dumps(payload, indent=2))
28
+ return
29
+ for check in result.checks:
30
+ print(f"{check.status.upper():7} {check.name}: {check.details}")
31
+ if check.suggested_fix:
32
+ print(f" fix: {check.suggested_fix}")