@sentropic/h2a-cli 0.23.0 → 0.25.1

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 (76) hide show
  1. package/dist/cli-contract.d.ts.map +1 -1
  2. package/dist/cli-contract.js +24 -0
  3. package/dist/cli-contract.js.map +1 -1
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +219 -1
  6. package/dist/cli.js.map +1 -1
  7. package/dist/index.d.ts +2 -1
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +1 -0
  10. package/dist/index.js.map +1 -1
  11. package/dist/mcp.d.ts +1 -1
  12. package/dist/mcp.d.ts.map +1 -1
  13. package/dist/mcp.js +1 -0
  14. package/dist/mcp.js.map +1 -1
  15. package/dist/runtime/identity/live.d.ts.map +1 -1
  16. package/dist/runtime/identity/live.js +15 -1
  17. package/dist/runtime/identity/live.js.map +1 -1
  18. package/dist/runtime/mcp/handlers.d.ts +13 -0
  19. package/dist/runtime/mcp/handlers.d.ts.map +1 -1
  20. package/dist/runtime/mcp/handlers.js +83 -1
  21. package/dist/runtime/mcp/handlers.js.map +1 -1
  22. package/dist/runtime/mcp/server.d.ts.map +1 -1
  23. package/dist/runtime/mcp/server.js +3 -1
  24. package/dist/runtime/mcp/server.js.map +1 -1
  25. package/dist/runtime/mcp/tools.d.ts.map +1 -1
  26. package/dist/runtime/mcp/tools.js +26 -0
  27. package/dist/runtime/mcp/tools.js.map +1 -1
  28. package/dist/runtime/mcp-http/app.d.ts +12 -0
  29. package/dist/runtime/mcp-http/app.d.ts.map +1 -0
  30. package/dist/runtime/mcp-http/app.js +61 -0
  31. package/dist/runtime/mcp-http/app.js.map +1 -0
  32. package/dist/runtime/mcp-http/hosted-mcp-server.d.ts +14 -0
  33. package/dist/runtime/mcp-http/hosted-mcp-server.d.ts.map +1 -0
  34. package/dist/runtime/mcp-http/hosted-mcp-server.js +38 -0
  35. package/dist/runtime/mcp-http/hosted-mcp-server.js.map +1 -0
  36. package/dist/runtime/mcp-http/index.d.ts +14 -0
  37. package/dist/runtime/mcp-http/index.d.ts.map +1 -0
  38. package/dist/runtime/mcp-http/index.js +14 -0
  39. package/dist/runtime/mcp-http/index.js.map +1 -0
  40. package/dist/runtime/mcp-http/main.d.ts +2 -0
  41. package/dist/runtime/mcp-http/main.d.ts.map +1 -0
  42. package/dist/runtime/mcp-http/main.js +14 -0
  43. package/dist/runtime/mcp-http/main.js.map +1 -0
  44. package/dist/runtime/mcp-http/oauth/config.d.ts +33 -0
  45. package/dist/runtime/mcp-http/oauth/config.d.ts.map +1 -0
  46. package/dist/runtime/mcp-http/oauth/config.js +31 -0
  47. package/dist/runtime/mcp-http/oauth/config.js.map +1 -0
  48. package/dist/runtime/mcp-http/oauth/crypto.d.ts +5 -0
  49. package/dist/runtime/mcp-http/oauth/crypto.d.ts.map +1 -0
  50. package/dist/runtime/mcp-http/oauth/crypto.js +19 -0
  51. package/dist/runtime/mcp-http/oauth/crypto.js.map +1 -0
  52. package/dist/runtime/mcp-http/oauth/file-store.d.ts +40 -0
  53. package/dist/runtime/mcp-http/oauth/file-store.d.ts.map +1 -0
  54. package/dist/runtime/mcp-http/oauth/file-store.js +101 -0
  55. package/dist/runtime/mcp-http/oauth/file-store.js.map +1 -0
  56. package/dist/runtime/mcp-http/oauth/hono-oauth-router.d.ts +5 -0
  57. package/dist/runtime/mcp-http/oauth/hono-oauth-router.d.ts.map +1 -0
  58. package/dist/runtime/mcp-http/oauth/hono-oauth-router.js +83 -0
  59. package/dist/runtime/mcp-http/oauth/hono-oauth-router.js.map +1 -0
  60. package/dist/runtime/mcp-http/oauth/redirect-uri.d.ts +11 -0
  61. package/dist/runtime/mcp-http/oauth/redirect-uri.d.ts.map +1 -0
  62. package/dist/runtime/mcp-http/oauth/redirect-uri.js +29 -0
  63. package/dist/runtime/mcp-http/oauth/redirect-uri.js.map +1 -0
  64. package/dist/runtime/mcp-http/oauth/single-tenant-provider.d.ts +68 -0
  65. package/dist/runtime/mcp-http/oauth/single-tenant-provider.d.ts.map +1 -0
  66. package/dist/runtime/mcp-http/oauth/single-tenant-provider.js +238 -0
  67. package/dist/runtime/mcp-http/oauth/single-tenant-provider.js.map +1 -0
  68. package/dist/runtime/mcp-http/readonly-allowlist.d.ts +24 -0
  69. package/dist/runtime/mcp-http/readonly-allowlist.d.ts.map +1 -0
  70. package/dist/runtime/mcp-http/readonly-allowlist.js +43 -0
  71. package/dist/runtime/mcp-http/readonly-allowlist.js.map +1 -0
  72. package/dist/runtime/mcp-http/serve.d.ts +30 -0
  73. package/dist/runtime/mcp-http/serve.d.ts.map +1 -0
  74. package/dist/runtime/mcp-http/serve.js +53 -0
  75. package/dist/runtime/mcp-http/serve.js.map +1 -0
  76. package/package.json +6 -2
