toolhome 0.5.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 (214) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +295 -0
  3. package/README.zh.md +281 -0
  4. package/dist/server/app.d.ts +16 -0
  5. package/dist/server/app.d.ts.map +1 -0
  6. package/dist/server/app.js +166 -0
  7. package/dist/server/app.js.map +1 -0
  8. package/dist/server/cli/main.d.ts +3 -0
  9. package/dist/server/cli/main.d.ts.map +1 -0
  10. package/dist/server/cli/main.js +587 -0
  11. package/dist/server/cli/main.js.map +1 -0
  12. package/dist/server/cli-plane/allow-list.d.ts +18 -0
  13. package/dist/server/cli-plane/allow-list.d.ts.map +1 -0
  14. package/dist/server/cli-plane/allow-list.js +28 -0
  15. package/dist/server/cli-plane/allow-list.js.map +1 -0
  16. package/dist/server/cli-plane/cli-service.d.ts +41 -0
  17. package/dist/server/cli-plane/cli-service.d.ts.map +1 -0
  18. package/dist/server/cli-plane/cli-service.js +282 -0
  19. package/dist/server/cli-plane/cli-service.js.map +1 -0
  20. package/dist/server/cli-plane/frames.d.ts +26 -0
  21. package/dist/server/cli-plane/frames.d.ts.map +1 -0
  22. package/dist/server/cli-plane/frames.js +12 -0
  23. package/dist/server/cli-plane/frames.js.map +1 -0
  24. package/dist/server/cli-plane/models.d.ts +174 -0
  25. package/dist/server/cli-plane/models.d.ts.map +1 -0
  26. package/dist/server/cli-plane/models.js +169 -0
  27. package/dist/server/cli-plane/models.js.map +1 -0
  28. package/dist/server/cli-plane/routes.d.ts +32 -0
  29. package/dist/server/cli-plane/routes.d.ts.map +1 -0
  30. package/dist/server/cli-plane/routes.js +94 -0
  31. package/dist/server/cli-plane/routes.js.map +1 -0
  32. package/dist/server/cli-plane/runner.d.ts +41 -0
  33. package/dist/server/cli-plane/runner.d.ts.map +1 -0
  34. package/dist/server/cli-plane/runner.js +163 -0
  35. package/dist/server/cli-plane/runner.js.map +1 -0
  36. package/dist/server/cli-plane/status.d.ts +12 -0
  37. package/dist/server/cli-plane/status.d.ts.map +1 -0
  38. package/dist/server/cli-plane/status.js +24 -0
  39. package/dist/server/cli-plane/status.js.map +1 -0
  40. package/dist/server/config/harness-import.d.ts +44 -0
  41. package/dist/server/config/harness-import.d.ts.map +1 -0
  42. package/dist/server/config/harness-import.js +166 -0
  43. package/dist/server/config/harness-import.js.map +1 -0
  44. package/dist/server/config.d.ts +20 -0
  45. package/dist/server/config.d.ts.map +1 -0
  46. package/dist/server/config.js +80 -0
  47. package/dist/server/config.js.map +1 -0
  48. package/dist/server/control/client.d.ts +24 -0
  49. package/dist/server/control/client.d.ts.map +1 -0
  50. package/dist/server/control/client.js +115 -0
  51. package/dist/server/control/client.js.map +1 -0
  52. package/dist/server/control/control-api.d.ts +39 -0
  53. package/dist/server/control/control-api.d.ts.map +1 -0
  54. package/dist/server/control/control-api.js +291 -0
  55. package/dist/server/control/control-api.js.map +1 -0
  56. package/dist/server/control/control-service.d.ts +713 -0
  57. package/dist/server/control/control-service.d.ts.map +1 -0
  58. package/dist/server/control/control-service.js +693 -0
  59. package/dist/server/control/control-service.js.map +1 -0
  60. package/dist/server/control/openapi.d.ts +2 -0
  61. package/dist/server/control/openapi.d.ts.map +1 -0
  62. package/dist/server/control/openapi.js +370 -0
  63. package/dist/server/control/openapi.js.map +1 -0
  64. package/dist/server/data-plane/data-plane.d.ts +20 -0
  65. package/dist/server/data-plane/data-plane.d.ts.map +1 -0
  66. package/dist/server/data-plane/data-plane.js +335 -0
  67. package/dist/server/data-plane/data-plane.js.map +1 -0
  68. package/dist/server/data-plane/gateway-server.d.ts +13 -0
  69. package/dist/server/data-plane/gateway-server.d.ts.map +1 -0
  70. package/dist/server/data-plane/gateway-server.js +796 -0
  71. package/dist/server/data-plane/gateway-server.js.map +1 -0
  72. package/dist/server/data-plane/legacy-session.d.ts +18 -0
  73. package/dist/server/data-plane/legacy-session.d.ts.map +1 -0
  74. package/dist/server/data-plane/legacy-session.js +137 -0
  75. package/dist/server/data-plane/legacy-session.js.map +1 -0
  76. package/dist/server/data-plane/projection.d.ts +18 -0
  77. package/dist/server/data-plane/projection.d.ts.map +1 -0
  78. package/dist/server/data-plane/projection.js +44 -0
  79. package/dist/server/data-plane/projection.js.map +1 -0
  80. package/dist/server/data-plane/registry.d.ts +31 -0
  81. package/dist/server/data-plane/registry.d.ts.map +1 -0
  82. package/dist/server/data-plane/registry.js +129 -0
  83. package/dist/server/data-plane/registry.js.map +1 -0
  84. package/dist/server/data-plane/task-extension.d.ts +7 -0
  85. package/dist/server/data-plane/task-extension.d.ts.map +1 -0
  86. package/dist/server/data-plane/task-extension.js +33 -0
  87. package/dist/server/data-plane/task-extension.js.map +1 -0
  88. package/dist/server/data-plane/virtualization.d.ts +35 -0
  89. package/dist/server/data-plane/virtualization.d.ts.map +1 -0
  90. package/dist/server/data-plane/virtualization.js +228 -0
  91. package/dist/server/data-plane/virtualization.js.map +1 -0
  92. package/dist/server/domain/errors.d.ts +9 -0
  93. package/dist/server/domain/errors.d.ts.map +1 -0
  94. package/dist/server/domain/errors.js +19 -0
  95. package/dist/server/domain/errors.js.map +1 -0
  96. package/dist/server/domain/models.d.ts +720 -0
  97. package/dist/server/domain/models.d.ts.map +1 -0
  98. package/dist/server/domain/models.js +327 -0
  99. package/dist/server/domain/models.js.map +1 -0
  100. package/dist/server/main.d.ts +2 -0
  101. package/dist/server/main.d.ts.map +1 -0
  102. package/dist/server/main.js +37 -0
  103. package/dist/server/main.js.map +1 -0
  104. package/dist/server/manage/management-mcp.d.ts +27 -0
  105. package/dist/server/manage/management-mcp.d.ts.map +1 -0
  106. package/dist/server/manage/management-mcp.js +431 -0
  107. package/dist/server/manage/management-mcp.js.map +1 -0
  108. package/dist/server/market/catalog.d.ts +111 -0
  109. package/dist/server/market/catalog.d.ts.map +1 -0
  110. package/dist/server/market/catalog.js +784 -0
  111. package/dist/server/market/catalog.js.map +1 -0
  112. package/dist/server/market/installers.d.ts +64 -0
  113. package/dist/server/market/installers.d.ts.map +1 -0
  114. package/dist/server/market/installers.js +181 -0
  115. package/dist/server/market/installers.js.map +1 -0
  116. package/dist/server/market/market-service.d.ts +146 -0
  117. package/dist/server/market/market-service.d.ts.map +1 -0
  118. package/dist/server/market/market-service.js +930 -0
  119. package/dist/server/market/market-service.js.map +1 -0
  120. package/dist/server/observability/call-recorder.d.ts +22 -0
  121. package/dist/server/observability/call-recorder.d.ts.map +1 -0
  122. package/dist/server/observability/call-recorder.js +99 -0
  123. package/dist/server/observability/call-recorder.js.map +1 -0
  124. package/dist/server/observability/logger.d.ts +16 -0
  125. package/dist/server/observability/logger.d.ts.map +1 -0
  126. package/dist/server/observability/logger.js +25 -0
  127. package/dist/server/observability/logger.js.map +1 -0
  128. package/dist/server/security/api-keys.d.ts +15 -0
  129. package/dist/server/security/api-keys.d.ts.map +1 -0
  130. package/dist/server/security/api-keys.js +35 -0
  131. package/dist/server/security/api-keys.js.map +1 -0
  132. package/dist/server/security/auth-service.d.ts +26 -0
  133. package/dist/server/security/auth-service.d.ts.map +1 -0
  134. package/dist/server/security/auth-service.js +76 -0
  135. package/dist/server/security/auth-service.js.map +1 -0
  136. package/dist/server/security/control-session.d.ts +9 -0
  137. package/dist/server/security/control-session.d.ts.map +1 -0
  138. package/dist/server/security/control-session.js +51 -0
  139. package/dist/server/security/control-session.js.map +1 -0
  140. package/dist/server/security/credential-materializer.d.ts +20 -0
  141. package/dist/server/security/credential-materializer.d.ts.map +1 -0
  142. package/dist/server/security/credential-materializer.js +82 -0
  143. package/dist/server/security/credential-materializer.js.map +1 -0
  144. package/dist/server/security/cursor-codec.d.ts +13 -0
  145. package/dist/server/security/cursor-codec.d.ts.map +1 -0
  146. package/dist/server/security/cursor-codec.js +43 -0
  147. package/dist/server/security/cursor-codec.js.map +1 -0
  148. package/dist/server/security/oauth-routes.d.ts +21 -0
  149. package/dist/server/security/oauth-routes.d.ts.map +1 -0
  150. package/dist/server/security/oauth-routes.js +100 -0
  151. package/dist/server/security/oauth-routes.js.map +1 -0
  152. package/dist/server/security/oauth-server.d.ts +16 -0
  153. package/dist/server/security/oauth-server.d.ts.map +1 -0
  154. package/dist/server/security/oauth-server.js +544 -0
  155. package/dist/server/security/oauth-server.js.map +1 -0
  156. package/dist/server/security/secret-box.d.ts +7 -0
  157. package/dist/server/security/secret-box.d.ts.map +1 -0
  158. package/dist/server/security/secret-box.js +27 -0
  159. package/dist/server/security/secret-box.js.map +1 -0
  160. package/dist/server/security/secure-action.d.ts +21 -0
  161. package/dist/server/security/secure-action.d.ts.map +1 -0
  162. package/dist/server/security/secure-action.js +69 -0
  163. package/dist/server/security/secure-action.js.map +1 -0
  164. package/dist/server/storage/sqlite-store.d.ts +70 -0
  165. package/dist/server/storage/sqlite-store.d.ts.map +1 -0
  166. package/dist/server/storage/sqlite-store.js +1522 -0
  167. package/dist/server/storage/sqlite-store.js.map +1 -0
  168. package/dist/server/storage/store.d.ts +82 -0
  169. package/dist/server/storage/store.d.ts.map +1 -0
  170. package/dist/server/storage/store.js +2 -0
  171. package/dist/server/storage/store.js.map +1 -0
  172. package/dist/server/upstream/adapter.d.ts +52 -0
  173. package/dist/server/upstream/adapter.d.ts.map +1 -0
  174. package/dist/server/upstream/adapter.js +807 -0
  175. package/dist/server/upstream/adapter.js.map +1 -0
  176. package/dist/server/upstream/credential-resolver.d.ts +14 -0
  177. package/dist/server/upstream/credential-resolver.d.ts.map +1 -0
  178. package/dist/server/upstream/credential-resolver.js +56 -0
  179. package/dist/server/upstream/credential-resolver.js.map +1 -0
  180. package/dist/server/upstream/extension-transport.d.ts +17 -0
  181. package/dist/server/upstream/extension-transport.d.ts.map +1 -0
  182. package/dist/server/upstream/extension-transport.js +207 -0
  183. package/dist/server/upstream/extension-transport.js.map +1 -0
  184. package/dist/server/upstream/manager.d.ts +23 -0
  185. package/dist/server/upstream/manager.d.ts.map +1 -0
  186. package/dist/server/upstream/manager.js +324 -0
  187. package/dist/server/upstream/manager.js.map +1 -0
  188. package/dist/server/upstream/oauth-provider.d.ts +38 -0
  189. package/dist/server/upstream/oauth-provider.d.ts.map +1 -0
  190. package/dist/server/upstream/oauth-provider.js +225 -0
  191. package/dist/server/upstream/oauth-provider.js.map +1 -0
  192. package/dist/server/upstream/oauth-refresh.d.ts +65 -0
  193. package/dist/server/upstream/oauth-refresh.d.ts.map +1 -0
  194. package/dist/server/upstream/oauth-refresh.js +240 -0
  195. package/dist/server/upstream/oauth-refresh.js.map +1 -0
  196. package/dist/server/upstream/oauth-routes.d.ts +17 -0
  197. package/dist/server/upstream/oauth-routes.d.ts.map +1 -0
  198. package/dist/server/upstream/oauth-routes.js +87 -0
  199. package/dist/server/upstream/oauth-routes.js.map +1 -0
  200. package/dist/server/upstream/oauth-service.d.ts +143 -0
  201. package/dist/server/upstream/oauth-service.d.ts.map +1 -0
  202. package/dist/server/upstream/oauth-service.js +217 -0
  203. package/dist/server/upstream/oauth-service.js.map +1 -0
  204. package/dist/server/upstream/stable-json.d.ts +3 -0
  205. package/dist/server/upstream/stable-json.d.ts.map +1 -0
  206. package/dist/server/upstream/stable-json.js +17 -0
  207. package/dist/server/upstream/stable-json.js.map +1 -0
  208. package/package.json +69 -0
  209. package/skills/toolhome/SKILL.md +238 -0
  210. package/skills/toolhome/references/cli-reference.md +242 -0
  211. package/skills/toolhome/references/deployment.md +113 -0
  212. package/skills/toolhome/references/market-guide.md +128 -0
  213. package/skills/toolhome/references/oauth-guide.md +96 -0
  214. package/skills/toolhome/references/troubleshooting.md +133 -0
