@n24q02m/hull 0.4.0-beta.9 → 0.5.0-beta.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.
- package/README.md +47 -16
- package/build/cli/build-cli.d.ts +5 -6
- package/build/cli/build-cli.d.ts.map +1 -1
- package/build/cli/build-cli.js +4 -61
- package/build/cli/build-cli.js.map +1 -1
- package/build/crypto/index.d.ts +1 -1
- package/build/crypto/index.d.ts.map +1 -1
- package/build/crypto/index.js +1 -1
- package/build/crypto/index.js.map +1 -1
- package/build/crypto/kdf.d.ts +0 -13
- package/build/crypto/kdf.d.ts.map +1 -1
- package/build/crypto/kdf.js +3 -18
- package/build/crypto/kdf.js.map +1 -1
- package/build/index.d.ts +1 -11
- package/build/index.d.ts.map +1 -1
- package/build/index.js +4 -16
- package/build/index.js.map +1 -1
- package/build/storage/account-registry.d.ts +53 -0
- package/build/storage/account-registry.d.ts.map +1 -0
- package/build/storage/account-registry.js +148 -0
- package/build/storage/account-registry.js.map +1 -0
- package/build/storage/backends.d.ts +0 -26
- package/build/storage/backends.d.ts.map +1 -1
- package/build/storage/backends.js +0 -65
- package/build/storage/backends.js.map +1 -1
- package/build/storage/config-file.d.ts +2 -19
- package/build/storage/config-file.d.ts.map +1 -1
- package/build/storage/config-file.js +2 -23
- package/build/storage/config-file.js.map +1 -1
- package/build/storage/credential-store.d.ts +1 -9
- package/build/storage/credential-store.d.ts.map +1 -1
- package/build/storage/credential-store.js +4 -16
- package/build/storage/credential-store.js.map +1 -1
- package/build/storage/data-dir.d.ts +2 -2
- package/build/storage/data-dir.js +2 -2
- package/build/storage/encryption.d.ts.map +1 -1
- package/build/storage/encryption.js +4 -0
- package/build/storage/encryption.js.map +1 -1
- package/build/storage/index.d.ts +5 -3
- package/build/storage/index.d.ts.map +1 -1
- package/build/storage/index.js +5 -3
- package/build/storage/index.js.map +1 -1
- package/build/storage/key-pool-crypto.d.ts +44 -0
- package/build/storage/key-pool-crypto.d.ts.map +1 -0
- package/build/storage/key-pool-crypto.js +132 -0
- package/build/storage/key-pool-crypto.js.map +1 -0
- package/build/storage/key-pool-store.d.ts +134 -0
- package/build/storage/key-pool-store.d.ts.map +1 -0
- package/build/storage/key-pool-store.js +470 -0
- package/build/storage/key-pool-store.js.map +1 -0
- package/build/storage/key-pool.d.ts +172 -0
- package/build/storage/key-pool.d.ts.map +1 -0
- package/build/storage/key-pool.js +374 -0
- package/build/storage/key-pool.js.map +1 -0
- package/build/storage/resolver.js +1 -1
- package/build/storage/resolver.js.map +1 -1
- package/build/token-auth/index.d.ts +1 -1
- package/build/token-auth/index.js +1 -1
- package/build/transport/index.d.ts +1 -2
- package/build/transport/index.d.ts.map +1 -1
- package/build/transport/index.js +1 -2
- package/build/transport/index.js.map +1 -1
- package/build/transport/local-server.d.ts +12 -128
- package/build/transport/local-server.d.ts.map +1 -1
- package/build/transport/local-server.js +20 -262
- package/build/transport/local-server.js.map +1 -1
- package/build/transport/streamable-http.d.ts +6 -7
- package/build/transport/streamable-http.d.ts.map +1 -1
- package/build/transport/streamable-http.js +6 -6
- package/build/transport/streamable-http.js.map +1 -1
- package/build/util/browser.d.ts.map +1 -0
- package/build/{relay → util}/browser.js +2 -2
- package/build/util/browser.js.map +1 -0
- package/package.json +4 -21
- package/build/auth/credential-form.d.ts +0 -163
- package/build/auth/credential-form.d.ts.map +0 -1
- package/build/auth/credential-form.js +0 -1869
- package/build/auth/credential-form.js.map +0 -1
- package/build/auth/delegated-oauth-app.d.ts +0 -90
- package/build/auth/delegated-oauth-app.d.ts.map +0 -1
- package/build/auth/delegated-oauth-app.js +0 -881
- package/build/auth/delegated-oauth-app.js.map +0 -1
- package/build/auth/index.d.ts +0 -12
- package/build/auth/index.d.ts.map +0 -1
- package/build/auth/index.js +0 -12
- package/build/auth/index.js.map +0 -1
- package/build/auth/local-oauth-app.d.ts +0 -152
- package/build/auth/local-oauth-app.d.ts.map +0 -1
- package/build/auth/local-oauth-app.js +0 -975
- package/build/auth/local-oauth-app.js.map +0 -1
- package/build/auth/relay-login.d.ts +0 -46
- package/build/auth/relay-login.d.ts.map +0 -1
- package/build/auth/relay-login.js +0 -197
- package/build/auth/relay-login.js.map +0 -1
- package/build/auth/router.d.ts +0 -18
- package/build/auth/router.d.ts.map +0 -1
- package/build/auth/router.js +0 -62
- package/build/auth/router.js.map +0 -1
- package/build/auth/session-store.d.ts +0 -49
- package/build/auth/session-store.d.ts.map +0 -1
- package/build/auth/session-store.js +0 -94
- package/build/auth/session-store.js.map +0 -1
- package/build/auth/stable-sub.d.ts +0 -27
- package/build/auth/stable-sub.d.ts.map +0 -1
- package/build/auth/stable-sub.js +0 -38
- package/build/auth/stable-sub.js.map +0 -1
- package/build/auth/well-known.d.ts +0 -4
- package/build/auth/well-known.d.ts.map +0 -1
- package/build/auth/well-known.js +0 -26
- package/build/auth/well-known.js.map +0 -1
- package/build/oauth/cache.d.ts +0 -22
- package/build/oauth/cache.d.ts.map +0 -1
- package/build/oauth/cache.js +0 -24
- package/build/oauth/cache.js.map +0 -1
- package/build/oauth/index.d.ts +0 -6
- package/build/oauth/index.d.ts.map +0 -1
- package/build/oauth/index.js +0 -6
- package/build/oauth/index.js.map +0 -1
- package/build/oauth/jwt-issuer.d.ts +0 -87
- package/build/oauth/jwt-issuer.d.ts.map +0 -1
- package/build/oauth/jwt-issuer.js +0 -205
- package/build/oauth/jwt-issuer.js.map +0 -1
- package/build/oauth/pkce.d.ts +0 -10
- package/build/oauth/pkce.d.ts.map +0 -1
- package/build/oauth/pkce.js +0 -34
- package/build/oauth/pkce.js.map +0 -1
- package/build/oauth/provider.d.ts +0 -52
- package/build/oauth/provider.d.ts.map +0 -1
- package/build/oauth/provider.js +0 -98
- package/build/oauth/provider.js.map +0 -1
- package/build/oauth/user-store.d.ts +0 -25
- package/build/oauth/user-store.d.ts.map +0 -1
- package/build/oauth/user-store.js +0 -85
- package/build/oauth/user-store.js.map +0 -1
- package/build/relay/browser.d.ts.map +0 -1
- package/build/relay/browser.js.map +0 -1
- package/build/relay/client.d.ts +0 -36
- package/build/relay/client.d.ts.map +0 -1
- package/build/relay/client.js +0 -135
- package/build/relay/client.js.map +0 -1
- package/build/relay/session.d.ts +0 -12
- package/build/relay/session.d.ts.map +0 -1
- package/build/relay/session.js +0 -55
- package/build/relay/session.js.map +0 -1
- package/build/relay/tool-helpers.d.ts +0 -85
- package/build/relay/tool-helpers.d.ts.map +0 -1
- package/build/relay/tool-helpers.js +0 -103
- package/build/relay/tool-helpers.js.map +0 -1
- package/build/relay/wordlist.d.ts +0 -2
- package/build/relay/wordlist.d.ts.map +0 -1
- package/build/relay/wordlist.js +0 -7782
- package/build/relay/wordlist.js.map +0 -1
- package/build/schema/types.d.ts +0 -67
- package/build/schema/types.d.ts.map +0 -1
- package/build/schema/types.js +0 -2
- package/build/schema/types.js.map +0 -1
- package/build/storage/mode.d.ts +0 -23
- package/build/storage/mode.d.ts.map +0 -1
- package/build/storage/mode.js +0 -46
- package/build/storage/mode.js.map +0 -1
- package/build/storage/session-lock.d.ts +0 -19
- package/build/storage/session-lock.d.ts.map +0 -1
- package/build/storage/session-lock.js +0 -105
- package/build/storage/session-lock.js.map +0 -1
- package/build/transport/oauth-middleware.d.ts +0 -45
- package/build/transport/oauth-middleware.d.ts.map +0 -1
- package/build/transport/oauth-middleware.js +0 -86
- package/build/transport/oauth-middleware.js.map +0 -1
- /package/build/{relay → util}/browser.d.ts +0 -0
package/build/oauth/provider.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OAuth 2.1 Provider logic for TypeScript MCP servers.
|
|
3
|
-
*
|
|
4
|
-
* Implements the "MCP Server as Authorization Server" pattern.
|
|
5
|
-
* The MCP Server issues JWTs, processes PKCE verification,
|
|
6
|
-
* and uses the Relay Server purely as a UI consent transport.
|
|
7
|
-
*/
|
|
8
|
-
import { createSession, pollForResult } from '../relay/client.js';
|
|
9
|
-
import { InMemoryAuthCache } from './cache.js';
|
|
10
|
-
import { verifyPKCE } from './pkce.js';
|
|
11
|
-
export { InMemoryAuthCache } from './cache.js';
|
|
12
|
-
export class OAuthProvider {
|
|
13
|
-
constructor(options) {
|
|
14
|
-
this.serverName = options.serverName;
|
|
15
|
-
this.relayBaseUrl = options.relayBaseUrl;
|
|
16
|
-
this.relaySchema = options.relaySchema;
|
|
17
|
-
this.jwtIssuer = options.jwtIssuer;
|
|
18
|
-
this.cache = options.cache ?? new InMemoryAuthCache();
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Create a relay session and return the URL to redirect the user to.
|
|
22
|
-
*/
|
|
23
|
-
async createAuthorizeRedirect(clientId, redirectUri, state, codeChallenge, codeChallengeMethod = 'S256') {
|
|
24
|
-
const session = await createSession(this.relayBaseUrl, this.serverName, this.relaySchema);
|
|
25
|
-
// Export private key for later credential retrieval
|
|
26
|
-
const privateJwk = await crypto.subtle.exportKey('jwk', session.keyPair.privateKey);
|
|
27
|
-
const preAuth = {
|
|
28
|
-
sessionId: session.sessionId,
|
|
29
|
-
clientId,
|
|
30
|
-
redirectUri,
|
|
31
|
-
state,
|
|
32
|
-
codeChallenge,
|
|
33
|
-
codeChallengeMethod,
|
|
34
|
-
keyPairJwk: privateJwk,
|
|
35
|
-
passphrase: session.passphrase,
|
|
36
|
-
expiresAt: Math.floor(Date.now() / 1000) + 600 // 10 minutes
|
|
37
|
-
};
|
|
38
|
-
this.cache.save(preAuth);
|
|
39
|
-
return session.relayUrl;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Exchange the authorization code (relay session ID) for an access_token.
|
|
43
|
-
* PKCE verification is performed.
|
|
44
|
-
*
|
|
45
|
-
* @param code The authorization code from the client.
|
|
46
|
-
* @param codeVerifier The PKCE code verifier.
|
|
47
|
-
* @param userIdExtractor Function to derive a unique user_id from the credentials.
|
|
48
|
-
* @returns Tuple of [access_token, credentials]
|
|
49
|
-
*/
|
|
50
|
-
async exchangeCode(code, codeVerifier, userIdExtractor) {
|
|
51
|
-
const preAuth = this.cache.getAndDelete(code);
|
|
52
|
-
if (!preAuth)
|
|
53
|
-
throw new Error('invalid_grant: Expired or invalid code');
|
|
54
|
-
// Verify PKCE
|
|
55
|
-
verifyPKCE(codeVerifier, preAuth.codeChallenge, preAuth.codeChallengeMethod);
|
|
56
|
-
// Reconstruct relay session
|
|
57
|
-
const reconstructedSession = await this.reconstructRelaySession(preAuth);
|
|
58
|
-
// Poll for credentials (short timeout — form already submitted)
|
|
59
|
-
const credentials = await pollForResult(this.relayBaseUrl, reconstructedSession, 1000, 10_000);
|
|
60
|
-
// Extract unique user_id
|
|
61
|
-
const userId = userIdExtractor(credentials);
|
|
62
|
-
if (!userId)
|
|
63
|
-
throw new Error('server_error: Unable to extract user_id from credentials');
|
|
64
|
-
// Issue access token
|
|
65
|
-
const accessToken = await this.jwtIssuer.issueAccessToken(userId);
|
|
66
|
-
return { accessToken, credentials };
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Reconstruct relay session from pre-auth session.
|
|
70
|
-
*/
|
|
71
|
-
async reconstructRelaySession(preAuth) {
|
|
72
|
-
// Reconstruct key pair to decrypt relay credentials
|
|
73
|
-
const privateKey = await crypto.subtle.importKey('jwk', preAuth.keyPairJwk, { name: 'ECDH', namedCurve: 'P-256' }, true, ['deriveKey', 'deriveBits']);
|
|
74
|
-
const publicKey = await crypto.subtle.importKey('jwk', { ...preAuth.keyPairJwk, d: undefined }, { name: 'ECDH', namedCurve: 'P-256' }, true, []);
|
|
75
|
-
return {
|
|
76
|
-
sessionId: preAuth.sessionId,
|
|
77
|
-
keyPair: { privateKey, publicKey },
|
|
78
|
-
passphrase: preAuth.passphrase,
|
|
79
|
-
relayUrl: ''
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Get RFC 8414 Authorization Server Metadata.
|
|
84
|
-
*/
|
|
85
|
-
getMetadata(baseUrl) {
|
|
86
|
-
return {
|
|
87
|
-
issuer: baseUrl,
|
|
88
|
-
authorization_endpoint: `${baseUrl}/authorize`,
|
|
89
|
-
token_endpoint: `${baseUrl}/token`,
|
|
90
|
-
jwks_uri: `${baseUrl}/.well-known/jwks.json`,
|
|
91
|
-
response_types_supported: ['code'],
|
|
92
|
-
grant_types_supported: ['authorization_code'],
|
|
93
|
-
code_challenge_methods_supported: ['S256', 'plain'],
|
|
94
|
-
token_endpoint_auth_methods_supported: ['none']
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
//# sourceMappingURL=provider.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/oauth/provider.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-user encrypted credential store for MCP HTTP modes.
|
|
3
|
-
* Uses better-sqlite3 for persistence and Node.js crypto for AES-256-GCM at-rest encryption.
|
|
4
|
-
*/
|
|
5
|
-
export interface IUserCredentialStore {
|
|
6
|
-
saveCredentials(userId: string, config: Record<string, string>): void;
|
|
7
|
-
getCredentials(userId: string): Record<string, string> | null;
|
|
8
|
-
deleteCredentials(userId: string): void;
|
|
9
|
-
}
|
|
10
|
-
export declare class SqliteUserStore implements IUserCredentialStore {
|
|
11
|
-
private db;
|
|
12
|
-
private masterKey;
|
|
13
|
-
/**
|
|
14
|
-
* @param dbPath Path to the SQLite DB file.
|
|
15
|
-
* @param masterKey 32-byte AES key for at-rest encryption.
|
|
16
|
-
*/
|
|
17
|
-
constructor(dbPath: string, masterKey: Buffer);
|
|
18
|
-
private encrypt;
|
|
19
|
-
private decrypt;
|
|
20
|
-
saveCredentials(userId: string, config: Record<string, string>): void;
|
|
21
|
-
getCredentials(userId: string): Record<string, string> | null;
|
|
22
|
-
deleteCredentials(userId: string): void;
|
|
23
|
-
close(): void;
|
|
24
|
-
}
|
|
25
|
-
//# sourceMappingURL=user-store.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"user-store.d.ts","sourceRoot":"","sources":["../../src/oauth/user-store.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,WAAW,oBAAoB;IACnC,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;IACrE,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;IAC7D,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACxC;AAED,qBAAa,eAAgB,YAAW,oBAAoB;IAC1D,OAAO,CAAC,EAAE,CAAmB;IAC7B,OAAO,CAAC,SAAS,CAAQ;IAEzB;;;OAGG;IACH,YAAY,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAwB5C;IAED,OAAO,CAAC,OAAO;IASf,OAAO,CAAC,OAAO;IASf,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAapE;IAED,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAY5D;IAED,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAEtC;IAED,KAAK,IAAI,IAAI,CAEZ;CACF"}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Per-user encrypted credential store for MCP HTTP modes.
|
|
3
|
-
* Uses better-sqlite3 for persistence and Node.js crypto for AES-256-GCM at-rest encryption.
|
|
4
|
-
*/
|
|
5
|
-
import { createCipheriv, createDecipheriv, randomBytes } from 'node:crypto';
|
|
6
|
-
import { chmodSync, mkdirSync } from 'node:fs';
|
|
7
|
-
import { dirname } from 'node:path';
|
|
8
|
-
import Database from 'better-sqlite3';
|
|
9
|
-
export class SqliteUserStore {
|
|
10
|
-
/**
|
|
11
|
-
* @param dbPath Path to the SQLite DB file.
|
|
12
|
-
* @param masterKey 32-byte AES key for at-rest encryption.
|
|
13
|
-
*/
|
|
14
|
-
constructor(dbPath, masterKey) {
|
|
15
|
-
if (masterKey.length !== 32)
|
|
16
|
-
throw new Error('masterKey must be 32 bytes');
|
|
17
|
-
this.masterKey = masterKey;
|
|
18
|
-
// Ensure directory exists with strict permissions (owner-only access).
|
|
19
|
-
// 0o700 = owner read+write+exec only; group/other denied. This is the
|
|
20
|
-
// secure default for a credential store directory.
|
|
21
|
-
mkdirSync(dirname(dbPath), { recursive: true, mode: 0o700 });
|
|
22
|
-
if (process.platform !== 'win32') {
|
|
23
|
-
chmodSync(dirname(dbPath), 0o700);
|
|
24
|
-
}
|
|
25
|
-
this.db = new Database(dbPath);
|
|
26
|
-
this.db.pragma('journal_mode = WAL');
|
|
27
|
-
this.db
|
|
28
|
-
.prepare(`
|
|
29
|
-
CREATE TABLE IF NOT EXISTS users (
|
|
30
|
-
user_id TEXT PRIMARY KEY,
|
|
31
|
-
encrypted_config BLOB NOT NULL,
|
|
32
|
-
created_at INTEGER NOT NULL,
|
|
33
|
-
updated_at INTEGER NOT NULL
|
|
34
|
-
)
|
|
35
|
-
`)
|
|
36
|
-
.run();
|
|
37
|
-
}
|
|
38
|
-
encrypt(plaintext) {
|
|
39
|
-
const iv = randomBytes(12);
|
|
40
|
-
const cipher = createCipheriv('aes-256-gcm', this.masterKey, iv, { authTagLength: 16 });
|
|
41
|
-
const encrypted = Buffer.concat([cipher.update(plaintext, 'utf-8'), cipher.final()]);
|
|
42
|
-
const tag = cipher.getAuthTag();
|
|
43
|
-
// Layout: [12 iv] [16 tag] [... ciphertext]
|
|
44
|
-
return Buffer.concat([iv, tag, encrypted]);
|
|
45
|
-
}
|
|
46
|
-
decrypt(payload) {
|
|
47
|
-
const iv = payload.subarray(0, 12);
|
|
48
|
-
const tag = payload.subarray(12, 28);
|
|
49
|
-
const ciphertext = payload.subarray(28);
|
|
50
|
-
const decipher = createDecipheriv('aes-256-gcm', this.masterKey, iv, { authTagLength: 16 });
|
|
51
|
-
decipher.setAuthTag(tag);
|
|
52
|
-
return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString('utf-8');
|
|
53
|
-
}
|
|
54
|
-
saveCredentials(userId, config) {
|
|
55
|
-
const now = Math.floor(Date.now() / 1000);
|
|
56
|
-
const encrypted = this.encrypt(JSON.stringify(config));
|
|
57
|
-
this.db
|
|
58
|
-
.prepare(`
|
|
59
|
-
INSERT INTO users (user_id, encrypted_config, created_at, updated_at)
|
|
60
|
-
VALUES (?, ?, ?, ?)
|
|
61
|
-
ON CONFLICT(user_id) DO UPDATE SET
|
|
62
|
-
encrypted_config = excluded.encrypted_config,
|
|
63
|
-
updated_at = excluded.updated_at
|
|
64
|
-
`)
|
|
65
|
-
.run(userId, encrypted, now, now);
|
|
66
|
-
}
|
|
67
|
-
getCredentials(userId) {
|
|
68
|
-
const row = this.db.prepare('SELECT encrypted_config FROM users WHERE user_id = ?').get(userId);
|
|
69
|
-
if (!row)
|
|
70
|
-
return null;
|
|
71
|
-
try {
|
|
72
|
-
return JSON.parse(this.decrypt(Buffer.from(row.encrypted_config)));
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
deleteCredentials(userId) {
|
|
79
|
-
this.db.prepare('DELETE FROM users WHERE user_id = ?').run(userId);
|
|
80
|
-
}
|
|
81
|
-
close() {
|
|
82
|
-
this.db.close();
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
//# sourceMappingURL=user-store.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"user-store.js","sourceRoot":"","sources":["../../src/oauth/user-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/relay/browser.ts"],"names":[],"mappings":"AAAA;;GAEG;AA2EH;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA6ClE"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/relay/browser.ts"],"names":[],"mappings":"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"}
|
package/build/relay/client.d.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { RelayConfigSchema } from '../schema/types.js';
|
|
2
|
-
export declare function generatePassphrase(wordCount?: number): string;
|
|
3
|
-
export interface RelaySession {
|
|
4
|
-
sessionId: string;
|
|
5
|
-
keyPair: CryptoKeyPair;
|
|
6
|
-
passphrase: string;
|
|
7
|
-
relayUrl: string;
|
|
8
|
-
}
|
|
9
|
-
export declare function createSession(relayBaseUrl: string, serverName: string, schema: RelayConfigSchema): Promise<RelaySession>;
|
|
10
|
-
export declare function pollForResult(relayBaseUrl: string, session: RelaySession, intervalMs?: number, timeoutMs?: number): Promise<Record<string, string>>;
|
|
11
|
-
export declare function sendMessage(relayBaseUrl: string, sessionId: string, message: {
|
|
12
|
-
type: string;
|
|
13
|
-
text: string;
|
|
14
|
-
data?: Record<string, unknown>;
|
|
15
|
-
}): Promise<string>;
|
|
16
|
-
export declare function pollForResponses(relayBaseUrl: string, sessionId: string, messageId: string, intervalMs?: number, timeoutMs?: number): Promise<string>;
|
|
17
|
-
/**
|
|
18
|
-
* Notify the browser that relay setup is complete, then clean up the session.
|
|
19
|
-
*
|
|
20
|
-
* The browser polls ``/api/sessions/:id/messages`` every 2s and stops on the
|
|
21
|
-
* first ``type:'complete'`` message. If the caller deletes the session before
|
|
22
|
-
* that poll lands, the browser sees 404 on its next fetch and the UI stalls
|
|
23
|
-
* on "Waiting for server...". This helper sends the ``complete`` message,
|
|
24
|
-
* then schedules the DELETE after a grace period long enough to cover a few
|
|
25
|
-
* poll cycles, and unref's the cleanup timer so it doesn't block process
|
|
26
|
-
* shutdown. If the process exits before the timer fires, the relay server's
|
|
27
|
-
* 10-minute TTL reclaims the session.
|
|
28
|
-
*
|
|
29
|
-
* Errors from ``sendMessage`` are logged via ``console.error`` and swallowed
|
|
30
|
-
* — the helper is a best-effort post-setup notification and must not fail
|
|
31
|
-
* the caller's primary flow.
|
|
32
|
-
*/
|
|
33
|
-
export declare function notifyComplete(relayBaseUrl: string, sessionId: string, text?: string, options?: {
|
|
34
|
-
gracePeriodMs?: number;
|
|
35
|
-
}): Promise<void>;
|
|
36
|
-
//# sourceMappingURL=client.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAA;AAM3D,wBAAgB,kBAAkB,CAAC,SAAS,SAAI,GAAG,MAAM,CAkBxD;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,aAAa,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,YAAY,CAAC,CAgBvB;AAED,wBAAsB,aAAa,CACjC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,YAAY,EACrB,UAAU,SAAO,EACjB,SAAS,SAAU,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAwCjC;AAED,wBAAsB,WAAW,CAC/B,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GACtE,OAAO,CAAC,MAAM,CAAC,CASjB;AAED,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,UAAU,SAAO,EACjB,SAAS,SAAU,GAClB,OAAO,CAAC,MAAM,CAAC,CA2BjB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,cAAc,CAClC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,IAAI,GAAE,MAA0B,EAChC,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAO,GACvC,OAAO,CAAC,IAAI,CAAC,CAcf"}
|
package/build/relay/client.js
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { decrypt } from '../crypto/aes.js';
|
|
2
|
-
import { deriveSharedSecret, exportPublicKey, generateKeyPair, importPublicKey } from '../crypto/ecdh.js';
|
|
3
|
-
import { deriveAesKey } from '../crypto/kdf.js';
|
|
4
|
-
import { WORDLIST } from './wordlist.js';
|
|
5
|
-
// Single fallback buffer for rejection sampling, reused to minimize GC
|
|
6
|
-
const fallbackBuffer = new Uint16Array(1);
|
|
7
|
-
export function generatePassphrase(wordCount = 4) {
|
|
8
|
-
const words = [];
|
|
9
|
-
const max = Math.floor(0x10000 / WORDLIST.length) * WORDLIST.length; // rejection threshold
|
|
10
|
-
// Optimization: Batch random generation for all words at once
|
|
11
|
-
const buffer = new Uint16Array(wordCount);
|
|
12
|
-
crypto.getRandomValues(buffer);
|
|
13
|
-
for (let i = 0; i < wordCount; i++) {
|
|
14
|
-
let index = buffer[i];
|
|
15
|
-
while (index >= max) {
|
|
16
|
-
// Reject biased values and resample
|
|
17
|
-
crypto.getRandomValues(fallbackBuffer);
|
|
18
|
-
index = fallbackBuffer[0];
|
|
19
|
-
}
|
|
20
|
-
words.push(WORDLIST[index % WORDLIST.length]);
|
|
21
|
-
}
|
|
22
|
-
return words.join('-');
|
|
23
|
-
}
|
|
24
|
-
export async function createSession(relayBaseUrl, serverName, schema) {
|
|
25
|
-
const sessionId = Buffer.from(crypto.getRandomValues(new Uint8Array(32))).toString('hex');
|
|
26
|
-
const keyPair = await generateKeyPair();
|
|
27
|
-
const passphrase = generatePassphrase();
|
|
28
|
-
const response = await fetch(`${relayBaseUrl}/api/sessions`, {
|
|
29
|
-
method: 'POST',
|
|
30
|
-
headers: { 'Content-Type': 'application/json' },
|
|
31
|
-
body: JSON.stringify({ sessionId, serverName, schema })
|
|
32
|
-
});
|
|
33
|
-
if (!response.ok)
|
|
34
|
-
throw new Error(`Relay session creation failed: ${response.status}`);
|
|
35
|
-
const pubKeyBase64 = await exportPublicKey(keyPair.publicKey);
|
|
36
|
-
const relayUrl = `${relayBaseUrl}/authorize?s=${sessionId}#k=${pubKeyBase64}&p=${encodeURIComponent(passphrase)}`;
|
|
37
|
-
return { sessionId, keyPair, passphrase, relayUrl };
|
|
38
|
-
}
|
|
39
|
-
export async function pollForResult(relayBaseUrl, session, intervalMs = 2000, timeoutMs = 600_000) {
|
|
40
|
-
const deadline = Date.now() + timeoutMs;
|
|
41
|
-
while (Date.now() < deadline) {
|
|
42
|
-
const response = await fetch(`${relayBaseUrl}/api/sessions/${session.sessionId}`);
|
|
43
|
-
if (response.status === 200) {
|
|
44
|
-
const body = await response.json();
|
|
45
|
-
if (body.status === 'skipped') {
|
|
46
|
-
// Cleanup session
|
|
47
|
-
await fetch(`${relayBaseUrl}/api/sessions/${session.sessionId}`, {
|
|
48
|
-
method: 'DELETE'
|
|
49
|
-
}).catch(() => { });
|
|
50
|
-
throw new Error('RELAY_SKIPPED');
|
|
51
|
-
}
|
|
52
|
-
const { browserPub, ciphertext, iv, tag } = body.result ?? body;
|
|
53
|
-
const browserKey = await importPublicKey(browserPub);
|
|
54
|
-
const sharedSecret = await deriveSharedSecret(session.keyPair.privateKey, browserKey);
|
|
55
|
-
const aesKey = await deriveAesKey(sharedSecret, session.passphrase);
|
|
56
|
-
const plaintext = await decrypt(aesKey, new Uint8Array(Buffer.from(ciphertext, 'base64')), new Uint8Array(Buffer.from(iv, 'base64')), new Uint8Array(Buffer.from(tag, 'base64')));
|
|
57
|
-
// Don't delete session here — keep alive for bidirectional messaging.
|
|
58
|
-
// Session auto-expires via TTL (10 min). Caller can delete manually if needed.
|
|
59
|
-
return JSON.parse(plaintext);
|
|
60
|
-
}
|
|
61
|
-
if (response.status === 404)
|
|
62
|
-
throw new Error('Session expired or not found');
|
|
63
|
-
if (response.status !== 202)
|
|
64
|
-
throw new Error(`Unexpected status: ${response.status}`);
|
|
65
|
-
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
66
|
-
}
|
|
67
|
-
throw new Error('Relay setup timed out');
|
|
68
|
-
}
|
|
69
|
-
export async function sendMessage(relayBaseUrl, sessionId, message) {
|
|
70
|
-
const response = await fetch(`${relayBaseUrl}/api/sessions/${sessionId}/messages`, {
|
|
71
|
-
method: 'POST',
|
|
72
|
-
headers: { 'Content-Type': 'application/json' },
|
|
73
|
-
body: JSON.stringify(message)
|
|
74
|
-
});
|
|
75
|
-
if (!response.ok)
|
|
76
|
-
throw new Error(`Failed to send message: ${response.status}`);
|
|
77
|
-
const body = await response.json();
|
|
78
|
-
return body.id;
|
|
79
|
-
}
|
|
80
|
-
export async function pollForResponses(relayBaseUrl, sessionId, messageId, intervalMs = 2000, timeoutMs = 300_000) {
|
|
81
|
-
const deadline = Date.now() + timeoutMs;
|
|
82
|
-
let lastSeenCount = 0;
|
|
83
|
-
while (Date.now() < deadline) {
|
|
84
|
-
const response = await fetch(`${relayBaseUrl}/api/sessions/${sessionId}/responses`);
|
|
85
|
-
if (!response.ok)
|
|
86
|
-
throw new Error(`Failed to poll responses: ${response.status}`);
|
|
87
|
-
const body = await response.json();
|
|
88
|
-
const responses = body.responses;
|
|
89
|
-
// Optimization: The responses array is append-only.
|
|
90
|
-
// Instead of doing O(N) array.find() scanning from the beginning,
|
|
91
|
-
// manually loop backwards stopping at the last seen count.
|
|
92
|
-
if (responses) {
|
|
93
|
-
for (let i = responses.length - 1; i >= lastSeenCount; i--) {
|
|
94
|
-
if (responses[i].messageId === messageId) {
|
|
95
|
-
return responses[i].value;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
lastSeenCount = responses.length;
|
|
99
|
-
}
|
|
100
|
-
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
101
|
-
}
|
|
102
|
-
throw new Error('Timed out waiting for response');
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Notify the browser that relay setup is complete, then clean up the session.
|
|
106
|
-
*
|
|
107
|
-
* The browser polls ``/api/sessions/:id/messages`` every 2s and stops on the
|
|
108
|
-
* first ``type:'complete'`` message. If the caller deletes the session before
|
|
109
|
-
* that poll lands, the browser sees 404 on its next fetch and the UI stalls
|
|
110
|
-
* on "Waiting for server...". This helper sends the ``complete`` message,
|
|
111
|
-
* then schedules the DELETE after a grace period long enough to cover a few
|
|
112
|
-
* poll cycles, and unref's the cleanup timer so it doesn't block process
|
|
113
|
-
* shutdown. If the process exits before the timer fires, the relay server's
|
|
114
|
-
* 10-minute TTL reclaims the session.
|
|
115
|
-
*
|
|
116
|
-
* Errors from ``sendMessage`` are logged via ``console.error`` and swallowed
|
|
117
|
-
* — the helper is a best-effort post-setup notification and must not fail
|
|
118
|
-
* the caller's primary flow.
|
|
119
|
-
*/
|
|
120
|
-
export async function notifyComplete(relayBaseUrl, sessionId, text = 'Setup complete!', options = {}) {
|
|
121
|
-
try {
|
|
122
|
-
await sendMessage(relayBaseUrl, sessionId, { type: 'complete', text });
|
|
123
|
-
}
|
|
124
|
-
catch (err) {
|
|
125
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
126
|
-
console.error(`[hull] Failed to send relay 'complete' message: ${msg}`);
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
const gracePeriodMs = options.gracePeriodMs ?? 5000;
|
|
130
|
-
const timer = setTimeout(() => {
|
|
131
|
-
fetch(`${relayBaseUrl}/api/sessions/${sessionId}`, { method: 'DELETE' }).catch(() => { });
|
|
132
|
-
}, gracePeriodMs);
|
|
133
|
-
timer.unref?.();
|
|
134
|
-
}
|
|
135
|
-
//# sourceMappingURL=client.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"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"}
|
package/build/relay/session.d.ts
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export interface ActiveFormSession {
|
|
2
|
-
token: string;
|
|
3
|
-
clientId: string;
|
|
4
|
-
startedAt: Date;
|
|
5
|
-
expiresAt: Date;
|
|
6
|
-
}
|
|
7
|
-
export declare function claimSession(clientId: string): ActiveFormSession;
|
|
8
|
-
export declare function releaseSession(): void;
|
|
9
|
-
export declare function isSessionActive(): boolean;
|
|
10
|
-
export declare function validateSessionToken(token: string): boolean;
|
|
11
|
-
export declare function getActiveSession(): ActiveFormSession | null;
|
|
12
|
-
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/relay/session.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,IAAI,CAAA;IACf,SAAS,EAAE,IAAI,CAAA;CAChB;AAQD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAYhE;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,eAAe,IAAI,OAAO,CAOzC;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAW3D;AAED,wBAAgB,gBAAgB,IAAI,iBAAiB,GAAG,IAAI,CAO3D"}
|
package/build/relay/session.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { randomBytes, timingSafeEqual } from 'node:crypto';
|
|
2
|
-
const SESSION_TTL_MS = 30 * 60 * 1000;
|
|
3
|
-
const TOKEN_BYTES = 32;
|
|
4
|
-
let state = null;
|
|
5
|
-
function now() {
|
|
6
|
-
return new Date();
|
|
7
|
-
}
|
|
8
|
-
export function claimSession(clientId) {
|
|
9
|
-
if (state !== null && state.expiresAt > now()) {
|
|
10
|
-
return state;
|
|
11
|
-
}
|
|
12
|
-
const startedAt = now();
|
|
13
|
-
state = {
|
|
14
|
-
token: randomBytes(TOKEN_BYTES).toString('base64url'),
|
|
15
|
-
clientId,
|
|
16
|
-
startedAt,
|
|
17
|
-
expiresAt: new Date(startedAt.getTime() + SESSION_TTL_MS)
|
|
18
|
-
};
|
|
19
|
-
return state;
|
|
20
|
-
}
|
|
21
|
-
export function releaseSession() {
|
|
22
|
-
state = null;
|
|
23
|
-
}
|
|
24
|
-
export function isSessionActive() {
|
|
25
|
-
if (state === null)
|
|
26
|
-
return false;
|
|
27
|
-
if (state.expiresAt <= now()) {
|
|
28
|
-
state = null;
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
export function validateSessionToken(token) {
|
|
34
|
-
if (state === null)
|
|
35
|
-
return false;
|
|
36
|
-
if (state.expiresAt <= now()) {
|
|
37
|
-
state = null;
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
const a = Buffer.from(state.token);
|
|
41
|
-
const b = Buffer.from(token);
|
|
42
|
-
const isLengthEqual = a.length === b.length;
|
|
43
|
-
const compareB = isLengthEqual ? b : a;
|
|
44
|
-
return timingSafeEqual(a, compareB) && isLengthEqual;
|
|
45
|
-
}
|
|
46
|
-
export function getActiveSession() {
|
|
47
|
-
if (state === null)
|
|
48
|
-
return null;
|
|
49
|
-
if (state.expiresAt <= now()) {
|
|
50
|
-
state = null;
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
return state;
|
|
54
|
-
}
|
|
55
|
-
//# sourceMappingURL=session.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/relay/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE1D,MAAM,cAAc,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAA;AACrC,MAAM,WAAW,GAAG,EAAE,CAAA;AAStB,IAAI,KAAK,GAA6B,IAAI,CAAA;AAE1C,SAAS,GAAG;IACV,OAAO,IAAI,IAAI,EAAE,CAAA;AACnB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,SAAS,GAAG,GAAG,EAAE,CAAA;IACvB,KAAK,GAAG;QACN,KAAK,EAAE,WAAW,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC;QACrD,QAAQ;QACR,SAAS;QACT,SAAS,EAAE,IAAI,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC;KAC1D,CAAA;IACD,OAAO,KAAK,CAAA;AACd,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,KAAK,GAAG,IAAI,CAAA;AACd,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAChC,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,KAAK,CAAA;IACd,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAa;IAChD,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAChC,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,KAAK,CAAA;IACd,CAAC;IACD,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;IAClC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5B,MAAM,aAAa,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAAA;IAC3C,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;IACtC,OAAO,eAAe,CAAC,CAAC,EAAE,QAAQ,CAAC,IAAI,aAAa,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,CAAA;IAC/B,IAAI,KAAK,CAAC,SAAS,IAAI,GAAG,EAAE,EAAE,CAAC;QAC7B,KAAK,GAAG,IAAI,CAAA;QACZ,OAAO,IAAI,CAAA;IACb,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC"}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Helper to register `config__open_relay` MCP tool — HTTP-only mode.
|
|
3
|
-
*
|
|
4
|
-
* After the stdio-pure + http-multi-user split, the relay config form is
|
|
5
|
-
* served by the HTTP server itself at ``<PUBLIC_URL>/authorize``. The
|
|
6
|
-
* ``config__open_relay`` tool directs the user to that URL.
|
|
7
|
-
*
|
|
8
|
-
* When the connected client declares URL-mode elicitation
|
|
9
|
-
* (``capabilities.elicitation.url``, SEP-1036), the tool asks the client to
|
|
10
|
-
* present a consent prompt and open the URL via ``Server.elicitInput`` so the
|
|
11
|
-
* credential form is opened out-of-band without the LLM in the loop. Clients
|
|
12
|
-
* that do NOT declare the capability fall back to the legacy behaviour: the
|
|
13
|
-
* tool returns the URL and best-effort opens the user's default browser
|
|
14
|
-
* server-side. There is no daemon-bridge discovery, no respawn, and no
|
|
15
|
-
* session deduplication — the HTTP server's own session tracking handles
|
|
16
|
-
* concurrent setup attempts.
|
|
17
|
-
*
|
|
18
|
-
* In stdio mode, the relay form does not exist; the tool returns
|
|
19
|
-
* ``stdio_unsupported`` so plugin code can render a "switch to HTTP mode"
|
|
20
|
-
* message instead of misleading the user with an unreachable URL.
|
|
21
|
-
*/
|
|
22
|
-
import type { ClientCapabilities, ElicitRequestURLParams, ElicitResult } from '@modelcontextprotocol/sdk/types.js';
|
|
23
|
-
export interface OpenRelayResult {
|
|
24
|
-
url: string;
|
|
25
|
-
browserOpened: boolean;
|
|
26
|
-
status: 'configured' | 'unconfigured' | 'expired' | 'session_active' | 'stdio_unsupported';
|
|
27
|
-
elicitation?: 'accepted' | 'declined' | 'cancelled';
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Minimal surface required to drive URL-mode elicitation. Satisfied by the
|
|
31
|
-
* low-level `Server` from `@modelcontextprotocol/sdk` (`mcp.server`), which
|
|
32
|
-
* exposes both `getClientCapabilities` and `elicitInput`.
|
|
33
|
-
*/
|
|
34
|
-
export interface ElicitationServer {
|
|
35
|
-
getClientCapabilities: () => ClientCapabilities | undefined;
|
|
36
|
-
elicitInput: (params: ElicitRequestURLParams) => Promise<ElicitResult>;
|
|
37
|
-
}
|
|
38
|
-
export interface OpenRelayHandlerOptions {
|
|
39
|
-
serverName: string;
|
|
40
|
-
publicUrl: string | null;
|
|
41
|
-
elicitation?: ElicitationServer;
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Build the `open_relay` handler closure. Tests can call
|
|
45
|
-
* ``buildOpenRelayHandler({ serverName, publicUrl })`` directly without
|
|
46
|
-
* needing a real MCP server instance.
|
|
47
|
-
*
|
|
48
|
-
* When `elicitation` is supplied AND the client declared URL-mode
|
|
49
|
-
* elicitation, the handler drives `elicitInput` and returns the accept /
|
|
50
|
-
* decline / cancel outcome. Otherwise it returns the legacy dict
|
|
51
|
-
* (`{ url, browserOpened, status }`) unchanged.
|
|
52
|
-
*/
|
|
53
|
-
export declare function buildOpenRelayHandler(options: OpenRelayHandlerOptions): () => Promise<OpenRelayResult>;
|
|
54
|
-
/**
|
|
55
|
-
* Minimal shape we require from the MCP server instance to register the
|
|
56
|
-
* tool. Both `@modelcontextprotocol/sdk`'s `McpServer.tool` and the
|
|
57
|
-
* FastMCP-style helpers used in consumer servers conform to this surface.
|
|
58
|
-
*/
|
|
59
|
-
export interface ToolRegistrar {
|
|
60
|
-
tool: (config: {
|
|
61
|
-
name: string;
|
|
62
|
-
description?: string;
|
|
63
|
-
}, handler: () => Promise<OpenRelayResult>) => void;
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Register `config__open_relay` on the given MCP server instance.
|
|
67
|
-
*
|
|
68
|
-
* Consumer servers add this single line in their tool registry after the
|
|
69
|
-
* other `config__*` registrations:
|
|
70
|
-
*
|
|
71
|
-
* ```ts
|
|
72
|
-
* import { registerOpenRelayTool } from '@n24q02m/hull'
|
|
73
|
-
* registerOpenRelayTool(mcp, SERVER_NAME, PUBLIC_URL, mcp.server)
|
|
74
|
-
* ```
|
|
75
|
-
*
|
|
76
|
-
* Pass ``null`` for ``publicUrl`` when the server is running in stdio mode;
|
|
77
|
-
* the tool will return ``status: 'stdio_unsupported'`` so the caller can
|
|
78
|
-
* surface a clear "switch to HTTP mode" message.
|
|
79
|
-
*
|
|
80
|
-
* Pass the low-level `Server` (`mcp.server`) as ``elicitation`` to enable
|
|
81
|
-
* URL-mode elicitation for capable clients. When omitted, the tool keeps
|
|
82
|
-
* the legacy browser-open behaviour for every client.
|
|
83
|
-
*/
|
|
84
|
-
export declare function registerOpenRelayTool(mcp: ToolRegistrar, serverName: string, publicUrl: string | null, elicitation?: ElicitationServer): void;
|
|
85
|
-
//# sourceMappingURL=tool-helpers.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tool-helpers.d.ts","sourceRoot":"","sources":["../../src/relay/tool-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAA;AAGlH,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAA;IACX,aAAa,EAAE,OAAO,CAAA;IACtB,MAAM,EAAE,YAAY,GAAG,cAAc,GAAG,SAAS,GAAG,gBAAgB,GAAG,mBAAmB,CAAA;IAC1F,WAAW,CAAC,EAAE,UAAU,GAAG,UAAU,GAAG,WAAW,CAAA;CACpD;AAED;;;;GAIG;AACH,MAAM,WAAW,iBAAiB;IAChC,qBAAqB,EAAE,MAAM,kBAAkB,GAAG,SAAS,CAAA;IAC3D,WAAW,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAA;CACvE;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,CAAC,EAAE,iBAAiB,CAAA;CAChC;AAaD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC,CAyBtG;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,KAAK,IAAI,CAAA;CACxG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,aAAa,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,WAAW,CAAC,EAAE,iBAAiB,GAC9B,IAAI,CAiBN"}
|