@@ -0,0 +1,238 @@
1
+ import { InvalidClientMetadataError, InvalidGrantError, InvalidScopeError, InvalidTargetError, InvalidTokenError } from "@modelcontextprotocol/sdk/server/auth/errors.js";
2
+ import { H2A_HOSTED_OAUTH_SCOPE as OAUTH_SCOPE } from "./config.js";
3
+ import { randomToken, sha256Hex, timingSafeEqualString, tokenHashPrefix } from "./crypto.js";
4
+ import { allRedirectUrisAllowed } from "./redirect-uri.js";
5
+ export class SingleTenantOAuthProvider {
6
+ opts;
7
+ clientsStore;
8
+ constructor(opts) {
9
+ this.opts = opts;
10
+ this.clientsStore = {
11
+ getClient: (clientId) => this.opts.store.getClient(clientId),
12
+ registerClient: async (client) => {
13
+ if (!allRedirectUrisAllowed(client.redirect_uris, this.opts.allowedRedirectUris, this.opts.nodeEnv)) {
14
+ throw new InvalidClientMetadataError("redirect_uris contains a URI that is not allowed");
15
+ }
16
+ const normalized = {
17
+ ...client,
18
+ scope: OAUTH_SCOPE,
19
+ grant_types: ["authorization_code", "refresh_token"],
20
+ response_types: ["code"],
21
+ token_endpoint_auth_method: client.token_endpoint_auth_method ?? "none"
22
+ };
23
+ return this.opts.store.registerClient(normalized);
24
+ }
25
+ };
26
+ }
27
+ nowSeconds() {
28
+ return this.opts.nowSeconds?.() ?? Math.floor(Date.now() / 1000);
29
+ }
30
+ async authorizeRequest(client, params, input) {
31
+ if (input.method !== "POST") {
32
+ return { kind: "consent", status: 200, html: this.renderConsentForm(client, params, undefined) };
33
+ }
34
+ if (!input.consentSecret || !timingSafeEqualString(input.consentSecret, this.opts.consentSecret)) {
35
+ return { kind: "consent", status: 401, html: this.renderConsentForm(client, params, "Invalid consent secret") };
36
+ }
37
+ const code = await this.issueAuthorizationCode(client, {
38
+ redirectUri: params.redirectUri,
39
+ codeChallenge: params.codeChallenge,
40
+ scopes: this.normalizeScopes(params.scopes),
41
+ ...(params.resource !== undefined && { resource: params.resource }),
42
+ ...(params.state !== undefined && { state: params.state })
43
+ });
44
+ const redirect = new URL(params.redirectUri);
45
+ redirect.searchParams.set("code", code);
46
+ if (params.state)
47
+ redirect.searchParams.set("state", params.state);
48
+ return { kind: "redirect", location: redirect.href };
49
+ }
50
+ async issueAuthorizationCode(client, params) {
51
+ const resource = this.normalizeResource(params.resource);
52
+ const scopes = this.normalizeScopes(params.scopes);
53
+ const code = randomToken();
54
+ const now = this.nowSeconds();
55
+ await this.opts.store.putAuthorizationCode(code, {
56
+ clientId: client.client_id,
57
+ redirectUri: params.redirectUri,
58
+ codeChallenge: params.codeChallenge,
59
+ scopes,
60
+ resource: resource.href,
61
+ createdAt: now,
62
+ expiresAt: now + this.opts.authCodeTtlSeconds
63
+ });
64
+ return code;
65
+ }
66
+ renderConsentForm(client, params, error) {
67
+ const scope = this.normalizeScopes(params.scopes).join(" ");
68
+ const resource = this.normalizeResource(params.resource).href;
69
+ const clientName = escapeHtml(client.client_name ?? client.client_id);
70
+ const errorHtml = error ? `<p class="alert" role="alert">${escapeHtml(error)}</p>` : "";
71
+ return `<!doctype html>
72
+ <html lang="en">
73
+ <head>
74
+ <meta charset="utf-8">
75
+ <meta name="viewport" content="width=device-width, initial-scale=1">
76
+ <title>Connect to h2a · SENT Tech</title>
77
+ <style>
78
+ body { margin:0; min-height:100vh; display:grid; place-items:center; background:#f8fafc;
79
+ color:#0f172a; font-family:Inter,system-ui,sans-serif; line-height:1.5; padding:1.5rem; }
80
+ .card { width:100%; max-width:30rem; background:#fff; border:1px solid #e2e8f0;
81
+ border-radius:.5rem; box-shadow:0 1px 3px rgb(15 23 42 / .08); padding:1.75rem; }
82
+ h1 { font-size:1.125rem; margin:0 0 .25rem; }
83
+ .lead { font-size:.9375rem; color:#475569; margin:0 0 1.25rem; }
84
+ dl { display:grid; grid-template-columns:auto 1fr; gap:.4rem .9rem; margin:0 0 1.25rem; font-size:.8125rem; }
85
+ dt { color:#475569; } dd { margin:0; word-break:break-all; font-family:monospace; font-size:.75rem; }
86
+ label { display:block; font-size:.875rem; font-weight:600; margin-bottom:.35rem; }
87
+ .hint { font-weight:400; color:#475569; font-size:.8125rem; }
88
+ input[type=password] { width:100%; padding:.6rem .7rem; font-size:.95rem; border:1px solid #e2e8f0;
89
+ border-radius:.5rem; margin-bottom:1rem; font-family:monospace; }
90
+ button { width:100%; padding:.65rem 1rem; font-size:.95rem; font-weight:600; color:#fff;
91
+ background:oklch(50% 0.134 242.749); border:0; border-radius:.5rem; cursor:pointer; }
92
+ .alert { background:#fef2f2; border:1px solid #fecaca; color:#991b1b; border-radius:.5rem;
93
+ padding:.6rem .8rem; font-size:.8125rem; margin:0 0 1rem; }
94
+ </style>
95
+ </head>
96
+ <body>
97
+ <main class="card">
98
+ <h1>Connect to h2a</h1>
99
+ <p class="lead"><strong>${clientName}</strong> is requesting access to this h2a MCP connector.
100
+ Approving grants it the <code>h2a:read</code> scope on this endpoint only &mdash; a
101
+ <strong>read-only</strong> view (discover / nhi / posture). It never receives any h2a
102
+ private key and cannot sign or mutate. Enter the operator consent secret to approve.</p>
103
+ ${errorHtml}
104
+ <dl>
105
+ <dt>Client</dt><dd>${clientName}</dd>
106
+ <dt>Redirect</dt><dd>${escapeHtml(params.redirectUri)}</dd>
107
+ <dt>Scope</dt><dd>${escapeHtml(scope)}</dd>
108
+ </dl>
109
+ <form method="post" action="/authorize">
110
+ <input type="hidden" name="response_type" value="code">
111
+ <input type="hidden" name="client_id" value="${escapeHtml(client.client_id)}">
112
+ <input type="hidden" name="redirect_uri" value="${escapeHtml(params.redirectUri)}">
113
+ <input type="hidden" name="code_challenge" value="${escapeHtml(params.codeChallenge)}">
114
+ <input type="hidden" name="code_challenge_method" value="S256">
115
+ <input type="hidden" name="scope" value="${escapeHtml(scope)}">
116
+ <input type="hidden" name="resource" value="${escapeHtml(resource)}">
117
+ ${params.state ? `<input type="hidden" name="state" value="${escapeHtml(params.state)}">` : ""}
118
+ <label for="cs">Consent secret <span class="hint">— operator only</span></label>
119
+ <input id="cs" name="consent_secret" type="password" autocomplete="current-password" autofocus>
120
+ <button type="submit">Authorize</button>
121
+ </form>
122
+ </main>
123
+ </body>
124
+ </html>`;
125
+ }
126
+ async challengeForAuthorizationCode(_client, authorizationCode) {
127
+ const record = await this.opts.store.getAuthorizationCode(authorizationCode, this.nowSeconds());
128
+ if (!record)
129
+ throw new InvalidGrantError("authorization code is invalid or expired");
130
+ return record.codeChallenge;
131
+ }
132
+ async exchangeAuthorizationCode(client, authorizationCode, _codeVerifier, redirectUri, resource) {
133
+ const record = await this.opts.store.consumeAuthorizationCode(authorizationCode, this.nowSeconds());
134
+ if (!record)
135
+ throw new InvalidGrantError("authorization code is invalid, expired, or already used");
136
+ if (record.clientId !== client.client_id)
137
+ throw new InvalidGrantError("authorization code was issued to another client");
138
+ if (redirectUri && redirectUri !== record.redirectUri)
139
+ throw new InvalidGrantError("redirect_uri does not match authorization code");
140
+ if (this.normalizeResource(resource).href !== record.resource)
141
+ throw new InvalidTargetError("resource does not match authorization code");
142
+ return this.issueTokens(client, record.scopes, new URL(record.resource), undefined);
143
+ }
144
+ async exchangeRefreshToken(client, refreshToken, scopes, resource) {
145
+ const record = await this.opts.store.findToken(refreshToken);
146
+ const now = this.nowSeconds();
147
+ if (!record || record.tokenType !== "refresh" || record.revokedAt || record.expiresAt <= now) {
148
+ throw new InvalidGrantError("refresh token is invalid or expired");
149
+ }
150
+ if (record.clientId !== client.client_id)
151
+ throw new InvalidGrantError("refresh token was issued to another client");
152
+ if (this.normalizeResource(resource).href !== record.resource)
153
+ throw new InvalidTargetError("resource does not match refresh token");
154
+ const requestedScopes = this.normalizeScopes(scopes ?? record.scopes);
155
+ if (!requestedScopes.every((scope) => record.scopes.includes(scope))) {
156
+ throw new InvalidScopeError("requested scope exceeds refresh token scope");
157
+ }
158
+ await this.opts.store.revokeToken(refreshToken, now);
159
+ return this.issueTokens(client, requestedScopes, new URL(record.resource), sha256Hex(refreshToken));
160
+ }
161
+ async verifyAccessToken(token) {
162
+ const record = await this.opts.store.findToken(token);
163
+ const now = this.nowSeconds();
164
+ if (!record || record.tokenType !== "access" || record.revokedAt || record.expiresAt <= now) {
165
+ throw new InvalidTokenError("access token is invalid or expired");
166
+ }
167
+ return {
168
+ token,
169
+ clientId: record.clientId,
170
+ scopes: record.scopes,
171
+ expiresAt: record.expiresAt,
172
+ resource: new URL(record.resource),
173
+ extra: { tokenHashPrefix: tokenHashPrefix(record.tokenHash) }
174
+ };
175
+ }
176
+ async revokeToken(_client, request) {
177
+ await this.opts.store.revokeToken(request.token, this.nowSeconds());
178
+ }
179
+ async issueTokensForTests(client) {
180
+ return this.issueTokens(client, [OAUTH_SCOPE], this.opts.resourceServerUrl, undefined);
181
+ }
182
+ async issueTokens(client, scopes, resource, parentRefreshTokenHash) {
183
+ const accessToken = randomToken();
184
+ const refreshToken = randomToken();
185
+ const now = this.nowSeconds();
186
+ await this.opts.store.putToken(accessToken, {
187
+ tokenType: "access",
188
+ clientId: client.client_id,
189
+ scopes,
190
+ resource: resource.href,
191
+ issuedAt: now,
192
+ expiresAt: now + this.opts.accessTokenTtlSeconds,
193
+ ...(parentRefreshTokenHash !== undefined && { parentRefreshTokenHash })
194
+ });
195
+ await this.opts.store.putToken(refreshToken, {
196
+ tokenType: "refresh",
197
+ clientId: client.client_id,
198
+ scopes,
199
+ resource: resource.href,
200
+ issuedAt: now,
201
+ expiresAt: now + this.opts.refreshTokenTtlSeconds,
202
+ ...(parentRefreshTokenHash !== undefined && { parentRefreshTokenHash })
203
+ });
204
+ return {
205
+ access_token: accessToken,
206
+ refresh_token: refreshToken,
207
+ token_type: "Bearer",
208
+ expires_in: this.opts.accessTokenTtlSeconds,
209
+ scope: scopes.join(" ")
210
+ };
211
+ }
212
+ normalizeScopes(scopes) {
213
+ const requested = scopes && scopes.length > 0 ? [...scopes] : [OAUTH_SCOPE];
214
+ if (!requested.every((scope) => scope === OAUTH_SCOPE)) {
215
+ throw new InvalidScopeError(`only ${OAUTH_SCOPE} scope is supported`);
216
+ }
217
+ return [OAUTH_SCOPE];
218
+ }
219
+ normalizeResource(resource) {
220
+ const rs = this.opts.resourceServerUrl;
221
+ if (resource === undefined)
222
+ return rs;
223
+ const path = resource.pathname.replace(/\/+$/, "");
224
+ const rsPath = rs.pathname.replace(/\/+$/, "");
225
+ if (resource.origin === rs.origin && (path === rsPath || path === ""))
226
+ return rs;
227
+ throw new InvalidTargetError("resource must match the MCP resource server URL");
228
+ }
229
+ }
230
+ function escapeHtml(value) {
231
+ return value
232
+ .replaceAll("&", "&amp;")
233
+ .replaceAll("<", "&lt;")
234
+ .replaceAll(">", "&gt;")
235
+ .replaceAll('"', "&quot;")
236
+ .replaceAll("'", "&#39;");
237
+ }
238
+ //# sourceMappingURL=single-tenant-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"single-tenant-provider.js","sourceRoot":"","sources":["../../../../src/runtime/mcp-http/oauth/single-tenant-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * EVO-12 hosted MCP — read-only tool allowlist (the load-bearing security
3
+ * invariant, DEC-116 / EVO-11 key custody).
4
+ *
5
+ * The internet-facing hosted MCP (enrolled from claude.ai) exposes ONLY read
6
+ * tools. No tool that takes a private key is ever routable on the hosted
7
+ * surface, so the ed25519 private key never travels over the wire. Signing
8
+ * stays on the keyring-holding side (local CLI / sidecar). This is a STRUCTURAL
9
+ * deployment property, not a phase: `hostedReadOnlyDescriptors` throws if the
10
+ * allowlist ever contains a private-key tool (defense-in-depth schema scan).
11
+ */
12
+ import type { McpToolDescriptor, McpToolName } from "../mcp/tools.js";
13
+ /** Phase-1 read-only surface: "claude.ai has the info" (discover / nhi / posture). */
14
+ export declare const H2A_HOSTED_READONLY_TOOLS: readonly McpToolName[];
15
+ /** True iff the tool's input schema carries a private key (must never be hosted). */
16
+ export declare function toolTakesPrivateKey(descriptor: McpToolDescriptor): boolean;
17
+ export declare function isHostedReadOnlyTool(name: string): boolean;
18
+ /**
19
+ * The read-only descriptor subset to expose on the hosted MCP. THROWS if any
20
+ * allowlisted tool takes a private key — the allowlist must never include a
21
+ * signing tool (structural invariant, not a runtime hope).
22
+ */
23
+ export declare function hostedReadOnlyDescriptors(all: readonly McpToolDescriptor[]): McpToolDescriptor[];
24
+ //# sourceMappingURL=readonly-allowlist.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readonly-allowlist.d.ts","sourceRoot":"","sources":["../../../src/runtime/mcp-http/readonly-allowlist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEtE,sFAAsF;AACtF,eAAO,MAAM,yBAAyB,EAAE,SAAS,WAAW,EAO3D,CAAC;AAEF,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,CAE1E;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,GAAG,EAAE,SAAS,iBAAiB,EAAE,GAChC,iBAAiB,EAAE,CAWrB"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * EVO-12 hosted MCP — read-only tool allowlist (the load-bearing security
3
+ * invariant, DEC-116 / EVO-11 key custody).
4
+ *
5
+ * The internet-facing hosted MCP (enrolled from claude.ai) exposes ONLY read
6
+ * tools. No tool that takes a private key is ever routable on the hosted
7
+ * surface, so the ed25519 private key never travels over the wire. Signing
8
+ * stays on the keyring-holding side (local CLI / sidecar). This is a STRUCTURAL
9
+ * deployment property, not a phase: `hostedReadOnlyDescriptors` throws if the
10
+ * allowlist ever contains a private-key tool (defense-in-depth schema scan).
11
+ */
12
+ /** Phase-1 read-only surface: "claude.ai has the info" (discover / nhi / posture). */
13
+ export const H2A_HOSTED_READONLY_TOOLS = [
14
+ "h2a_discover_instances",
15
+ "h2a_discover_sessions",
16
+ "h2a_nhi_inventory",
17
+ "h2a_nhi_report",
18
+ "h2a_conflict_posture",
19
+ "h2a_blockage_list"
20
+ ];
21
+ /** True iff the tool's input schema carries a private key (must never be hosted). */
22
+ export function toolTakesPrivateKey(descriptor) {
23
+ return JSON.stringify(descriptor.inputSchema ?? {}).includes("privateKeyPem");
24
+ }
25
+ export function isHostedReadOnlyTool(name) {
26
+ return H2A_HOSTED_READONLY_TOOLS.includes(name);
27
+ }
28
+ /**
29
+ * The read-only descriptor subset to expose on the hosted MCP. THROWS if any
30
+ * allowlisted tool takes a private key — the allowlist must never include a
31
+ * signing tool (structural invariant, not a runtime hope).
32
+ */
33
+ export function hostedReadOnlyDescriptors(all) {
34
+ const exposed = all.filter((d) => isHostedReadOnlyTool(d.name));
35
+ const leaky = exposed.filter(toolTakesPrivateKey);
36
+ if (leaky.length > 0) {
37
+ throw new Error(`EVO-12 hosted allowlist must not include private-key tools: ${leaky
38
+ .map((d) => d.name)
39
+ .join(", ")}`);
40
+ }
41
+ return exposed;
42
+ }
43
+ //# sourceMappingURL=readonly-allowlist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readonly-allowlist.js","sourceRoot":"","sources":["../../../src/runtime/mcp-http/readonly-allowlist.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,sFAAsF;AACtF,MAAM,CAAC,MAAM,yBAAyB,GAA2B;IAC/D,wBAAwB;IACxB,uBAAuB;IACvB,mBAAmB;IACnB,gBAAgB;IAChB,sBAAsB;IACtB,mBAAmB;CACpB,CAAC;AAEF,qFAAqF;AACrF,MAAM,UAAU,mBAAmB,CAAC,UAA6B;IAC/D,OAAO,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAQ,yBAA+C,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AACzE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,GAAiC;IAEjC,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAChE,MAAM,KAAK,GAAG,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAClD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,+DAA+D,KAAK;aACjE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aAClB,IAAI,CAAC,IAAI,CAAC,EAAE,CAChB,CAAC;IACJ,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { McpServer } from "../mcp/server.js";
2
+ import { type H2AHostedOAuthConfig } from "./oauth/config.js";
3
+ export interface HostedEnv {
4
+ PUBLIC_BASE_URL?: string;
5
+ OAUTH_ISSUER_URL?: string;
6
+ OAUTH_ALLOWED_REDIRECT_URIS?: string;
7
+ OAUTH_CONSENT_SECRET?: string;
8
+ OAUTH_ACCESS_TOKEN_TTL_SECONDS?: string;
9
+ OAUTH_REFRESH_TOKEN_TTL_SECONDS?: string;
10
+ OAUTH_AUTH_CODE_TTL_SECONDS?: string;
11
+ OAUTH_STORE_PATH?: string;
12
+ H2A_ROOT?: string;
13
+ PORT?: string;
14
+ NODE_ENV?: string;
15
+ }
16
+ export interface HostedConfig {
17
+ oauthConfig: H2AHostedOAuthConfig;
18
+ storePath: string;
19
+ root: string;
20
+ port: number;
21
+ }
22
+ /** Pure: validate + derive the hosted config from env (defaults claude.ai redirects). */
23
+ export declare function buildHostedConfigFromEnv(env: HostedEnv): HostedConfig;
24
+ export interface StartedHostedServer {
25
+ port: number;
26
+ h2aMcpServer: McpServer;
27
+ stop(): void;
28
+ }
29
+ export declare function startHostedServer(env?: HostedEnv): Promise<StartedHostedServer>;
30
+ //# sourceMappingURL=serve.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../../../src/runtime/mcp-http/serve.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,EAAE,KAAK,oBAAoB,EAAsB,MAAM,mBAAmB,CAAC;AAIlF,MAAM,WAAW,SAAS;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,8BAA8B,CAAC,EAAE,MAAM,CAAC;IACxC,+BAA+B,CAAC,EAAE,MAAM,CAAC;IACzC,2BAA2B,CAAC,EAAE,MAAM,CAAC;IACrC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAKD,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,oBAAoB,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,yFAAyF;AACzF,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,SAAS,GAAG,YAAY,CAoBrE;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,SAAS,CAAC;IACxB,IAAI,IAAI,IAAI,CAAC;CACd;AAED,wBAAsB,iBAAiB,CAAC,GAAG,GAAE,SAAuB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAelG"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * EVO-12 hosted MCP — env-driven entrypoint. Reads the deploy env, builds the
3
+ * OAuth provider + the read-only h2a MCP surface, and serves the Hono app over
4
+ * HTTP (@hono/node-server). `buildHostedConfigFromEnv` is the testable core.
5
+ */
6
+ import { join } from "node:path";
7
+ import { serve } from "@hono/node-server";
8
+ import { createMcpServer } from "../mcp/index.js";
9
+ import { createHostedApp } from "./app.js";
10
+ import { oauthConfigFromEnv } from "./oauth/config.js";
11
+ import { FileOAuthStore } from "./oauth/file-store.js";
12
+ import { SingleTenantOAuthProvider } from "./oauth/single-tenant-provider.js";
13
+ const DEFAULT_CLAUDE_REDIRECTS = "https://claude.ai/api/mcp/auth_callback,https://claude.com/api/mcp/auth_callback";
14
+ /** Pure: validate + derive the hosted config from env (defaults claude.ai redirects). */
15
+ export function buildHostedConfigFromEnv(env) {
16
+ const publicBaseUrl = env.PUBLIC_BASE_URL;
17
+ if (!publicBaseUrl)
18
+ throw new Error("PUBLIC_BASE_URL is required");
19
+ const root = env.H2A_ROOT ?? join(process.cwd(), ".h2a");
20
+ const oauthConfig = oauthConfigFromEnv({
21
+ PUBLIC_BASE_URL: publicBaseUrl,
22
+ OAUTH_ISSUER_URL: env.OAUTH_ISSUER_URL ?? publicBaseUrl,
23
+ OAUTH_ALLOWED_REDIRECT_URIS: env.OAUTH_ALLOWED_REDIRECT_URIS ?? DEFAULT_CLAUDE_REDIRECTS,
24
+ ...(env.OAUTH_CONSENT_SECRET !== undefined && { OAUTH_CONSENT_SECRET: env.OAUTH_CONSENT_SECRET }),
25
+ OAUTH_ACCESS_TOKEN_TTL_SECONDS: Number(env.OAUTH_ACCESS_TOKEN_TTL_SECONDS ?? 3600),
26
+ OAUTH_REFRESH_TOKEN_TTL_SECONDS: Number(env.OAUTH_REFRESH_TOKEN_TTL_SECONDS ?? 1_209_600),
27
+ OAUTH_AUTH_CODE_TTL_SECONDS: Number(env.OAUTH_AUTH_CODE_TTL_SECONDS ?? 60),
28
+ NODE_ENV: env.NODE_ENV ?? "production"
29
+ });
30
+ return {
31
+ oauthConfig,
32
+ storePath: env.OAUTH_STORE_PATH ?? join(root, "oauth-clients.json"),
33
+ root,
34
+ port: Number(env.PORT ?? 8787)
35
+ };
36
+ }
37
+ export async function startHostedServer(env = process.env) {
38
+ const cfg = buildHostedConfigFromEnv(env);
39
+ const store = new FileOAuthStore(cfg.storePath);
40
+ await store.load();
41
+ const oauthProvider = new SingleTenantOAuthProvider({ store, ...cfg.oauthConfig });
42
+ const h2aMcpServer = createMcpServer({ root: cfg.root });
43
+ const app = createHostedApp({ oauthProvider, oauthConfig: cfg.oauthConfig, h2aMcpServer });
44
+ const server = serve({ fetch: app.fetch, port: cfg.port });
45
+ return {
46
+ port: cfg.port,
47
+ h2aMcpServer,
48
+ stop: () => {
49
+ server.close?.();
50
+ }
51
+ };
52
+ }
53
+ //# sourceMappingURL=serve.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/runtime/mcp-http/serve.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAA6B,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAgB9E,MAAM,wBAAwB,GAC5B,kFAAkF,CAAC;AASrF,yFAAyF;AACzF,MAAM,UAAU,wBAAwB,CAAC,GAAc;IACrD,MAAM,aAAa,GAAG,GAAG,CAAC,eAAe,CAAC;IAC1C,IAAI,CAAC,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACnE,MAAM,IAAI,GAAG,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IACzD,MAAM,WAAW,GAAG,kBAAkB,CAAC;QACrC,eAAe,EAAE,aAAa;QAC9B,gBAAgB,EAAE,GAAG,CAAC,gBAAgB,IAAI,aAAa;QACvD,2BAA2B,EAAE,GAAG,CAAC,2BAA2B,IAAI,wBAAwB;QACxF,GAAG,CAAC,GAAG,CAAC,oBAAoB,KAAK,SAAS,IAAI,EAAE,oBAAoB,EAAE,GAAG,CAAC,oBAAoB,EAAE,CAAC;QACjG,8BAA8B,EAAE,MAAM,CAAC,GAAG,CAAC,8BAA8B,IAAI,IAAI,CAAC;QAClF,+BAA+B,EAAE,MAAM,CAAC,GAAG,CAAC,+BAA+B,IAAI,SAAS,CAAC;QACzF,2BAA2B,EAAE,MAAM,CAAC,GAAG,CAAC,2BAA2B,IAAI,EAAE,CAAC;QAC1E,QAAQ,EAAE,GAAG,CAAC,QAAQ,IAAI,YAAY;KACvC,CAAC,CAAC;IACH,OAAO;QACL,WAAW;QACX,SAAS,EAAE,GAAG,CAAC,gBAAgB,IAAI,IAAI,CAAC,IAAI,EAAE,oBAAoB,CAAC;QACnE,IAAI;QACJ,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC;KAC/B,CAAC;AACJ,CAAC;AAQD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,MAAiB,OAAO,CAAC,GAAG;IAClE,MAAM,GAAG,GAAG,wBAAwB,CAAC,GAAG,CAAC,CAAC;IAC1C,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAChD,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;IACnB,MAAM,aAAa,GAAG,IAAI,yBAAyB,CAAC,EAAE,KAAK,EAAE,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IACnF,MAAM,YAAY,GAAG,eAAe,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACzD,MAAM,GAAG,GAAG,eAAe,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,CAAC,WAAW,EAAE,YAAY,EAAE,CAAC,CAAC;IAC3F,MAAM,MAAM,GAAG,KAAK,CAAC,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3D,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,YAAY;QACZ,IAAI,EAAE,GAAG,EAAE;YACR,MAAiC,CAAC,KAAK,EAAE,EAAE,CAAC;QAC/C,CAAC;KACF,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentropic/h2a-cli",
3
- "version": "0.23.0",
3
+ "version": "0.25.1",
4
4
  "description": "Unified CLI surface for h2a hosts and MCP-oriented coordination flows.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -40,7 +40,11 @@
40
40
  "skills"
41
41
  ],
42
42
  "dependencies": {
43
- "@sentropic/h2a": "^0.23.0"
43
+ "@hono/mcp": "^0.3.0",
44
+ "@hono/node-server": "^2.0.4",
45
+ "@modelcontextprotocol/sdk": "^1.29.0",
46
+ "@sentropic/h2a": "^0.25.1",
47
+ "hono": "^4.12.23"
44
48
  },
45
49
  "publishConfig": {
46
50
  "access": "public"