@@ -0,0 +1,242 @@
1
+ # CLI Reference
2
+
3
+ ## auth
4
+
5
+ ```bash
6
+ export TOOLHOME_URL="https://tool.cyncyn.xyz"
7
+ export TOOLHOME_CONTROL_KEY="tch_ctl_<your-control-key>"
8
+ toolhome auth login --url "$TOOLHOME_URL" --control-key "$TOOLHOME_CONTROL_KEY"
9
+ toolhome auth logout
10
+ ```
11
+
12
+ Credentials saved to `~/.config/toolhome/config.json` (0600). Supports env vars `TOOLHOME_URL`, `TOOLHOME_CONTROL_KEY`, and `TOOLHOME_CONFIG` (an alternate config-file path).
13
+
14
+ ## server
15
+
16
+ ```bash
17
+ toolhome server list # list all servers
18
+ toolhome server get <id> # get a server by id
19
+ toolhome server add <file|-> # add from JSON file or stdin
20
+ toolhome server update <id> <file|-> # update fields (partial)
21
+ toolhome server delete <id> # delete a server
22
+ toolhome server enable <id> # enable
23
+ toolhome server disable <id> # disable
24
+ toolhome server refresh <id> # force reconnect + snapshot
25
+ toolhome server restart <id> # restart home-hosted stdio
26
+ toolhome server test <id> # test connectivity
27
+ toolhome server capabilities <id> # show capability snapshot
28
+ toolhome server status <id> # runtime status + last error
29
+ toolhome server logs <id> # recent log entries
30
+ toolhome server endpoint <id> # per-server endpoint URL
31
+ ```
32
+
33
+ Server JSON (create):
34
+
35
+ ```json
36
+ {
37
+ "slug": "firecrawl",
38
+ "name": "Firecrawl",
39
+ "kind": "remote",
40
+ "transport": { "type": "streamable-http", "url": "https://mcp.firecrawl.dev/v2/mcp" },
41
+ "credentialId": "<uuid>",
42
+ "enabled": true,
43
+ "settings": { "urlClientId": false }
44
+ }
45
+ ```
46
+
47
+ Home-hosted stdio:
48
+
49
+ ```json
50
+ {
51
+ "slug": "resend",
52
+ "name": "Resend",
53
+ "kind": "home",
54
+ "transport": {
55
+ "type": "stdio",
56
+ "command": "/data/market/node_modules/.bin/resend-mcp",
57
+ "args": []
58
+ },
59
+ "credentialId": "<uuid>",
60
+ "enabled": true
61
+ }
62
+ ```
63
+
64
+ ## credential
65
+
66
+ ```bash
67
+ toolhome credential list # list all credentials
68
+ toolhome credential get <id> # get a credential
69
+ toolhome credential add <file|-> # add from JSON
70
+ toolhome credential update <id> <file|-> # update
71
+ toolhome credential delete <id> # delete
72
+ toolhome credential test <id> # validate + refresh
73
+ toolhome credential revoke <id> # revoke OAuth tokens
74
+ toolhome credential authorize <name> # start OAuth flow (by name or id)
75
+ ```
76
+
77
+ Authorize options:
78
+
79
+ ```
80
+ --server <slug> specify the server (auto-resolved if omitted)
81
+ --force clear old client + re-authorize
82
+ --no-open don't open browser
83
+ --no-wait print URL and exit
84
+ --timeout <sec> wait time (default 600)
85
+ ```
86
+
87
+ Credential JSON (create):
88
+
89
+ ```json
90
+ { "name": "firecrawl", "payload": { "type": "bearer", "token": "fc-xxx" } }
91
+ { "name": "apifox", "payload": { "type": "headers", "headers": { "Authorization": "Bearer xxx", "X-Apifox-Api-Version": "2025-09-01" } } }
92
+ { "name": "cloudflare", "payload": { "type": "oauth" } }
93
+ { "name": "resend", "payload": { "type": "env", "variables": { "RESEND_API_KEY": "re_xxx" } } }
94
+ ```
95
+
96
+ ## access-key
97
+
98
+ ```bash
99
+ toolhome access-key create <name> # returns secret once
100
+ toolhome access-key list
101
+ toolhome access-key revoke <id>
102
+ ```
103
+
104
+ ## control-key
105
+
106
+ ```bash
107
+ toolhome control-key create <name> # returns secret once
108
+ toolhome control-key list
109
+ toolhome control-key revoke <id>
110
+ ```
111
+
112
+ ## market
113
+
114
+ ```bash
115
+ toolhome market list # browse catalog with install status
116
+ toolhome market install <id> # install MCP or CLI; browser URL for missing secrets
117
+ toolhome market install <id> --set KEY=value # non-secret config (repeatable --set)
118
+ toolhome market uninstall <id> # remove MCP server or CLI and credential
119
+ toolhome market updates # installed vs catalog pin (+ upstream latest)
120
+ toolhome market update <id> # update to the catalog pin (keeps credential, restarts)
121
+ ```
122
+
123
+ ## config
124
+
125
+ ```bash
126
+ toolhome config export <file> # redacted export
127
+ toolhome config export <file> --include-secrets # full backup (0600)
128
+ toolhome config import <file> # restore (atomic transaction)
129
+ toolhome config import-harness <file> [--preview] [--upsert]
130
+ # import a Claude Desktop / Cursor mcpServers JSON;
131
+ # secrets become encrypted credentials (never echoed)
132
+ ```
133
+
134
+ Harness import accepts `{ "mcpServers": { name: { command, args, env } | { url, headers } } }`.
135
+ Secret-looking env keys (password/token/secret/key/authorization) and `Authorization` headers
136
+ are stored as encrypted credentials; everything else lands in the server transport.
137
+ `--upsert` re-imports as an update: existing servers are diffed (unchanged → skip,
138
+ changed → update transport/env/credential) instead of reported as conflicts.
139
+
140
+ ## endpoint
141
+
142
+ ```bash
143
+ toolhome endpoint aggregate # aggregate /mcp URL
144
+ toolhome endpoint server <id> # per-server /mcp/{slug} URL
145
+ ```
146
+
147
+ ## diagnostics
148
+
149
+ ```bash
150
+ toolhome status # overview (servers, credentials, keys, endpoints)
151
+ toolhome doctor # health check per server
152
+ toolhome events # recent events
153
+ toolhome events --limit 100 # includes hosted cli.exec audit events
154
+ ```
155
+
156
+ ## api (raw)
157
+
158
+ ```bash
159
+ toolhome api GET /api/v1/openapi.json
160
+ toolhome api POST /api/v1/servers --body ./server.json
161
+ ```
162
+
163
+ ## cli (hosted CLI plane)
164
+
165
+ Server-side CLI hosting (Form A): register a CLI once on the ToolHome host; client
166
+ machines never install or log in to it. Records live in the `clis` table and execs are
167
+ audited in the events stream. The web console has a parallel **CLIs** page
168
+ (register/edit/delete, enable toggle, and a Run sheet that streams exec output).
169
+
170
+ ```bash
171
+ toolhome cli list
172
+ toolhome cli get <id>
173
+ toolhome cli add ./cli.json
174
+ toolhome cli update <id> ./cli-patch.json
175
+ toolhome cli enable <id>
176
+ toolhome cli disable <id>
177
+ toolhome cli delete <id>
178
+ toolhome cli status <slug>
179
+ toolhome cli exec <slug> -- --version
180
+ toolhome cli exec <slug> --timeout 60000 --max-output-bytes 65536 -- <args...>
181
+ toolhome cli exec <slug> --stdin-file ./input.txt -- <args...>
182
+ toolhome cli exec <slug> --stdin-file - -- <args...>
183
+ ```
184
+
185
+ `--stdin <text>` and `--stdin-file <path|->` are mutually exclusive. Put platform arguments after `--`; timeout is in milliseconds and cannot exceed the record's limit. Human output streams stdout/stderr; `--output json` emits NDJSON frames and an outcome. A failed or timed-out remote execution exits locally with code 1. Ctrl-C cancels the stream.
186
+
187
+ ```bash
188
+ # registry (Control API, admin key)
189
+ toolhome api GET /api/v1/clis # list registered CLIs
190
+ toolhome api POST /api/v1/clis --body ./cli.json # register (201); prefer Market for Azure bootstrap
191
+ toolhome api GET /api/v1/clis/<id> # fetch
192
+ echo '{"enabled":false}' | toolhome api PATCH /api/v1/clis/<id> --body -
193
+ toolhome api DELETE /api/v1/clis/<id> # delete
194
+ ```
195
+
196
+ Exec and status live on the data plane (`POST /cli/{slug}/exec`, `GET /cli/{slug}/status`):
197
+
198
+ ```bash
199
+ # exec: argv array only — never a shell string; body must be JSON
200
+ toolhome cli exec azure-cli --timeout 30000 -- account show -o table
201
+ # → 200 NDJSON stream: {"type":"stdout","data":...} {"type":"stderr","data":...}
202
+ # {"type":"exit","code":0,"durationMs":4211,"result":"ok"}
203
+ # timeout → {"type":"exit","code":null,"durationMs":...,"result":"timeout"}
204
+ # truncated output → exit frame carries "truncated":true
205
+
206
+ toolhome api GET /cli/azure-cli/status
207
+ # → { "installed":true, "version":"azure-cli 2.x", "loggedIn":true, "lastCheckedAt":"..." }
208
+ ```
209
+
210
+ Semantics:
211
+
212
+ - **argv array only**: `spawn(command, argv)` with no shell; a shell string is rejected (400).
213
+ - **Allow-list**: `deny` rules are evaluated first; a non-empty `allow` list must match at
214
+ least one rule (`*` matches one argv token, rules match as argv prefixes). Denied argv is
215
+ rejected with 403 before any process is spawned.
216
+ - **Credential materialization**: MCP and hosted CLI entries share the encrypted credential
217
+ payload format. CLI bindings project `env:<name>`, `token`, `value`, `header:<name>`, or
218
+ `accessToken` into the process environment. OAuth uses its stored access token and returns
219
+ `credential_authorization_required` until authorization is complete.
220
+ - **Env injection**: every exec gets `CI=true`, `NO_COLOR=1`, `PAGER=cat`, `TERM=dumb`
221
+ (unless the record sets `interactive: true`), plus the bound credential variables.
222
+ - **Status probe**: the declared `probe.command` runs in the same context; its stdout is
223
+ parsed as `version=...` / `loggedIn=true|false` lines; `installed` = exit code 0.
224
+ - **Isolation**: `executionMode: 'host'` spawns on the ToolHome host (trusted entries);
225
+ `'docker'` spawns a one-shot sibling container (`docker run --rm -i <image> <argv…>`).
226
+ `containerVolumes` are explicit Docker named volumes forwarded with `--volume`; bind paths,
227
+ duplicate targets, and host credential directories are rejected.
228
+ credential directories are never mounted implicitly. `authStrategy: "azure-service-principal"`
229
+ performs a fixed `az login --service-principal` bootstrap from bound environment variables,
230
+ while `authStrategy: "tailscale-auth-key"` starts an in-container userspace `tailscaled` and performs a fixed `tailscale --socket=... up --auth-key` bootstrap.
231
+ Catalog entries may use npm, Go, GitHub Release, uv, or Docker installer backends, but those
232
+ backends are hidden implementation details rather than separate Market products.
233
+ - **Audit**: every exec appends a `cli.exec` event (slug, argv, exit code, duration) visible
234
+ via `toolhome events` / `GET /api/v1/events`.
235
+
236
+ ## global options
237
+
238
+ ```
239
+ --url <url> control API URL (or TOOLHOME_URL env)
240
+ --key <key> control key (or TOOLHOME_CONTROL_KEY env)
241
+ --output <human|json> output format (default human)
242
+ ```
@@ -0,0 +1,113 @@
1
+ # Deployment Guide
2
+
3
+ ## Docker (Recommended)
4
+
5
+ ```yaml
6
+ # docker-compose.yml
7
+ services:
8
+ toolhome:
9
+ image: ghcr.io/crayonlu/toolhome:latest
10
+ init: true
11
+ restart: unless-stopped
12
+ ports:
13
+ - '127.0.0.1:3344:3344'
14
+ volumes:
15
+ - ./data:/data
16
+ - /var/run/docker.sock:/var/run/docker.sock
17
+ group_add:
18
+ - '${DOCKER_GROUP_ID:-999}'
19
+ env_file: .env
20
+ ```
21
+
22
+ ```bash
23
+ # .env
24
+ TOOLHOME_MASTER_KEY=<32+ char random>
25
+ TOOLHOME_BOOTSTRAP_CONTROL_KEY=tch_ctl_<random suffix, different from master>
26
+ TOOLHOME_PUBLIC_URL=https://tool.cyncyn.xyz
27
+ TOOLHOME_ALLOWED_HOSTS=tool.cyncyn.xyz
28
+ ```
29
+
30
+ ```bash
31
+ docker compose up -d
32
+ ```
33
+
34
+ ## CI/CD (GitHub Actions)
35
+
36
+ The repo includes `.github/workflows/ci.yml` with three jobs:
37
+
38
+ 1. **test**: server check + test, web typecheck + test
39
+ 2. **docker**: build dists -> docker build -> push `ghcr.io/crayonlu/toolhome:latest`
40
+ 3. **deploy**: download the app image artifact, build the pinned GitHub CLI image, transfer both over SSH, load them with Docker, and run `docker compose up -d`
41
+
42
+ ### Required GitHub Secrets
43
+
44
+ | Secret | Value |
45
+ | ------------- | ------------------------------- |
46
+ | `DEPLOY_HOST` | Server IP or hostname |
47
+ | `DEPLOY_USER` | SSH username |
48
+ | `DEPLOY_KEY` | SSH private key (no passphrase) |
49
+
50
+ ### GHCR Package Visibility
51
+
52
+ After first CI push, set the package to Public:
53
+ `github.com/<user>?tab=packages -> toolhome -> Package settings -> Change visibility -> Public`
54
+
55
+ ### Deployment Flow
56
+
57
+ ```
58
+ git push main -> test -> docker build + push GHCR -> SSH deploy -> done
59
+ git tag v* -> test -> docker build + push :v* tag (no deploy)
60
+ ```
61
+
62
+ The current image job also updates `:latest` on tag pushes. Tags alone do not publish npm packages or create GitHub Releases. For a release, verify both independently: `npm view toolhome version` and `gh release list`. Build/check/test both planes, include `skills/toolhome` in the npm archive, publish npm with the user's 2FA confirmation when requested, and create a GitHub Release for the matching version tag. Never overwrite an already published npm version.
63
+
64
+ ## Reverse Proxy
65
+
66
+ Put an HTTPS reverse proxy (nginx/Caddy/openresty) in front. Key requirements:
67
+
68
+ - **WebSocket/SSE support**: MCP uses Streamable HTTP with SSE responses. The proxy must not buffer SSE.
69
+ - **No CDN proxy for the domain**: If behind Cloudflare (orange cloud), SSE POST responses get buffered for ~25s. Use DNS-only (grey cloud) for the ToolHome subdomain.
70
+ - **Valid TLS certificate**: OAuth callbacks and URL-based client metadata require HTTPS.
71
+
72
+ ### nginx Example
73
+
74
+ ```nginx
75
+ location / {
76
+ proxy_pass http://127.0.0.1:3344;
77
+ proxy_http_version 1.1;
78
+ proxy_set_header Host $host;
79
+ proxy_set_header X-Forwarded-Proto $scheme;
80
+ proxy_buffering off;
81
+ proxy_read_timeout 600s;
82
+ }
83
+ ```
84
+
85
+ ## Data Persistence
86
+
87
+ | Path | Content | Persist? |
88
+ | --------------------------------------------------- | ---------------------------------------------------------------------------------- | ------------ |
89
+ | `/data/toolhome.sqlite` | SQLite database (servers, credentials, keys, events) | Yes (volume) |
90
+ | `/data/market/` | Market-installed npm/Go/GitHub Release artifacts | Yes (volume) |
91
+ | `/data/.uv/` | uv tool and cache state | Yes (volume) |
92
+ | Docker named volumes declared by Hosted CLI entries | Platform CLI state, such as Azure `/root/.azure` or Tailscale `/var/lib/tailscale` | Yes (volume) |
93
+ | `/app/dist/` | Server code (baked in image) | No (image) |
94
+ | `/app/web-dist/` | Web console (baked in image) | No (image) |
95
+
96
+ ## Local Development
97
+
98
+ ```bash
99
+ npm install
100
+ npm run dev # server (tsx watch)
101
+ npm run dev:web # web console (Vite, proxies /api to :3344)
102
+ ```
103
+
104
+ ## Building from Source
105
+
106
+ ```bash
107
+ npm run build # builds server dist + web dist
108
+ docker compose up -d --build # build image locally
109
+ ```
110
+
111
+ The Dockerfile copies prebuilt `dist/` and `web/dist/` from the build context (not built in-container, to avoid memory issues on low-RAM hosts).
112
+
113
+ For Docker-backed MCP or Hosted CLI entries, keep the Docker socket mount and `group_add` configuration. The curated Tailscale CLI uses userspace networking inside the sibling container and persists its identity in the declared named volume, so it does not require a host TUN device, extra capabilities, or a special network mode. A basic `docker run --rm -i` container is sufficient for the curated read-only platform CLI entries.
@@ -0,0 +1,128 @@
1
+ # Market Guide
2
+
3
+ ## Overview
4
+
5
+ The Market is a curated catalog of MCP servers and hosted platform CLIs. One command installs the target, creates the credential, and configures the runtime. A Market entry represents the capability itself; installer technologies such as npm, Go, GitHub Release, uv, and Docker remain implementation details.
6
+
7
+ ## Catalog Entries (35)
8
+
9
+ ### Remote (OAuth)
10
+
11
+ linear, slack, stripe, figma, sentry, supabase, notion, cloudflare
12
+
13
+ ### Remote (API Key)
14
+
15
+ github (PAT), context7, exa, tavily, firecrawl, openrouter, apifox
16
+
17
+ ### Remote (no auth)
18
+
19
+ deepwiki
20
+
21
+ ### Home-stdio MCP servers
22
+
23
+ resend, tailscale, playwright, postgres, sqlite, memory, sequential-thinking, mosaic
24
+
25
+ ### Uvx-backed MCP server
26
+
27
+ fetch
28
+
29
+ ### Docker-backed MCP server
30
+
31
+ markitdown
32
+
33
+ ### Hosted platform CLIs
34
+
35
+ azure-cli (`az`), gh-cli (`gh`), tailscale-cli, lark-cli, firecrawl-cli (`firecrawl`), wrangler-cli (`wrangler`), vercel-cli, aliyun-cli (`aliyun`)
36
+
37
+ ### Existing host binary
38
+
39
+ host-shell (`/bin/sh`), explicit opt-in for trusted shell execution. This is the ninth CLI entry; there are 26 MCP entries.
40
+
41
+ ### Deployment coverage
42
+
43
+ | Installer / source | CLI execution | Curated CLI examples |
44
+ | --------------------------------- | ------------- | ------------------------------------------- |
45
+ | npm | host | Lark, Firecrawl, Wrangler, Vercel |
46
+ | GitHub Release archive | host | Aliyun (linux-amd64 only) |
47
+ | Docker image or inline Dockerfile | docker | Azure, GitHub, Tailscale |
48
+ | Existing host binary | host | host-shell |
49
+ | Go module | host | Backend supported; no curated CLI entry yet |
50
+ | uv / Python | host | Backend supported; no curated CLI entry yet |
51
+
52
+ There is no Cargo, apt, Homebrew, arbitrary installer-script, or remote-SSH installer backend. To use other binaries, provision them on the ToolHome host and register them with `toolhome cli add`. The catalog is curated, not an exhaustive list of platform CLIs. All five artifact installers are shared by MCP and CLI; the two execution modes are `host` and `docker`. Docker deployment includes npm, Go, uv, curl, tar, unzip, and the Docker client. Sibling containers additionally require the host socket and its group permissions.
53
+
54
+ Hosted CLI entries are parallel to MCP entries. The catalog describes the platform command, pinned artifact, credential requirements, and allowed argv; it does not expose npm, Go, uv, or Docker as products.
55
+
56
+ npm-backed CLI entries (`lark-cli`, `firecrawl-cli`, `wrangler-cli`, `vercel-cli`) install into the persistent market volume and run in host mode. Credentials are injected as environment variables (`FIRECRAWL_API_KEY`, `CLOUDFLARE_API_TOKEN`, `VERCEL_TOKEN`); `lark-cli` ships no token env var — run `lark-cli auth login --no-wait --json` through exec, complete the device flow in a browser, then finish with `lark-cli auth login --device-code <code>`. Its npm package downloads the platform binary on first execution, so the first command needs a generous exec timeout. `aliyun-cli` installs a pinned GitHub Release tarball (linux-amd64) and authenticates through `ALIBABA_CLOUD_ACCESS_KEY_ID`, `ALIBABA_CLOUD_ACCESS_KEY_SECRET`, and `ALIBABA_CLOUD_REGION_ID`; bare probe commands (its `aliyun version` probe) resolve next to the installed binary.
57
+
58
+ `gh-cli` installs without any credential and authenticates afterwards through the GitHub device flow. CI preloads `toolhome/gh-cli:2.97.0` for the managed deployment. Fresh self-hosted instances build it from the pinned upstream release when the image is unavailable; this requires access to Alpine and GitHub and supports amd64/arm64. The managed CI image sets a deployment-specific `GH_HOST` relay; the catalog fallback uses GitHub directly. Match the login hostname to the installed image's configuration. Login state persists in the `toolhome-gh-cli-state` named volume:
59
+
60
+ ```bash
61
+ toolhome cli exec gh-cli --stdin $'\n' -- auth login --hostname github.com --git-protocol https --web
62
+ # copy the one-time code, open https://github.com/login/device, then wait for the exec to finish
63
+ toolhome cli exec gh-cli -- auth status
64
+ ```
65
+
66
+ `gh auth token` stays denied so stored tokens can never reach argv logs; a token alternative is still possible via `gh auth login --with-token` with the token fed through exec stdin.
67
+
68
+ > ⚠️ The npm package name `mcp-server-fetch` is **squatted** by a canary (npx-confusion) package that runs code on install. The official Fetch server is Python — the catalog installs it via `uvx`, never via npm.
69
+
70
+ ## Installation
71
+
72
+ ```bash
73
+ toolhome market list # browse with install status
74
+ toolhome market install resend # enter missing secret at browser URL
75
+ toolhome market install context7 # enter missing secret at browser URL
76
+ toolhome market install deepwiki # no config needed
77
+ toolhome market install fetch # uvx (Python), no config
78
+ toolhome market uninstall resend
79
+ ```
80
+
81
+ ### How It Works
82
+
83
+ **Remote entries**: Creates a credential (bearer/headers/oauth) + a remote server with the upstream URL. For OAuth entries, run `toolhome credential authorize <name>` after install.
84
+
85
+ **Home-stdio entries**: The installer backend runs the pinned npm recipe, creates an env credential, and creates a home server with the stdio command pointing to the installed binary. The install is async with progress logging.
86
+
87
+ **Uvx entries**: The installer backend runs `uv tool install <package>` (Python packages from PyPI), then creates a home server that executes the installed binary from the persistent `UV_TOOL_BIN_DIR`. It does not run `uvx <package>` again during every MCP refresh, so the pinned artifact is reused without a second dependency resolution. Some entries pin extra dependencies via `--with` (e.g. `mcp-server-fetch` pins `mcp<2` because upstream still imports the pre-2.0 `McpError` name).
88
+
89
+ **Hosted CLI entries**: A CLI entry provisions a first-class CLI record and installs its pinned artifact through the entry's hidden npm, Go, GitHub Release, uvx, or Docker recipe. Execution remains argv-only and uses the same encrypted credential payloads as MCP. A declarative binding maps credential material to the platform CLI environment, for example `GH_TOKEN <- bearer token` or `AZURE_CLIENT_ID <- env:CLIENT_ID`. OAuth-backed CLI records use the stored access token and return `credential_authorization_required` until the credential is authorized. ToolHome never mounts a user's local `~/.azure`, `~/.config/gh`, `~/.aws`, or similar directory by default.
90
+
91
+ Azure service-principal entries use `authStrategy: "azure-service-principal"`: the runner starts `/bin/sh` inside the pinned Azure image, executes a fixed non-interactive `az login --service-principal` from `AZURE_CLIENT_ID`, `AZURE_CLIENT_SECRET`, and `AZURE_TENANT_ID`, then executes the allow-listed `az` argv. The named `/root/.azure` volume preserves CLI state across runs without importing a user's local Azure profile.
92
+
93
+ Tailscale entries use `authStrategy: "tailscale-auth-key"` and an explicit named `/var/lib/tailscale` volume. Each invocation starts `tailscaled` in userspace-networking mode inside the pinned container, waits for its socket, performs the fixed `tailscale --socket=/var/run/tailscale/tailscaled.sock up --auth-key="$TS_AUTHKEY" --reset` bootstrap, then executes the allow-listed `tailscale` argv through that socket. This avoids requiring host TUN devices or kernel capabilities; the state volume preserves the node identity. The entry remains limited to the catalog's read-only `version`, `status`, and `netcheck` commands.
94
+
95
+ ### Hosted CLI Authentication
96
+
97
+ MCP and CLI credentials share the encrypted `CredentialPayload` storage format and a common materialization layer:
98
+
99
+ - `env` credentials pass variables through, or select them with `env:<name>` bindings.
100
+ - `bearer` credentials bind with `token`.
101
+ - `api-key` credentials bind with `value`.
102
+ - `headers` credentials bind with `header:<Header-Name>`.
103
+ - `oauth` credentials bind with `accessToken` after authorization.
104
+
105
+ The MCP side still owns OAuth provider discovery, refresh, and callback handling. The CLI side only consumes the stored access token; it does not implement a second OAuth transport.
106
+
107
+ ### Install Location
108
+
109
+ Home-stdio packages, Go binaries, and GitHub Release binaries install to `TOOLHOME_MARKET_DIR` (default `<dataDir>/market`), which is a persistent Docker volume. Packages survive container restarts. Uvx tools and caches install to `<dataDir>/.uv`, also persistent.
110
+
111
+ ### Installation Progress
112
+
113
+ - CLI: shows `installing: npm install <package>...` with live progress
114
+ - Web console: InstallSheet shows spinner + current step + live npm output log
115
+ - API: `POST /market/:id/install` returns a `jobId`; `GET /market/install/:jobId` polls status
116
+
117
+ ## Adding Custom Servers (outside Market)
118
+
119
+ For servers not in the catalog, use the standard CLI:
120
+
121
+ ```bash
122
+ echo '{"name":"custom","payload":{"type":"bearer","token":"xxx"}}' | toolhome credential add -
123
+ echo '{"slug":"custom","name":"Custom","kind":"remote","transport":{"type":"streamable-http","url":"https://example.com/mcp"},"credentialId":"<id>","enabled":true}' | toolhome server add -
124
+ ```
125
+
126
+ ## Market Catalog Source
127
+
128
+ The catalog is defined in `src/market/catalog.ts` (bundled). To add entries, edit this file and redeploy. A remote catalog URL (`TOOLHOME_MARKET_URL`) is planned for future updates without redeployment.
@@ -0,0 +1,96 @@
1
+ # OAuth Guide
2
+
3
+ ## Two Registration Methods
4
+
5
+ ToolHome supports two OAuth client registration methods for upstream MCP servers:
6
+
7
+ 1. **URL-based Client Metadata** (RFC 9728, default): ToolHome publishes a client metadata document at `https://<public-url>/oauth/upstream/client/<credential-id>`. The upstream authorization server fetches this document to register the client.
8
+
9
+ 2. **Dynamic Client Registration (DCR)**: ToolHome POSTs client metadata to the upstream's `registration_endpoint`. The server returns a client ID.
10
+
11
+ The method is controlled by `settings.urlClientId` per server:
12
+
13
+ - `true` (default) = URL-based
14
+ - `false` = DCR
15
+ - `undefined` = inherit global `TOOLHOME_OAUTH_URL_CLIENT_ID` (default `true`)
16
+
17
+ ## Per-Server Configuration
18
+
19
+ Set via CLI:
20
+
21
+ ```bash
22
+ echo '{"settings":{"urlClientId":false}}' | toolhome server update <id> -
23
+ ```
24
+
25
+ Or via the web console: Server edit form -> "OAuth client registration" dropdown.
26
+
27
+ ## Provider Compatibility
28
+
29
+ | Provider | Recommended Method | Notes |
30
+ | -------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
31
+ | **Cloudflare** | DCR (`urlClientId: false`) | URL-based fails: 503 fetching from proxied origin |
32
+ | **Notion** | DCR (`urlClientId: false`) | URL-based fails: "Invalid client" |
33
+ | **Linear** | DCR (`urlClientId: false`) | URL-based fails: "Invalid client" |
34
+ | **GitHub** | PAT (not OAuth) | OAuth server doesn't support DCR; metadata discovery requires auth. Use a Personal Access Token as a bearer credential |
35
+ | **Slack** | URL-based (default) | No DCR endpoint; URL-based should work |
36
+ | **Stripe** | URL-based (default) | No DCR endpoint |
37
+ | **Figma** | URL-based (default) | No DCR endpoint |
38
+ | **Sentry** | URL-based (default) | No DCR endpoint |
39
+ | **Supabase** | URL-based (default) | No DCR endpoint |
40
+ | **Vercel** | Not compatible | Only approves localhost callbacks; incompatible with remote gateway |
41
+
42
+ ## Authorization Flow
43
+
44
+ ```bash
45
+ toolhome credential authorize <name>
46
+ ```
47
+
48
+ 1. Resolves credential by name (or ID)
49
+ 2. Calls `POST /api/v1/credentials/:id/authorize`
50
+ 3. Opens the authorization URL in the browser
51
+ 4. Waits, polling credential status every 2s
52
+ 5. Reports success, failure, or timeout (default 600s)
53
+
54
+ ### Force Re-authorization
55
+
56
+ ```bash
57
+ toolhome credential authorize <name> --force
58
+ ```
59
+
60
+ Clears the stored client information and re-registers. Use when switching registration methods or changing accounts.
61
+
62
+ ## Troubleshooting OAuth
63
+
64
+ ### "Incompatible auth server: does not support dynamic client registration"
65
+
66
+ The upstream doesn't have a `registration_endpoint`. Switch to URL-based:
67
+
68
+ ```bash
69
+ echo '{"settings":{"urlClientId":true}}' | toolhome server update <id> -
70
+ ```
71
+
72
+ ### "Invalid client. The clientId provided does not match."
73
+
74
+ URL-based metadata was rejected by the upstream. Switch to DCR:
75
+
76
+ ```bash
77
+ echo '{"settings":{"urlClientId":false}}' | toolhome server update <id> -
78
+ ```
79
+
80
+ ### "invalid_redirect_uri"
81
+
82
+ The upstream only approves specific redirect URIs (e.g., Vercel only allows `http://localhost:<port>/callback`). This is incompatible with a remote gateway. Use a PAT or keep the MCP in the harness locally.
83
+
84
+ ### OAuth metadata fetch fails (503)
85
+
86
+ If the ToolHome domain is behind a CDN/proxy (e.g., Cloudflare), the upstream's auth server may fail to fetch the client metadata document. Fix: use DNS-only (grey cloud) for the domain, or switch to DCR.
87
+
88
+ ### Credential shows "Expired"
89
+
90
+ OAuth access tokens expire (typically 1 hour). ToolHome refreshes them lazily on connection. The web console auto-refreshes expired OAuth credentials on page load. Manual refresh:
91
+
92
+ ```bash
93
+ toolhome credential test <id>
94
+ # or
95
+ toolhome server refresh <server-id>
96
+ ```