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.
- package/LICENSE +21 -0
- package/README.md +295 -0
- package/README.zh.md +281 -0
- package/dist/server/app.d.ts +16 -0
- package/dist/server/app.d.ts.map +1 -0
- package/dist/server/app.js +166 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/cli/main.d.ts +3 -0
- package/dist/server/cli/main.d.ts.map +1 -0
- package/dist/server/cli/main.js +587 -0
- package/dist/server/cli/main.js.map +1 -0
- package/dist/server/cli-plane/allow-list.d.ts +18 -0
- package/dist/server/cli-plane/allow-list.d.ts.map +1 -0
- package/dist/server/cli-plane/allow-list.js +28 -0
- package/dist/server/cli-plane/allow-list.js.map +1 -0
- package/dist/server/cli-plane/cli-service.d.ts +41 -0
- package/dist/server/cli-plane/cli-service.d.ts.map +1 -0
- package/dist/server/cli-plane/cli-service.js +282 -0
- package/dist/server/cli-plane/cli-service.js.map +1 -0
- package/dist/server/cli-plane/frames.d.ts +26 -0
- package/dist/server/cli-plane/frames.d.ts.map +1 -0
- package/dist/server/cli-plane/frames.js +12 -0
- package/dist/server/cli-plane/frames.js.map +1 -0
- package/dist/server/cli-plane/models.d.ts +174 -0
- package/dist/server/cli-plane/models.d.ts.map +1 -0
- package/dist/server/cli-plane/models.js +169 -0
- package/dist/server/cli-plane/models.js.map +1 -0
- package/dist/server/cli-plane/routes.d.ts +32 -0
- package/dist/server/cli-plane/routes.d.ts.map +1 -0
- package/dist/server/cli-plane/routes.js +94 -0
- package/dist/server/cli-plane/routes.js.map +1 -0
- package/dist/server/cli-plane/runner.d.ts +41 -0
- package/dist/server/cli-plane/runner.d.ts.map +1 -0
- package/dist/server/cli-plane/runner.js +163 -0
- package/dist/server/cli-plane/runner.js.map +1 -0
- package/dist/server/cli-plane/status.d.ts +12 -0
- package/dist/server/cli-plane/status.d.ts.map +1 -0
- package/dist/server/cli-plane/status.js +24 -0
- package/dist/server/cli-plane/status.js.map +1 -0
- package/dist/server/config/harness-import.d.ts +44 -0
- package/dist/server/config/harness-import.d.ts.map +1 -0
- package/dist/server/config/harness-import.js +166 -0
- package/dist/server/config/harness-import.js.map +1 -0
- package/dist/server/config.d.ts +20 -0
- package/dist/server/config.d.ts.map +1 -0
- package/dist/server/config.js +80 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/control/client.d.ts +24 -0
- package/dist/server/control/client.d.ts.map +1 -0
- package/dist/server/control/client.js +115 -0
- package/dist/server/control/client.js.map +1 -0
- package/dist/server/control/control-api.d.ts +39 -0
- package/dist/server/control/control-api.d.ts.map +1 -0
- package/dist/server/control/control-api.js +291 -0
- package/dist/server/control/control-api.js.map +1 -0
- package/dist/server/control/control-service.d.ts +713 -0
- package/dist/server/control/control-service.d.ts.map +1 -0
- package/dist/server/control/control-service.js +693 -0
- package/dist/server/control/control-service.js.map +1 -0
- package/dist/server/control/openapi.d.ts +2 -0
- package/dist/server/control/openapi.d.ts.map +1 -0
- package/dist/server/control/openapi.js +370 -0
- package/dist/server/control/openapi.js.map +1 -0
- package/dist/server/data-plane/data-plane.d.ts +20 -0
- package/dist/server/data-plane/data-plane.d.ts.map +1 -0
- package/dist/server/data-plane/data-plane.js +335 -0
- package/dist/server/data-plane/data-plane.js.map +1 -0
- package/dist/server/data-plane/gateway-server.d.ts +13 -0
- package/dist/server/data-plane/gateway-server.d.ts.map +1 -0
- package/dist/server/data-plane/gateway-server.js +796 -0
- package/dist/server/data-plane/gateway-server.js.map +1 -0
- package/dist/server/data-plane/legacy-session.d.ts +18 -0
- package/dist/server/data-plane/legacy-session.d.ts.map +1 -0
- package/dist/server/data-plane/legacy-session.js +137 -0
- package/dist/server/data-plane/legacy-session.js.map +1 -0
- package/dist/server/data-plane/projection.d.ts +18 -0
- package/dist/server/data-plane/projection.d.ts.map +1 -0
- package/dist/server/data-plane/projection.js +44 -0
- package/dist/server/data-plane/projection.js.map +1 -0
- package/dist/server/data-plane/registry.d.ts +31 -0
- package/dist/server/data-plane/registry.d.ts.map +1 -0
- package/dist/server/data-plane/registry.js +129 -0
- package/dist/server/data-plane/registry.js.map +1 -0
- package/dist/server/data-plane/task-extension.d.ts +7 -0
- package/dist/server/data-plane/task-extension.d.ts.map +1 -0
- package/dist/server/data-plane/task-extension.js +33 -0
- package/dist/server/data-plane/task-extension.js.map +1 -0
- package/dist/server/data-plane/virtualization.d.ts +35 -0
- package/dist/server/data-plane/virtualization.d.ts.map +1 -0
- package/dist/server/data-plane/virtualization.js +228 -0
- package/dist/server/data-plane/virtualization.js.map +1 -0
- package/dist/server/domain/errors.d.ts +9 -0
- package/dist/server/domain/errors.d.ts.map +1 -0
- package/dist/server/domain/errors.js +19 -0
- package/dist/server/domain/errors.js.map +1 -0
- package/dist/server/domain/models.d.ts +720 -0
- package/dist/server/domain/models.d.ts.map +1 -0
- package/dist/server/domain/models.js +327 -0
- package/dist/server/domain/models.js.map +1 -0
- package/dist/server/main.d.ts +2 -0
- package/dist/server/main.d.ts.map +1 -0
- package/dist/server/main.js +37 -0
- package/dist/server/main.js.map +1 -0
- package/dist/server/manage/management-mcp.d.ts +27 -0
- package/dist/server/manage/management-mcp.d.ts.map +1 -0
- package/dist/server/manage/management-mcp.js +431 -0
- package/dist/server/manage/management-mcp.js.map +1 -0
- package/dist/server/market/catalog.d.ts +111 -0
- package/dist/server/market/catalog.d.ts.map +1 -0
- package/dist/server/market/catalog.js +784 -0
- package/dist/server/market/catalog.js.map +1 -0
- package/dist/server/market/installers.d.ts +64 -0
- package/dist/server/market/installers.d.ts.map +1 -0
- package/dist/server/market/installers.js +181 -0
- package/dist/server/market/installers.js.map +1 -0
- package/dist/server/market/market-service.d.ts +146 -0
- package/dist/server/market/market-service.d.ts.map +1 -0
- package/dist/server/market/market-service.js +930 -0
- package/dist/server/market/market-service.js.map +1 -0
- package/dist/server/observability/call-recorder.d.ts +22 -0
- package/dist/server/observability/call-recorder.d.ts.map +1 -0
- package/dist/server/observability/call-recorder.js +99 -0
- package/dist/server/observability/call-recorder.js.map +1 -0
- package/dist/server/observability/logger.d.ts +16 -0
- package/dist/server/observability/logger.d.ts.map +1 -0
- package/dist/server/observability/logger.js +25 -0
- package/dist/server/observability/logger.js.map +1 -0
- package/dist/server/security/api-keys.d.ts +15 -0
- package/dist/server/security/api-keys.d.ts.map +1 -0
- package/dist/server/security/api-keys.js +35 -0
- package/dist/server/security/api-keys.js.map +1 -0
- package/dist/server/security/auth-service.d.ts +26 -0
- package/dist/server/security/auth-service.d.ts.map +1 -0
- package/dist/server/security/auth-service.js +76 -0
- package/dist/server/security/auth-service.js.map +1 -0
- package/dist/server/security/control-session.d.ts +9 -0
- package/dist/server/security/control-session.d.ts.map +1 -0
- package/dist/server/security/control-session.js +51 -0
- package/dist/server/security/control-session.js.map +1 -0
- package/dist/server/security/credential-materializer.d.ts +20 -0
- package/dist/server/security/credential-materializer.d.ts.map +1 -0
- package/dist/server/security/credential-materializer.js +82 -0
- package/dist/server/security/credential-materializer.js.map +1 -0
- package/dist/server/security/cursor-codec.d.ts +13 -0
- package/dist/server/security/cursor-codec.d.ts.map +1 -0
- package/dist/server/security/cursor-codec.js +43 -0
- package/dist/server/security/cursor-codec.js.map +1 -0
- package/dist/server/security/oauth-routes.d.ts +21 -0
- package/dist/server/security/oauth-routes.d.ts.map +1 -0
- package/dist/server/security/oauth-routes.js +100 -0
- package/dist/server/security/oauth-routes.js.map +1 -0
- package/dist/server/security/oauth-server.d.ts +16 -0
- package/dist/server/security/oauth-server.d.ts.map +1 -0
- package/dist/server/security/oauth-server.js +544 -0
- package/dist/server/security/oauth-server.js.map +1 -0
- package/dist/server/security/secret-box.d.ts +7 -0
- package/dist/server/security/secret-box.d.ts.map +1 -0
- package/dist/server/security/secret-box.js +27 -0
- package/dist/server/security/secret-box.js.map +1 -0
- package/dist/server/security/secure-action.d.ts +21 -0
- package/dist/server/security/secure-action.d.ts.map +1 -0
- package/dist/server/security/secure-action.js +69 -0
- package/dist/server/security/secure-action.js.map +1 -0
- package/dist/server/storage/sqlite-store.d.ts +70 -0
- package/dist/server/storage/sqlite-store.d.ts.map +1 -0
- package/dist/server/storage/sqlite-store.js +1522 -0
- package/dist/server/storage/sqlite-store.js.map +1 -0
- package/dist/server/storage/store.d.ts +82 -0
- package/dist/server/storage/store.d.ts.map +1 -0
- package/dist/server/storage/store.js +2 -0
- package/dist/server/storage/store.js.map +1 -0
- package/dist/server/upstream/adapter.d.ts +52 -0
- package/dist/server/upstream/adapter.d.ts.map +1 -0
- package/dist/server/upstream/adapter.js +807 -0
- package/dist/server/upstream/adapter.js.map +1 -0
- package/dist/server/upstream/credential-resolver.d.ts +14 -0
- package/dist/server/upstream/credential-resolver.d.ts.map +1 -0
- package/dist/server/upstream/credential-resolver.js +56 -0
- package/dist/server/upstream/credential-resolver.js.map +1 -0
- package/dist/server/upstream/extension-transport.d.ts +17 -0
- package/dist/server/upstream/extension-transport.d.ts.map +1 -0
- package/dist/server/upstream/extension-transport.js +207 -0
- package/dist/server/upstream/extension-transport.js.map +1 -0
- package/dist/server/upstream/manager.d.ts +23 -0
- package/dist/server/upstream/manager.d.ts.map +1 -0
- package/dist/server/upstream/manager.js +324 -0
- package/dist/server/upstream/manager.js.map +1 -0
- package/dist/server/upstream/oauth-provider.d.ts +38 -0
- package/dist/server/upstream/oauth-provider.d.ts.map +1 -0
- package/dist/server/upstream/oauth-provider.js +225 -0
- package/dist/server/upstream/oauth-provider.js.map +1 -0
- package/dist/server/upstream/oauth-refresh.d.ts +65 -0
- package/dist/server/upstream/oauth-refresh.d.ts.map +1 -0
- package/dist/server/upstream/oauth-refresh.js +240 -0
- package/dist/server/upstream/oauth-refresh.js.map +1 -0
- package/dist/server/upstream/oauth-routes.d.ts +17 -0
- package/dist/server/upstream/oauth-routes.d.ts.map +1 -0
- package/dist/server/upstream/oauth-routes.js +87 -0
- package/dist/server/upstream/oauth-routes.js.map +1 -0
- package/dist/server/upstream/oauth-service.d.ts +143 -0
- package/dist/server/upstream/oauth-service.d.ts.map +1 -0
- package/dist/server/upstream/oauth-service.js +217 -0
- package/dist/server/upstream/oauth-service.js.map +1 -0
- package/dist/server/upstream/stable-json.d.ts +3 -0
- package/dist/server/upstream/stable-json.d.ts.map +1 -0
- package/dist/server/upstream/stable-json.js +17 -0
- package/dist/server/upstream/stable-json.js.map +1 -0
- package/package.json +69 -0
- package/skills/toolhome/SKILL.md +238 -0
- package/skills/toolhome/references/cli-reference.md +242 -0
- package/skills/toolhome/references/deployment.md +113 -0
- package/skills/toolhome/references/market-guide.md +128 -0
- package/skills/toolhome/references/oauth-guide.md +96 -0
- package/skills/toolhome/references/troubleshooting.md +133 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { assertSecureTokenEndpoint, auth, } from '@modelcontextprotocol/client';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { AppError } from '../domain/errors.js';
|
|
4
|
+
import { StoredOAuthProvider } from './oauth-provider.js';
|
|
5
|
+
const beginInputSchema = z.object({
|
|
6
|
+
serverId: z.uuid().optional(),
|
|
7
|
+
force: z.boolean().default(false),
|
|
8
|
+
});
|
|
9
|
+
export class UpstreamOAuthService {
|
|
10
|
+
#store;
|
|
11
|
+
#publicUrl;
|
|
12
|
+
#upstreams;
|
|
13
|
+
#logger;
|
|
14
|
+
#urlClientId;
|
|
15
|
+
constructor(store, publicUrl, upstreams, logger, urlClientId = true) {
|
|
16
|
+
this.#store = store;
|
|
17
|
+
this.#publicUrl = publicUrl;
|
|
18
|
+
this.#upstreams = upstreams;
|
|
19
|
+
this.#logger = logger;
|
|
20
|
+
this.#urlClientId = urlClientId;
|
|
21
|
+
}
|
|
22
|
+
#provider(credentialId, urlClientId = this.#urlClientId) {
|
|
23
|
+
return new StoredOAuthProvider(this.#store, credentialId, this.#publicUrl, {
|
|
24
|
+
urlClientId,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
metadata(credentialId) {
|
|
28
|
+
return this.#provider(credentialId).clientMetadata;
|
|
29
|
+
}
|
|
30
|
+
async begin(credentialId, value = {}) {
|
|
31
|
+
const input = beginInputSchema.parse(value);
|
|
32
|
+
const server = this.#serverFor(credentialId, input.serverId);
|
|
33
|
+
const provider = this.#provider(credentialId, server.settings.urlClientId ?? this.#urlClientId);
|
|
34
|
+
const payload = this.#oauthPayload(credentialId);
|
|
35
|
+
if (input.force) {
|
|
36
|
+
// A forced flow starts over: discard any previously registered client so
|
|
37
|
+
// the next authorization re-registers under the current configuration
|
|
38
|
+
// (e.g. DCR instead of URL-based client metadata).
|
|
39
|
+
provider.invalidateCredentials('client');
|
|
40
|
+
}
|
|
41
|
+
const result = await auth(provider, {
|
|
42
|
+
serverUrl: server.transport.url,
|
|
43
|
+
...(payload.scope === undefined ? {} : { scope: payload.scope }),
|
|
44
|
+
...(input.force ? { forceReauthorization: true } : {}),
|
|
45
|
+
});
|
|
46
|
+
if (result === 'AUTHORIZED') {
|
|
47
|
+
const connection = await this.#reload(server);
|
|
48
|
+
return {
|
|
49
|
+
status: 'authorized',
|
|
50
|
+
credential: this.#store.getCredential(credentialId),
|
|
51
|
+
serverId: server.id,
|
|
52
|
+
connection,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const authorizationUrl = provider.authorizationUrl();
|
|
56
|
+
if (!authorizationUrl) {
|
|
57
|
+
throw new AppError('oauth_redirect_missing', 'Authorization server did not return a redirect', 502);
|
|
58
|
+
}
|
|
59
|
+
await this.#upstreams.invalidate(server.id);
|
|
60
|
+
this.#store.appendEvent({
|
|
61
|
+
level: 'info',
|
|
62
|
+
type: 'credential.authorization_started',
|
|
63
|
+
serverId: server.id,
|
|
64
|
+
message: `Started OAuth authorization for ${server.slug}`,
|
|
65
|
+
detail: { credentialId },
|
|
66
|
+
});
|
|
67
|
+
return {
|
|
68
|
+
status: 'authorization-required',
|
|
69
|
+
credential: this.#store.getCredential(credentialId),
|
|
70
|
+
serverId: server.id,
|
|
71
|
+
authorizationUrl,
|
|
72
|
+
callbackUrl: provider.redirectUrl.toString(),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
async callback(credentialId, input) {
|
|
76
|
+
const server = this.#serverFor(credentialId);
|
|
77
|
+
const provider = this.#provider(credentialId, server.settings.urlClientId ?? this.#urlClientId);
|
|
78
|
+
provider.validateCallbackState(input.state);
|
|
79
|
+
const payload = this.#oauthPayload(credentialId);
|
|
80
|
+
const result = await auth(provider, {
|
|
81
|
+
serverUrl: server.transport.url,
|
|
82
|
+
authorizationCode: input.code,
|
|
83
|
+
...(input.iss === undefined ? {} : { iss: input.iss }),
|
|
84
|
+
...(payload.scope === undefined ? {} : { scope: payload.scope }),
|
|
85
|
+
});
|
|
86
|
+
if (result !== 'AUTHORIZED') {
|
|
87
|
+
throw new AppError('oauth_exchange_incomplete', 'OAuth authorization did not complete', 502);
|
|
88
|
+
}
|
|
89
|
+
const connection = await this.#reload(server);
|
|
90
|
+
this.#store.appendEvent({
|
|
91
|
+
level: 'info',
|
|
92
|
+
type: 'credential.authorized',
|
|
93
|
+
serverId: server.id,
|
|
94
|
+
message: `Authorized ${server.slug}`,
|
|
95
|
+
detail: { credentialId },
|
|
96
|
+
});
|
|
97
|
+
return {
|
|
98
|
+
status: 'authorized',
|
|
99
|
+
server,
|
|
100
|
+
credential: this.#store.getCredential(credentialId),
|
|
101
|
+
connection,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
async revoke(credentialId) {
|
|
105
|
+
const server = this.#serverFor(credentialId);
|
|
106
|
+
const provider = this.#provider(credentialId, server.settings.urlClientId ?? this.#urlClientId);
|
|
107
|
+
const payload = this.#oauthPayload(credentialId);
|
|
108
|
+
const metadata = payload.discoveryState?.authorizationServerMetadata;
|
|
109
|
+
const endpointValue = metadata ? Reflect.get(metadata, 'revocation_endpoint') : undefined;
|
|
110
|
+
const endpoint = typeof endpointValue === 'string' ? endpointValue : undefined;
|
|
111
|
+
const clientInformation = provider.clientInformation();
|
|
112
|
+
const attempts = [];
|
|
113
|
+
if (endpoint) {
|
|
114
|
+
assertSecureTokenEndpoint(endpoint);
|
|
115
|
+
if (payload.refreshToken) {
|
|
116
|
+
attempts.push(await this.#revokeToken(endpoint, payload.refreshToken, 'refresh_token', clientInformation));
|
|
117
|
+
}
|
|
118
|
+
if (payload.accessToken) {
|
|
119
|
+
attempts.push(await this.#revokeToken(endpoint, payload.accessToken, 'access_token', clientInformation));
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
provider.invalidateCredentials('tokens');
|
|
123
|
+
provider.invalidateCredentials('verifier');
|
|
124
|
+
await this.#upstreams.invalidate(server.id);
|
|
125
|
+
this.#store.appendEvent({
|
|
126
|
+
level: attempts.some((attempt) => !attempt.ok) ? 'warn' : 'info',
|
|
127
|
+
type: 'credential.revoked',
|
|
128
|
+
serverId: server.id,
|
|
129
|
+
message: `Revoked OAuth credential for ${server.slug}`,
|
|
130
|
+
detail: { credentialId, remoteRevocationSupported: endpoint !== undefined, attempts },
|
|
131
|
+
});
|
|
132
|
+
return {
|
|
133
|
+
revoked: true,
|
|
134
|
+
remoteRevocationSupported: endpoint !== undefined,
|
|
135
|
+
remoteRevoked: attempts.length > 0 && attempts.every((attempt) => attempt.ok),
|
|
136
|
+
attempts,
|
|
137
|
+
credential: this.#store.getCredential(credentialId),
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
async #revokeToken(endpoint, token, tokenType, clientInformation) {
|
|
141
|
+
const body = new URLSearchParams({ token, token_type_hint: tokenType });
|
|
142
|
+
const headers = new Headers({ 'content-type': 'application/x-www-form-urlencoded' });
|
|
143
|
+
if (clientInformation?.client_secret) {
|
|
144
|
+
const user = formEncode(clientInformation.client_id);
|
|
145
|
+
const password = formEncode(clientInformation.client_secret);
|
|
146
|
+
headers.set('authorization', `Basic ${Buffer.from(`${user}:${password}`).toString('base64')}`);
|
|
147
|
+
}
|
|
148
|
+
else if (clientInformation) {
|
|
149
|
+
body.set('client_id', clientInformation.client_id);
|
|
150
|
+
}
|
|
151
|
+
try {
|
|
152
|
+
const response = await fetch(endpoint, {
|
|
153
|
+
method: 'POST',
|
|
154
|
+
headers,
|
|
155
|
+
body,
|
|
156
|
+
redirect: 'error',
|
|
157
|
+
signal: AbortSignal.timeout(10_000),
|
|
158
|
+
});
|
|
159
|
+
return { tokenType, ok: response.ok, status: response.status };
|
|
160
|
+
}
|
|
161
|
+
catch (error) {
|
|
162
|
+
this.#logger.warn('Remote OAuth token revocation failed', {
|
|
163
|
+
error: error instanceof Error ? error.message : String(error),
|
|
164
|
+
});
|
|
165
|
+
return { tokenType, ok: false };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
#serverFor(credentialId, serverId) {
|
|
169
|
+
const candidates = this.#store
|
|
170
|
+
.listServers()
|
|
171
|
+
.filter((server) => server.credentialId === credentialId);
|
|
172
|
+
const server = serverId
|
|
173
|
+
? candidates.find((candidate) => candidate.id === serverId)
|
|
174
|
+
: candidates.length === 1
|
|
175
|
+
? candidates[0]
|
|
176
|
+
: undefined;
|
|
177
|
+
if (!server) {
|
|
178
|
+
throw new AppError('oauth_server_required', candidates.length === 0
|
|
179
|
+
? 'Attach the OAuth credential to a remote MCP server first'
|
|
180
|
+
: 'OAuth credential must be attached to exactly one remote MCP server', 400);
|
|
181
|
+
}
|
|
182
|
+
if (server.transport.type !== 'streamable-http') {
|
|
183
|
+
throw new AppError('oauth_remote_only', 'OAuth is only available for remote MCP servers', 400);
|
|
184
|
+
}
|
|
185
|
+
return { ...server, transport: server.transport };
|
|
186
|
+
}
|
|
187
|
+
#oauthPayload(credentialId) {
|
|
188
|
+
const payload = this.#store.getCredentialPayload(credentialId);
|
|
189
|
+
if (!payload || payload.type !== 'oauth') {
|
|
190
|
+
throw new AppError('oauth_credential_invalid', 'Credential is not OAuth', 400);
|
|
191
|
+
}
|
|
192
|
+
return payload;
|
|
193
|
+
}
|
|
194
|
+
async #reload(server) {
|
|
195
|
+
await this.#upstreams.remove(server.id);
|
|
196
|
+
if (!server.enabled)
|
|
197
|
+
return { refreshed: false, reason: 'server-disabled' };
|
|
198
|
+
try {
|
|
199
|
+
await this.#upstreams.refresh(server.id);
|
|
200
|
+
return { refreshed: true };
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
this.#logger.warn('OAuth completed but the upstream MCP refresh failed', {
|
|
204
|
+
serverId: server.id,
|
|
205
|
+
error: error instanceof Error ? error.message : String(error),
|
|
206
|
+
});
|
|
207
|
+
return {
|
|
208
|
+
refreshed: false,
|
|
209
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
function formEncode(value) {
|
|
215
|
+
return new URLSearchParams({ value }).toString().slice('value='.length);
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=oauth-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth-service.js","sourceRoot":"","sources":["../../../src/upstream/oauth-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stable-json.d.ts","sourceRoot":"","sources":["../../../src/upstream/stable-json.ts"],"names":[],"mappings":"AAYA,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEjD;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAElD"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
function normalize(value) {
|
|
3
|
+
if (Array.isArray(value))
|
|
4
|
+
return value.map(normalize);
|
|
5
|
+
if (value === null || typeof value !== 'object')
|
|
6
|
+
return value;
|
|
7
|
+
return Object.fromEntries(Object.entries(value)
|
|
8
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
9
|
+
.map(([key, item]) => [key, normalize(item)]));
|
|
10
|
+
}
|
|
11
|
+
export function stableJson(value) {
|
|
12
|
+
return JSON.stringify(normalize(value));
|
|
13
|
+
}
|
|
14
|
+
export function fingerprint(value) {
|
|
15
|
+
return createHash('sha256').update(stableJson(value)).digest('hex');
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=stable-json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stable-json.js","sourceRoot":"","sources":["../../../src/upstream/stable-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,SAAS,SAAS,CAAC,KAAc;IAC/B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACtD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC9D,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;SAClB,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;SACpD,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAChD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACtE,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "toolhome",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Self-hosted home for remote-native and home-hosted MCP servers and CLIs.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"toolhome": "./dist/server/cli/main.js",
|
|
8
|
+
"mcp-home": "./dist/server/cli/main.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"skills/toolhome"
|
|
13
|
+
],
|
|
14
|
+
"keywords": [
|
|
15
|
+
"mcp",
|
|
16
|
+
"model-context-protocol",
|
|
17
|
+
"gateway",
|
|
18
|
+
"proxy",
|
|
19
|
+
"oauth",
|
|
20
|
+
"cli",
|
|
21
|
+
"self-hosted"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=24.0.0"
|
|
26
|
+
},
|
|
27
|
+
"allowScripts": {
|
|
28
|
+
"esbuild@0.28.2": true,
|
|
29
|
+
"fsevents": false
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "npm run build:server && npm run build:web",
|
|
33
|
+
"build:server": "tsc -p tsconfig.build.json",
|
|
34
|
+
"build:web": "npm --prefix web run build",
|
|
35
|
+
"dev:web": "npm --prefix web run dev",
|
|
36
|
+
"prepublishOnly": "npm run build:server",
|
|
37
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
38
|
+
"dev": "tsx watch src/main.ts",
|
|
39
|
+
"start": "node dist/server/main.js",
|
|
40
|
+
"cli": "tsx src/cli/main.ts",
|
|
41
|
+
"format": "prettier --write .",
|
|
42
|
+
"format:check": "prettier --check .",
|
|
43
|
+
"test": "vitest run --config vitest.config.ts",
|
|
44
|
+
"test:unit": "vitest run --config vitest.config.ts tests/unit",
|
|
45
|
+
"test:integration": "vitest run --config vitest.config.ts tests/integration",
|
|
46
|
+
"test:real": "tsx scripts/real-environment.ts",
|
|
47
|
+
"test:all": "npm run format:check && npm run build && npm run test && npm run test:real",
|
|
48
|
+
"smoke:market": "tsx scripts/smoke-market.ts"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@hono/node-server": "2.1.0",
|
|
52
|
+
"@modelcontextprotocol/client": "2.0.0",
|
|
53
|
+
"@modelcontextprotocol/core": "2.0.0",
|
|
54
|
+
"@modelcontextprotocol/hono": "2.0.0",
|
|
55
|
+
"@modelcontextprotocol/server": "2.0.0",
|
|
56
|
+
"commander": "15.0.0",
|
|
57
|
+
"hono": "4.13.1",
|
|
58
|
+
"jose": "6.2.8",
|
|
59
|
+
"zod": "4.4.3"
|
|
60
|
+
},
|
|
61
|
+
"devDependencies": {
|
|
62
|
+
"@types/node": "26.2.0",
|
|
63
|
+
"prettier": "3.9.6",
|
|
64
|
+
"tsx": "4.23.11",
|
|
65
|
+
"typescript": "7.0.2",
|
|
66
|
+
"vite": "8.2.1",
|
|
67
|
+
"vitest": "4.1.10"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: toolhome
|
|
3
|
+
description: >
|
|
4
|
+
Deploy and manage a self-hosted ToolHome instance: an MCP and Hosted CLI
|
|
5
|
+
control plane that aggregates upstream capabilities and platform CLIs. Use
|
|
6
|
+
when the user wants to set up ToolHome, manage MCP servers or hosted CLIs,
|
|
7
|
+
reuse encrypted credentials, authorize OAuth upstreams, install from the
|
|
8
|
+
Market catalog, configure harnesses (Claude Code, Cursor, Codex, Grok), or
|
|
9
|
+
troubleshoot ToolHome. Covers CLI, web console, Docker deployment, and CI/CD.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ToolHome
|
|
13
|
+
|
|
14
|
+
ToolHome is a single-user, self-hosted control plane for MCP servers and Hosted platform CLIs. Manage upstream MCP servers, CLIs such as Azure `az`, GitHub `gh`, and Tailscale, and encrypted credentials in one place.
|
|
15
|
+
|
|
16
|
+
## When to Use This Skill
|
|
17
|
+
|
|
18
|
+
- User wants to deploy or manage the MCP or Hosted CLI plane
|
|
19
|
+
- User wants to aggregate multiple MCP servers behind one URL
|
|
20
|
+
- User wants to run platform CLIs such as Azure `az`, GitHub `gh`, Tailscale, Cloudflare `wrangler`, Vercel, Lark `lark-cli`, Firecrawl, or Aliyun `aliyun` remotely
|
|
21
|
+
- User needs to authorize OAuth for MCP upstreams or reuse access tokens in a CLI
|
|
22
|
+
- User wants to install MCP servers or hosted CLIs from the Market catalog
|
|
23
|
+
- User wants to connect Claude Code, Cursor, Codex, or Grok to a self-hosted MCP gateway
|
|
24
|
+
- User is troubleshooting ToolHome (status, OAuth, connectivity)
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
ToolHome has three components. Install what you need:
|
|
29
|
+
|
|
30
|
+
### 1. Deploy the server (Docker)
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
docker run -d \
|
|
34
|
+
--name toolhome \
|
|
35
|
+
-p 3344:3344 \
|
|
36
|
+
-v toolhome-data:/data \
|
|
37
|
+
-e TOOLHOME_MASTER_KEY="$(openssl rand -base64 48)" \
|
|
38
|
+
-e TOOLHOME_BOOTSTRAP_CONTROL_KEY="tch_ctl_$(openssl rand -base64 32 | tr '+/' '-_' | tr -d '=')" \
|
|
39
|
+
-e TOOLHOME_PUBLIC_URL="https://tool.cyncyn.xyz" \
|
|
40
|
+
ghcr.io/crayonlu/toolhome:latest
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Or with Docker Compose (see `references/deployment.md`). After startup, open the web console at `TOOLHOME_PUBLIC_URL` and sign in with the bootstrap Control Key.
|
|
44
|
+
|
|
45
|
+
### 2. Install the CLI (npm)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm install -g toolhome
|
|
49
|
+
toolhome auth login --url https://tool.cyncyn.xyz --control-key "$TOOLHOME_CONTROL_KEY"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The CLI manages MCP servers and hosted CLIs as equal planes, plus shared credentials, OAuth, Market, and diagnostics. It requires Node.js 24 or later. The npm package also includes this skill under `skills/toolhome`.
|
|
53
|
+
|
|
54
|
+
### 3. Install this skill (for AI agents)
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npx skills add crayonlu/toolhome -g -y
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Teaches the agent all CLI commands, OAuth flows, Market installation, and troubleshooting.
|
|
61
|
+
|
|
62
|
+
## CLI Quick Reference
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
toolhome status # overview
|
|
66
|
+
toolhome doctor # health check
|
|
67
|
+
toolhome server list # list servers
|
|
68
|
+
toolhome server add ./server.json # add a server
|
|
69
|
+
toolhome cli list # list hosted CLIs
|
|
70
|
+
toolhome cli add ./cli.json # register a hosted CLI
|
|
71
|
+
toolhome cli status gh-cli # probe a hosted CLI
|
|
72
|
+
toolhome cli exec gh-cli -- --version # run remotely, stream output
|
|
73
|
+
toolhome credential list # list credentials
|
|
74
|
+
toolhome credential authorize <name> # OAuth authorization (opens browser, waits)
|
|
75
|
+
toolhome access-key create laptop # create an MCP Access Key for harnesses
|
|
76
|
+
toolhome endpoint aggregate # show the aggregate endpoint URL
|
|
77
|
+
toolhome market list # browse the Market catalog
|
|
78
|
+
toolhome market install resend # supply the secret at the one-time browser URL
|
|
79
|
+
toolhome calls list --limit 10 # recent tool calls
|
|
80
|
+
toolhome calls stats # call statistics
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Common Workflows
|
|
84
|
+
|
|
85
|
+
### Deploy ToolHome
|
|
86
|
+
|
|
87
|
+
1. Generate two random keys (master + bootstrap control, each 32+ chars)
|
|
88
|
+
2. Start the Docker container with the keys and public URL
|
|
89
|
+
3. Open the web console, sign in with the bootstrap key
|
|
90
|
+
4. Create a new Control Key, revoke bootstrap
|
|
91
|
+
5. Run `toolhome doctor` to verify health
|
|
92
|
+
|
|
93
|
+
### Add an Upstream Server
|
|
94
|
+
|
|
95
|
+
1. Create a credential:
|
|
96
|
+
```bash
|
|
97
|
+
echo '{"name":"firecrawl","payload":{"type":"bearer","token":"fc-xxx"}}' | toolhome credential add -
|
|
98
|
+
```
|
|
99
|
+
2. Get the credential ID from `toolhome credential list`
|
|
100
|
+
3. Create a server:
|
|
101
|
+
```bash
|
|
102
|
+
echo '{"slug":"firecrawl","name":"Firecrawl","kind":"remote","transport":{"type":"streamable-http","url":"https://mcp.firecrawl.dev/v2/mcp"},"credentialId":"<id>","enabled":true}' | toolhome server add -
|
|
103
|
+
```
|
|
104
|
+
4. Verify: `toolhome doctor`
|
|
105
|
+
|
|
106
|
+
### Install and Run a Hosted CLI
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
toolhome market install gh-cli
|
|
110
|
+
toolhome cli status gh-cli
|
|
111
|
+
toolhome cli exec gh-cli -- --version
|
|
112
|
+
toolhome cli exec gh-cli --stdin $'\n' -- auth login --hostname github.com --git-protocol https --web
|
|
113
|
+
toolhome cli exec gh-cli -- auth status
|
|
114
|
+
toolhome cli exec gh-cli -- repo view crayonlu/toolhome
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Give the user the device code and authorization URL for browser confirmation. Run the platform CLI only on the ToolHome server; the client needs only `toolhome`. Put platform arguments after `--`. Registry commands take the record ID; `exec` and `status` take its slug. Use `toolhome cli get <id>` to inspect allowed commands before execution. `host` runs installed binaries and `docker` runs sibling containers with explicit state volumes. Read `references/market-guide.md` for supported products and deployment prerequisites, and `references/cli-reference.md` for registry, stdin, timeout, and streaming options.
|
|
118
|
+
|
|
119
|
+
### Authorize OAuth Upstream
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
toolhome credential authorize cloudflare
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
This resolves the credential by name, opens the browser, and waits until authorization succeeds, fails, or times out (default 600s). For force re-authorization:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
toolhome credential authorize cloudflare --force
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
If OAuth fails with "Invalid client" or "Incompatible auth server", switch the registration method per-server:
|
|
132
|
+
|
|
133
|
+
- URL-based (default): works for most providers
|
|
134
|
+
- DCR: needed for Cloudflare, Notion, Linear (set `urlClientId: false` in server settings)
|
|
135
|
+
|
|
136
|
+
See `references/oauth-guide.md` for per-provider compatibility.
|
|
137
|
+
|
|
138
|
+
### Install from Market
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
toolhome market list # browse MCP and CLI entries
|
|
142
|
+
toolhome market install resend # home-stdio (npm); browser secret
|
|
143
|
+
toolhome market install context7 # remote (bearer); browser secret
|
|
144
|
+
toolhome market install deepwiki # remote (no auth)
|
|
145
|
+
toolhome market install fetch # uvx (Python, no config)
|
|
146
|
+
toolhome market install markitdown # Docker-backed MCP
|
|
147
|
+
toolhome market install gh-cli # Hosted GitHub CLI (no token; device-flow login after)
|
|
148
|
+
toolhome market uninstall resend # remove
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Market installs are async with progress: the CLI shows installer steps, the web console shows a live log. Packaged artifacts have version pins and installs write a persistent record (source, version, recipe revision). Remote services and the existing `/bin/sh` host-shell entry have no downloadable version pin. If required secrets are omitted, the CLI prints a one-time browser action URL. Use that flow for secrets; `--set` also accepts values, so reserve it for non-secret configuration.
|
|
152
|
+
|
|
153
|
+
**Docker entries** (e.g. `markitdown`) run the image as a sibling container via `docker run --rm -i <image>`: the install pulls the image, or builds it from the entry's inline Dockerfile when not pullable. The gateway container must mount the host docker socket and its runtime user must be in the host docker group (compose `group_add`, default GID 999, override with `DOCKER_GROUP_ID`). Only needed when a package cannot run inside the Alpine gateway image (e.g. `markitdown` — its `onnxruntime` dependency ships no musl wheels).
|
|
154
|
+
|
|
155
|
+
### Give an AI Agent Safe Management Access
|
|
156
|
+
|
|
157
|
+
Create an **agent-scoped control key** (web console → Settings → Control Keys, or API):
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
echo '{"name":"agent-key","scope":"agent"}' | toolhome api POST /api/v1/control-keys --body -
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Agent keys can read state and run safe operations (enable/disable/refresh/restart, market install, tool visibility) but are denied credentials, control/access keys, secret exports, and server deletion (HTTP 403). Existing keys keep full admin scope.
|
|
164
|
+
|
|
165
|
+
Point an agent's management MCP at:
|
|
166
|
+
|
|
167
|
+
```json
|
|
168
|
+
{
|
|
169
|
+
"url": "https://tool.cyncyn.xyz/manage/mcp",
|
|
170
|
+
"headers": { "Authorization": "Bearer tch_ctl_agent..." }
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The management surface exposes `home_status`, `server_list`, `server_get`, `market_search`, `tool_list`, `calls_query`, and idempotent writes `server_set_enabled`, `server_refresh`, `server_restart`, `market_install`, `tool_set_visibility`. All writes are audited in the call log. The management endpoint never appears inside the `/mcp` aggregate.
|
|
175
|
+
|
|
176
|
+
### Connect a Harness
|
|
177
|
+
|
|
178
|
+
Create an MCP Access Key:
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
toolhome access-key create laptop
|
|
182
|
+
# Returns: tch_mcp_xxx (shown once, copy it)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Configure the harness (aggregate endpoint):
|
|
186
|
+
|
|
187
|
+
```json
|
|
188
|
+
{
|
|
189
|
+
"url": "https://tool.cyncyn.xyz/mcp",
|
|
190
|
+
"headers": { "Authorization": "Bearer tch_mcp_xxx" }
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Or per-server (independent endpoint, original tool names):
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"url": "https://tool.cyncyn.xyz/mcp/firecrawl",
|
|
199
|
+
"headers": { "Authorization": "Bearer tch_mcp_xxx" }
|
|
200
|
+
}
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Aggregate tool names are `{server_slug}.{tool_name}`. Per-server preserves original names.
|
|
204
|
+
|
|
205
|
+
### Diagnose Issues
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
toolhome doctor # check all servers
|
|
209
|
+
toolhome server status <id> # detailed runtime state + last error
|
|
210
|
+
toolhome server logs <id> # recent log entries
|
|
211
|
+
toolhome cli status <slug> # hosted CLI probe
|
|
212
|
+
toolhome events --limit 100 # recent events, including cli.exec
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Configuration
|
|
216
|
+
|
|
217
|
+
| Env | Description | Default |
|
|
218
|
+
| --------------------------------- | -------------------------------------------------- | ------------------------------ |
|
|
219
|
+
| `TOOLHOME_PUBLIC_URL` | External HTTPS origin | required |
|
|
220
|
+
| `TOOLHOME_MASTER_KEY` | Secret encryption key (32+ chars) | required |
|
|
221
|
+
| `TOOLHOME_BOOTSTRAP_CONTROL_KEY` | First-boot Control Key | required on first boot |
|
|
222
|
+
| `TOOLHOME_HOST` / `TOOLHOME_PORT` | Listen address | `127.0.0.1:3344` |
|
|
223
|
+
| `TOOLHOME_DATA_DIR` | SQLite + market data | `/data` |
|
|
224
|
+
| `TOOLHOME_MARKET_DIR` | Market npm install dir | `<dataDir>/market` |
|
|
225
|
+
| `TOOLHOME_WEB_DIR` | Web console static files | disabled (set in Docker image) |
|
|
226
|
+
| `TOOLHOME_OAUTH_URL_CLIENT_ID` | Global OAuth client registration | `true` (URL-based) |
|
|
227
|
+
| `TOOLHOME_UV_INDEX_URL` | PyPI mirror for uvx Market installs | unset (pypi.org) |
|
|
228
|
+
| `TOOLHOME_CALLS_RETENTION_DAYS` | Tool call record retention in days (metadata only) | `30` |
|
|
229
|
+
|
|
230
|
+
## Deep Dives
|
|
231
|
+
|
|
232
|
+
For detailed information, read the reference files:
|
|
233
|
+
|
|
234
|
+
- `references/cli-reference.md` — Full CLI command reference with all flags
|
|
235
|
+
- `references/oauth-guide.md` — OAuth registration methods, per-provider compatibility, troubleshooting
|
|
236
|
+
- `references/market-guide.md` — Market catalog entries, installation details
|
|
237
|
+
- `references/deployment.md` — Docker Compose, CI/CD, reverse proxy, data persistence
|
|
238
|
+
- `references/troubleshooting.md` — Common issues and solutions
|