@n24q02m/hull 0.0.0-stage → 0.4.0-beta.10
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 +201 -0
- package/README.md +166 -2
- package/build/auth/credential-form.d.ts +163 -0
- package/build/auth/credential-form.d.ts.map +1 -0
- package/build/auth/credential-form.js +1869 -0
- package/build/auth/credential-form.js.map +1 -0
- package/build/auth/delegated-oauth-app.d.ts +90 -0
- package/build/auth/delegated-oauth-app.d.ts.map +1 -0
- package/build/auth/delegated-oauth-app.js +881 -0
- package/build/auth/delegated-oauth-app.js.map +1 -0
- package/build/auth/index.d.ts +12 -0
- package/build/auth/index.d.ts.map +1 -0
- package/build/auth/index.js +12 -0
- package/build/auth/index.js.map +1 -0
- package/build/auth/local-oauth-app.d.ts +152 -0
- package/build/auth/local-oauth-app.d.ts.map +1 -0
- package/build/auth/local-oauth-app.js +975 -0
- package/build/auth/local-oauth-app.js.map +1 -0
- package/build/auth/relay-login.d.ts +46 -0
- package/build/auth/relay-login.d.ts.map +1 -0
- package/build/auth/relay-login.js +197 -0
- package/build/auth/relay-login.js.map +1 -0
- package/build/auth/router.d.ts +18 -0
- package/build/auth/router.d.ts.map +1 -0
- package/build/auth/router.js +62 -0
- package/build/auth/router.js.map +1 -0
- package/build/auth/session-store.d.ts +49 -0
- package/build/auth/session-store.d.ts.map +1 -0
- package/build/auth/session-store.js +94 -0
- package/build/auth/session-store.js.map +1 -0
- package/build/auth/stable-sub.d.ts +27 -0
- package/build/auth/stable-sub.d.ts.map +1 -0
- package/build/auth/stable-sub.js +38 -0
- package/build/auth/stable-sub.js.map +1 -0
- package/build/auth/well-known.d.ts +4 -0
- package/build/auth/well-known.d.ts.map +1 -0
- package/build/auth/well-known.js +26 -0
- package/build/auth/well-known.js.map +1 -0
- package/build/cli/build-cli.d.ts +97 -0
- package/build/cli/build-cli.d.ts.map +1 -0
- package/build/cli/build-cli.js +309 -0
- package/build/cli/build-cli.js.map +1 -0
- package/build/cli/index.d.ts +2 -0
- package/build/cli/index.d.ts.map +1 -0
- package/build/cli/index.js +2 -0
- package/build/cli/index.js.map +1 -0
- package/build/crypto/aes.d.ts +8 -0
- package/build/crypto/aes.d.ts.map +1 -0
- package/build/crypto/aes.js +27 -0
- package/build/crypto/aes.js.map +1 -0
- package/build/crypto/ecdh.d.ts +5 -0
- package/build/crypto/ecdh.d.ts.map +1 -0
- package/build/crypto/ecdh.js +16 -0
- package/build/crypto/ecdh.js.map +1 -0
- package/build/crypto/index.d.ts +4 -0
- package/build/crypto/index.d.ts.map +1 -0
- package/build/crypto/index.js +4 -0
- package/build/crypto/index.js.map +1 -0
- package/build/crypto/kdf.d.ts +15 -0
- package/build/crypto/kdf.d.ts.map +1 -0
- package/build/crypto/kdf.js +26 -0
- package/build/crypto/kdf.js.map +1 -0
- package/build/index.d.ts +19 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +28 -0
- package/build/index.js.map +1 -0
- package/build/lifecycle/lock.d.ts +68 -0
- package/build/lifecycle/lock.d.ts.map +1 -0
- package/build/lifecycle/lock.js +173 -0
- package/build/lifecycle/lock.js.map +1 -0
- package/build/oauth/cache.d.ts +22 -0
- package/build/oauth/cache.d.ts.map +1 -0
- package/build/oauth/cache.js +24 -0
- package/build/oauth/cache.js.map +1 -0
- package/build/oauth/index.d.ts +6 -0
- package/build/oauth/index.d.ts.map +1 -0
- package/build/oauth/index.js +6 -0
- package/build/oauth/index.js.map +1 -0
- package/build/oauth/jwt-issuer.d.ts +87 -0
- package/build/oauth/jwt-issuer.d.ts.map +1 -0
- package/build/oauth/jwt-issuer.js +205 -0
- package/build/oauth/jwt-issuer.js.map +1 -0
- package/build/oauth/pkce.d.ts +10 -0
- package/build/oauth/pkce.d.ts.map +1 -0
- package/build/oauth/pkce.js +34 -0
- package/build/oauth/pkce.js.map +1 -0
- package/build/oauth/provider.d.ts +52 -0
- package/build/oauth/provider.d.ts.map +1 -0
- package/build/oauth/provider.js +98 -0
- package/build/oauth/provider.js.map +1 -0
- package/build/oauth/user-store.d.ts +25 -0
- package/build/oauth/user-store.d.ts.map +1 -0
- package/build/oauth/user-store.js +85 -0
- package/build/oauth/user-store.js.map +1 -0
- package/build/relay/browser.d.ts +15 -0
- package/build/relay/browser.d.ts.map +1 -0
- package/build/relay/browser.js +121 -0
- package/build/relay/browser.js.map +1 -0
- package/build/relay/client.d.ts +36 -0
- package/build/relay/client.d.ts.map +1 -0
- package/build/relay/client.js +135 -0
- package/build/relay/client.js.map +1 -0
- package/build/relay/session.d.ts +12 -0
- package/build/relay/session.d.ts.map +1 -0
- package/build/relay/session.js +55 -0
- package/build/relay/session.js.map +1 -0
- package/build/relay/tool-helpers.d.ts +85 -0
- package/build/relay/tool-helpers.d.ts.map +1 -0
- package/build/relay/tool-helpers.js +103 -0
- package/build/relay/tool-helpers.js.map +1 -0
- package/build/relay/wordlist.d.ts +2 -0
- package/build/relay/wordlist.d.ts.map +1 -0
- package/build/relay/wordlist.js +7782 -0
- package/build/relay/wordlist.js.map +1 -0
- package/build/schema/types.d.ts +67 -0
- package/build/schema/types.d.ts.map +1 -0
- package/build/schema/types.js +2 -0
- package/build/schema/types.js.map +1 -0
- package/build/storage/atomic-write.d.ts +8 -0
- package/build/storage/atomic-write.d.ts.map +1 -0
- package/build/storage/atomic-write.js +26 -0
- package/build/storage/atomic-write.js.map +1 -0
- package/build/storage/backends.d.ts +74 -0
- package/build/storage/backends.d.ts.map +1 -0
- package/build/storage/backends.js +193 -0
- package/build/storage/backends.js.map +1 -0
- package/build/storage/config-file.d.ts +35 -0
- package/build/storage/config-file.d.ts.map +1 -0
- package/build/storage/config-file.js +259 -0
- package/build/storage/config-file.js.map +1 -0
- package/build/storage/credential-store.d.ts +71 -0
- package/build/storage/credential-store.d.ts.map +1 -0
- package/build/storage/credential-store.js +91 -0
- package/build/storage/credential-store.js.map +1 -0
- package/build/storage/data-dir.d.ts +58 -0
- package/build/storage/data-dir.d.ts.map +1 -0
- package/build/storage/data-dir.js +79 -0
- package/build/storage/data-dir.js.map +1 -0
- package/build/storage/encryption.d.ts +9 -0
- package/build/storage/encryption.d.ts.map +1 -0
- package/build/storage/encryption.js +27 -0
- package/build/storage/encryption.js.map +1 -0
- package/build/storage/home-dir.d.ts +11 -0
- package/build/storage/home-dir.d.ts.map +1 -0
- package/build/storage/home-dir.js +18 -0
- package/build/storage/home-dir.js.map +1 -0
- package/build/storage/index.d.ts +12 -0
- package/build/storage/index.d.ts.map +1 -0
- package/build/storage/index.js +15 -0
- package/build/storage/index.js.map +1 -0
- package/build/storage/machine-id.d.ts +4 -0
- package/build/storage/machine-id.d.ts.map +1 -0
- package/build/storage/machine-id.js +73 -0
- package/build/storage/machine-id.js.map +1 -0
- package/build/storage/mode.d.ts +23 -0
- package/build/storage/mode.d.ts.map +1 -0
- package/build/storage/mode.js +46 -0
- package/build/storage/mode.js.map +1 -0
- package/build/storage/per-plugin-store.d.ts +65 -0
- package/build/storage/per-plugin-store.d.ts.map +1 -0
- package/build/storage/per-plugin-store.js +194 -0
- package/build/storage/per-plugin-store.js.map +1 -0
- package/build/storage/resolver.d.ts +7 -0
- package/build/storage/resolver.d.ts.map +1 -0
- package/build/storage/resolver.js +38 -0
- package/build/storage/resolver.js.map +1 -0
- package/build/storage/session-lock.d.ts +19 -0
- package/build/storage/session-lock.d.ts.map +1 -0
- package/build/storage/session-lock.js +105 -0
- package/build/storage/session-lock.js.map +1 -0
- package/build/token-auth/authenticator.d.ts +70 -0
- package/build/token-auth/authenticator.d.ts.map +1 -0
- package/build/token-auth/authenticator.js +146 -0
- package/build/token-auth/authenticator.js.map +1 -0
- package/build/token-auth/cli.d.ts +38 -0
- package/build/token-auth/cli.d.ts.map +1 -0
- package/build/token-auth/cli.js +192 -0
- package/build/token-auth/cli.js.map +1 -0
- package/build/token-auth/context.d.ts +30 -0
- package/build/token-auth/context.d.ts.map +1 -0
- package/build/token-auth/context.js +32 -0
- package/build/token-auth/context.js.map +1 -0
- package/build/token-auth/http-guard.d.ts +26 -0
- package/build/token-auth/http-guard.d.ts.map +1 -0
- package/build/token-auth/http-guard.js +51 -0
- package/build/token-auth/http-guard.js.map +1 -0
- package/build/token-auth/index.d.ts +14 -0
- package/build/token-auth/index.d.ts.map +1 -0
- package/build/token-auth/index.js +14 -0
- package/build/token-auth/index.js.map +1 -0
- package/build/token-auth/limiter.d.ts +20 -0
- package/build/token-auth/limiter.d.ts.map +1 -0
- package/build/token-auth/limiter.js +41 -0
- package/build/token-auth/limiter.js.map +1 -0
- package/build/token-auth/loopback.d.ts +17 -0
- package/build/token-auth/loopback.d.ts.map +1 -0
- package/build/token-auth/loopback.js +62 -0
- package/build/token-auth/loopback.js.map +1 -0
- package/build/token-auth/py-compat.d.ts +24 -0
- package/build/token-auth/py-compat.d.ts.map +1 -0
- package/build/token-auth/py-compat.js +76 -0
- package/build/token-auth/py-compat.js.map +1 -0
- package/build/token-auth/tokens.d.ts +36 -0
- package/build/token-auth/tokens.d.ts.map +1 -0
- package/build/token-auth/tokens.js +115 -0
- package/build/token-auth/tokens.js.map +1 -0
- package/build/token-auth/users.d.ts +62 -0
- package/build/token-auth/users.d.ts.map +1 -0
- package/build/token-auth/users.js +174 -0
- package/build/token-auth/users.js.map +1 -0
- package/build/transport/cache.d.ts +19 -0
- package/build/transport/cache.d.ts.map +1 -0
- package/build/transport/cache.js +76 -0
- package/build/transport/cache.js.map +1 -0
- package/build/transport/index.d.ts +5 -0
- package/build/transport/index.d.ts.map +1 -0
- package/build/transport/index.js +4 -0
- package/build/transport/index.js.map +1 -0
- package/build/transport/local-server.d.ts +173 -0
- package/build/transport/local-server.d.ts.map +1 -0
- package/build/transport/local-server.js +457 -0
- package/build/transport/local-server.js.map +1 -0
- package/build/transport/oauth-middleware.d.ts +45 -0
- package/build/transport/oauth-middleware.d.ts.map +1 -0
- package/build/transport/oauth-middleware.js +86 -0
- package/build/transport/oauth-middleware.js.map +1 -0
- package/build/transport/streamable-http.d.ts +54 -0
- package/build/transport/streamable-http.d.ts.map +1 -0
- package/build/transport/streamable-http.js +57 -0
- package/build/transport/streamable-http.js.map +1 -0
- package/package.json +107 -4
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare function deriveAesKey(sharedSecret: ArrayBuffer, passphrase: string): Promise<CryptoKey>;
|
|
2
|
+
/**
|
|
3
|
+
* Derive a deterministic 32-byte Ed25519 seed for OAuth JWT signing.
|
|
4
|
+
*
|
|
5
|
+
* Used in HTTP multi-user mode (CREDENTIAL_SECRET set) so every container
|
|
6
|
+
* replica converges on the SAME signing key without a shared volume or
|
|
7
|
+
* external secret store. Domain-separated per server via the HKDF info label.
|
|
8
|
+
*
|
|
9
|
+
* HKDF-SHA256 with IKM = secret, empty salt, info =
|
|
10
|
+
* `mcp-jwt-signing-key-v1:<serverName>`. Byte-identical to core-py's
|
|
11
|
+
* `derive_jwt_signing_seed` for the same inputs (enforced by the shared
|
|
12
|
+
* crypto-vectors fixture).
|
|
13
|
+
*/
|
|
14
|
+
export declare function deriveJwtSigningSeed(secret: string, serverName: string): Buffer;
|
|
15
|
+
//# sourceMappingURL=kdf.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kdf.d.ts","sourceRoot":"","sources":["../../src/crypto/kdf.ts"],"names":[],"mappings":"AAOA,wBAAsB,YAAY,CAAC,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAWpG;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAG/E"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { hkdfSync } from 'node:crypto';
|
|
2
|
+
const encoder = new TextEncoder();
|
|
3
|
+
const INFO = encoder.encode('mcp-relay');
|
|
4
|
+
const JWT_SIGNING_INFO_PREFIX = 'mcp-jwt-signing-key-v1:';
|
|
5
|
+
export async function deriveAesKey(sharedSecret, passphrase) {
|
|
6
|
+
const salt = encoder.encode(passphrase);
|
|
7
|
+
const keyMaterial = await crypto.subtle.importKey('raw', sharedSecret, 'HKDF', false, ['deriveKey']);
|
|
8
|
+
return crypto.subtle.deriveKey({ name: 'HKDF', hash: 'SHA-256', salt, info: INFO }, keyMaterial, { name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Derive a deterministic 32-byte Ed25519 seed for OAuth JWT signing.
|
|
12
|
+
*
|
|
13
|
+
* Used in HTTP multi-user mode (CREDENTIAL_SECRET set) so every container
|
|
14
|
+
* replica converges on the SAME signing key without a shared volume or
|
|
15
|
+
* external secret store. Domain-separated per server via the HKDF info label.
|
|
16
|
+
*
|
|
17
|
+
* HKDF-SHA256 with IKM = secret, empty salt, info =
|
|
18
|
+
* `mcp-jwt-signing-key-v1:<serverName>`. Byte-identical to core-py's
|
|
19
|
+
* `derive_jwt_signing_seed` for the same inputs (enforced by the shared
|
|
20
|
+
* crypto-vectors fixture).
|
|
21
|
+
*/
|
|
22
|
+
export function deriveJwtSigningSeed(secret, serverName) {
|
|
23
|
+
const info = encoder.encode(`${JWT_SIGNING_INFO_PREFIX}${serverName}`);
|
|
24
|
+
return Buffer.from(hkdfSync('sha256', Buffer.from(secret, 'utf-8'), Buffer.alloc(0), info, 32));
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=kdf.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kdf.js","sourceRoot":"","sources":["../../src/crypto/kdf.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAEtC,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;AAEjC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAA;AACxC,MAAM,uBAAuB,GAAG,yBAAyB,CAAA;AAEzD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,YAAyB,EAAE,UAAkB;IAC9E,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAA;IACvC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,WAAW,CAAC,CAAC,CAAA;IAEpG,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAC5B,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,EACnD,WAAW,EACX,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,EAChC,IAAI,EACJ,CAAC,SAAS,EAAE,SAAS,CAAC,CACvB,CAAA;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,UAAkB;IACrE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,uBAAuB,GAAG,UAAU,EAAE,CAAC,CAAA;IACtE,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;AACjG,CAAC"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { authorizationServerMetadata, type CapabilityInfo, type ConfigField, type CredentialsCallback, createDelegatedOAuthApp, createLocalOAuthApp, type DelegatedOAuthAppOptions, type DelegatedOAuthAppResult, type FlowType, type LocalOAuthAppOptions, type LocalOAuthAppResult, type NextStep, type OAuthTokens, protectedResourceMetadata, type RelayConfigSchema, type RenderOptions, renderCredentialForm, type StepCallback, type SubjectContext, type TokenCallback, type UpstreamOAuthConfig } from './auth/index.js';
|
|
2
|
+
export { type BuildCliOptions, buildCli, type CliHandler, type ServeFn } from './cli/build-cli.js';
|
|
3
|
+
export * from './crypto/index.js';
|
|
4
|
+
export { JWTIssuer } from './oauth/jwt-issuer.js';
|
|
5
|
+
export { verifyPKCE } from './oauth/pkce.js';
|
|
6
|
+
export { InMemoryAuthCache, type IOAuthSessionCache, OAuthProvider, type OAuthProviderOptions, type PreAuthSession } from './oauth/provider.js';
|
|
7
|
+
export { type IUserCredentialStore, SqliteUserStore } from './oauth/user-store.js';
|
|
8
|
+
export { tryOpenBrowser } from './relay/browser.js';
|
|
9
|
+
export { createSession, generatePassphrase, notifyComplete, pollForResponses, pollForResult, type RelaySession, sendMessage } from './relay/client.js';
|
|
10
|
+
export { buildOpenRelayHandler, type ElicitationServer, type OpenRelayHandlerOptions, type OpenRelayResult, registerOpenRelayTool, type ToolRegistrar } from './relay/tool-helpers.js';
|
|
11
|
+
export type * from './schema/types.js';
|
|
12
|
+
export { exportConfig, importConfig, listConfigs, scheduleReloadExit } from './storage/config-file.js';
|
|
13
|
+
export { deleteStoredConfig as deleteConfig, readStoredConfig as readConfig, writeStoredConfig as writeConfig } from './storage/credential-store.js';
|
|
14
|
+
export { clearMode, getMode, type ServerMode, setLocalMode } from './storage/mode.js';
|
|
15
|
+
export * from './storage/resolver.js';
|
|
16
|
+
export { acquireSessionLock, releaseSessionLock, type SessionInfo, writeSessionLock } from './storage/session-lock.js';
|
|
17
|
+
export * from './token-auth/index.js';
|
|
18
|
+
export { type HttpRoute, type HttpServerHandle, type RunHttpServerOptions, runHttpServer } from './transport/local-server.js';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,2BAA2B,EAC3B,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,mBAAmB,EACxB,uBAAuB,EACvB,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,EAC5B,KAAK,QAAQ,EACb,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,oBAAoB,EACpB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACzB,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EAAE,KAAK,eAAe,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAClG,cAAc,mBAAmB,CAAA;AAEjC,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EACL,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,aAAa,EACb,KAAK,oBAAoB,EACzB,KAAK,cAAc,EACpB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAAE,KAAK,oBAAoB,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,KAAK,YAAY,EACjB,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,qBAAqB,EACrB,KAAK,aAAa,EACnB,MAAM,yBAAyB,CAAA;AAChC,mBAAmB,mBAAmB,CAAA;AACtC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAGtG,OAAO,EACL,kBAAkB,IAAI,YAAY,EAClC,gBAAgB,IAAI,UAAU,EAC9B,iBAAiB,IAAI,WAAW,EACjC,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrF,cAAc,uBAAuB,CAAA;AACrC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,WAAW,EAChB,gBAAgB,EACjB,MAAM,2BAA2B,CAAA;AAGlC,cAAc,uBAAuB,CAAA;AAErC,OAAO,EACL,KAAK,SAAS,EACd,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EACzB,aAAa,EACd,MAAM,6BAA6B,CAAA"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Local OAuth 2.1 Authorization Server (single-user, 127.0.0.1) +
|
|
2
|
+
// Delegated OAuth (upstream redirect / device code) for remote multi-user.
|
|
3
|
+
export { authorizationServerMetadata, createDelegatedOAuthApp, createLocalOAuthApp, protectedResourceMetadata, renderCredentialForm } from './auth/index.js';
|
|
4
|
+
// Console-script CLI builder shared by every TS MCP server (config/relay/doctor
|
|
5
|
+
// built-ins + version + help; parity with core-py's build_cli).
|
|
6
|
+
export { buildCli } from './cli/build-cli.js';
|
|
7
|
+
export * from './crypto/index.js';
|
|
8
|
+
// OAuth 2.1 multi-user infrastructure (HTTP mode)
|
|
9
|
+
export { JWTIssuer } from './oauth/jwt-issuer.js';
|
|
10
|
+
export { verifyPKCE } from './oauth/pkce.js';
|
|
11
|
+
export { InMemoryAuthCache, OAuthProvider } from './oauth/provider.js';
|
|
12
|
+
export { SqliteUserStore } from './oauth/user-store.js';
|
|
13
|
+
export { tryOpenBrowser } from './relay/browser.js';
|
|
14
|
+
export { createSession, generatePassphrase, notifyComplete, pollForResponses, pollForResult, sendMessage } from './relay/client.js';
|
|
15
|
+
export { buildOpenRelayHandler, registerOpenRelayTool } from './relay/tool-helpers.js';
|
|
16
|
+
export { exportConfig, importConfig, listConfigs, scheduleReloadExit } from './storage/config-file.js';
|
|
17
|
+
// Single-user credential read/write/delete route through the unified
|
|
18
|
+
// per-plugin store (with legacy config.enc read fallback).
|
|
19
|
+
export { deleteStoredConfig as deleteConfig, readStoredConfig as readConfig, writeStoredConfig as writeConfig } from './storage/credential-store.js';
|
|
20
|
+
export { clearMode, getMode, setLocalMode } from './storage/mode.js';
|
|
21
|
+
export * from './storage/resolver.js';
|
|
22
|
+
export { acquireSessionLock, releaseSessionLock, writeSessionLock } from './storage/session-lock.js';
|
|
23
|
+
// HTTP token-table auth (no-auth / token / multi users.toml), the TS port of
|
|
24
|
+
// core-py hull_core.auth; wired into runHttpServer via its `auth` option.
|
|
25
|
+
export * from './token-auth/index.js';
|
|
26
|
+
// HTTP MCP server entry point (OAuth AS + /mcp transport on 127.0.0.1)
|
|
27
|
+
export { runHttpServer } from './transport/local-server.js';
|
|
28
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,2EAA2E;AAC3E,OAAO,EACL,2BAA2B,EAI3B,uBAAuB,EACvB,mBAAmB,EAQnB,yBAAyB,EAGzB,oBAAoB,EAKrB,MAAM,iBAAiB,CAAA;AACxB,gFAAgF;AAChF,gEAAgE;AAChE,OAAO,EAAwB,QAAQ,EAAiC,MAAM,oBAAoB,CAAA;AAClG,cAAc,mBAAmB,CAAA;AACjC,kDAAkD;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAC5C,OAAO,EACL,iBAAiB,EAEjB,aAAa,EAGd,MAAM,qBAAqB,CAAA;AAC5B,OAAO,EAA6B,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAClF,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,aAAa,EAEb,WAAW,EACZ,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,qBAAqB,EAIrB,qBAAqB,EAEtB,MAAM,yBAAyB,CAAA;AAEhC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AACtG,qEAAqE;AACrE,2DAA2D;AAC3D,OAAO,EACL,kBAAkB,IAAI,YAAY,EAClC,gBAAgB,IAAI,UAAU,EAC9B,iBAAiB,IAAI,WAAW,EACjC,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAE,SAAS,EAAE,OAAO,EAAmB,YAAY,EAAE,MAAM,mBAAmB,CAAA;AACrF,cAAc,uBAAuB,CAAA;AACrC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAElB,gBAAgB,EACjB,MAAM,2BAA2B,CAAA;AAClC,6EAA6E;AAC7E,0EAA0E;AAC1E,cAAc,uBAAuB,CAAA;AACrC,uEAAuE;AACvE,OAAO,EAIL,aAAa,EACd,MAAM,6BAA6B,CAAA"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lock-file lifecycle helpers for HTTP server mode.
|
|
3
|
+
*
|
|
4
|
+
* After the stdio-pure + http-multi-user split, lock files are used solely
|
|
5
|
+
* by `runHttpServer` to prevent two HTTP servers binding the same port for
|
|
6
|
+
* the same `serverName`. They are no longer used to discover or steer
|
|
7
|
+
* daemon-bridge processes; per-credState TTLs and PID liveness checks have
|
|
8
|
+
* been removed along with the daemon model.
|
|
9
|
+
*
|
|
10
|
+
* On-disk format (4 lines + trailing newline, padded to 512 bytes):
|
|
11
|
+
*
|
|
12
|
+
* ```
|
|
13
|
+
* {pid}
|
|
14
|
+
* {port}
|
|
15
|
+
* {token}
|
|
16
|
+
* {spawnedAt_iso8601_utc}
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* `sweepStaleLocks` removes lock files whose `spawnedAt` is older than the
|
|
20
|
+
* configured TTL. There is no daemon to terminate — the HTTP server owns
|
|
21
|
+
* its own lifecycle and exits when its parent process exits.
|
|
22
|
+
*
|
|
23
|
+
* TypeScript port of core-py's `mcp_core.lifecycle.lock`. Behaviour kept
|
|
24
|
+
* identical for cross-language parity.
|
|
25
|
+
*/
|
|
26
|
+
export declare const DEFAULT_LOCK_TTL_HOURS = 24;
|
|
27
|
+
/** Parsed lock metadata — shared by sweep and refresh helpers. */
|
|
28
|
+
export interface LockMetadata {
|
|
29
|
+
pid: number;
|
|
30
|
+
port: number;
|
|
31
|
+
token: string;
|
|
32
|
+
spawnedAt: Date;
|
|
33
|
+
}
|
|
34
|
+
export declare function locksDir(root?: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* No-arg lock-dir resolver — sweep tests `vi.spyOn(lockModule, 'lockDir')`
|
|
37
|
+
* to redirect into a temp directory without threading a `root` arg.
|
|
38
|
+
*/
|
|
39
|
+
export declare function lockDir(): string;
|
|
40
|
+
/**
|
|
41
|
+
* Refresh `spawnedAt` of an existing lock file. Silent no-op on legacy /
|
|
42
|
+
* malformed files so callers don't need try/except.
|
|
43
|
+
*
|
|
44
|
+
* Padded to 512 bytes so on-disk size stays stable while a Windows
|
|
45
|
+
* byte-range lock is held past the metadata region (parity with core-py).
|
|
46
|
+
*/
|
|
47
|
+
export declare function refreshLockTimestamp(path: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Remove stale lock files for `serverName`. Returns count removed.
|
|
50
|
+
*
|
|
51
|
+
* Called by `runHttpServer` at startup before writing its own lock,
|
|
52
|
+
* preventing pile-up of dozens of `<server>-<port>.lock` files when HTTP
|
|
53
|
+
* servers exit abnormally (Windows OOM, taskkill, signal).
|
|
54
|
+
*
|
|
55
|
+
* A lock is stale when:
|
|
56
|
+
* - file is unreadable or has malformed payload (legacy / corrupt)
|
|
57
|
+
* - `spawnedAt` is older than the TTL
|
|
58
|
+
*/
|
|
59
|
+
export declare function sweepStaleLocks(serverName: string, ttlHours?: number, root?: string): number;
|
|
60
|
+
/**
|
|
61
|
+
* Write the 4-line lock payload (pid, port, token, ISO timestamp) padded
|
|
62
|
+
* to 512 bytes. Used by `runHttpServer` after the OS file lock is
|
|
63
|
+
* acquired.
|
|
64
|
+
*
|
|
65
|
+
* Returns the absolute path to the lock file.
|
|
66
|
+
*/
|
|
67
|
+
export declare function writeLockFile(serverName: string, port: number, token: string, root?: string): string;
|
|
68
|
+
//# sourceMappingURL=lock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/lifecycle/lock.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAcH,eAAO,MAAM,sBAAsB,KAAK,CAAA;AAExC,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,IAAI,CAAA;CAChB;AAED,wBAAgB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;;GAGG;AACH,wBAAgB,OAAO,IAAI,MAAM,CAEhC;AAeD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAiBvD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAoD5F;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAMpG"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lock-file lifecycle helpers for HTTP server mode.
|
|
3
|
+
*
|
|
4
|
+
* After the stdio-pure + http-multi-user split, lock files are used solely
|
|
5
|
+
* by `runHttpServer` to prevent two HTTP servers binding the same port for
|
|
6
|
+
* the same `serverName`. They are no longer used to discover or steer
|
|
7
|
+
* daemon-bridge processes; per-credState TTLs and PID liveness checks have
|
|
8
|
+
* been removed along with the daemon model.
|
|
9
|
+
*
|
|
10
|
+
* On-disk format (4 lines + trailing newline, padded to 512 bytes):
|
|
11
|
+
*
|
|
12
|
+
* ```
|
|
13
|
+
* {pid}
|
|
14
|
+
* {port}
|
|
15
|
+
* {token}
|
|
16
|
+
* {spawnedAt_iso8601_utc}
|
|
17
|
+
* ```
|
|
18
|
+
*
|
|
19
|
+
* `sweepStaleLocks` removes lock files whose `spawnedAt` is older than the
|
|
20
|
+
* configured TTL. There is no daemon to terminate — the HTTP server owns
|
|
21
|
+
* its own lifecycle and exits when its parent process exits.
|
|
22
|
+
*
|
|
23
|
+
* TypeScript port of core-py's `mcp_core.lifecycle.lock`. Behaviour kept
|
|
24
|
+
* identical for cross-language parity.
|
|
25
|
+
*/
|
|
26
|
+
import { existsSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
27
|
+
import { homedir } from 'node:os';
|
|
28
|
+
import { join } from 'node:path';
|
|
29
|
+
// Self-import so `sweepStaleLocks` can resolve `lockDir` through the
|
|
30
|
+
// module namespace at call time. This makes
|
|
31
|
+
// `vi.spyOn(lockModule, 'lockDir').mockReturnValue(...)` actually
|
|
32
|
+
// intercept the helper from inside the sweep loop — without this
|
|
33
|
+
// indirection, the closure-captured local binding ignores spies and
|
|
34
|
+
// test mocks become silent no-ops.
|
|
35
|
+
import * as self from './lock.js';
|
|
36
|
+
export const DEFAULT_LOCK_TTL_HOURS = 24;
|
|
37
|
+
export function locksDir(root) {
|
|
38
|
+
return root ?? join(homedir(), '.config', 'mcp', 'locks');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* No-arg lock-dir resolver — sweep tests `vi.spyOn(lockModule, 'lockDir')`
|
|
42
|
+
* to redirect into a temp directory without threading a `root` arg.
|
|
43
|
+
*/
|
|
44
|
+
export function lockDir() {
|
|
45
|
+
return locksDir();
|
|
46
|
+
}
|
|
47
|
+
/** Internal helper: parse the textual lock payload into `LockMetadata`. */
|
|
48
|
+
function parseLockText(raw) {
|
|
49
|
+
const lines = raw.replace(/\n+$/, '').split('\n');
|
|
50
|
+
if (lines.length < 4)
|
|
51
|
+
return null;
|
|
52
|
+
const pid = Number(lines[0]);
|
|
53
|
+
const port = Number(lines[1]);
|
|
54
|
+
if (!Number.isInteger(pid) || !Number.isInteger(port))
|
|
55
|
+
return null;
|
|
56
|
+
const token = lines[2];
|
|
57
|
+
const spawnedAt = new Date(lines[3]);
|
|
58
|
+
if (Number.isNaN(spawnedAt.getTime()))
|
|
59
|
+
return null;
|
|
60
|
+
return { pid, port, token, spawnedAt };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Refresh `spawnedAt` of an existing lock file. Silent no-op on legacy /
|
|
64
|
+
* malformed files so callers don't need try/except.
|
|
65
|
+
*
|
|
66
|
+
* Padded to 512 bytes so on-disk size stays stable while a Windows
|
|
67
|
+
* byte-range lock is held past the metadata region (parity with core-py).
|
|
68
|
+
*/
|
|
69
|
+
export function refreshLockTimestamp(path) {
|
|
70
|
+
if (!existsSync(path))
|
|
71
|
+
return;
|
|
72
|
+
let raw;
|
|
73
|
+
try {
|
|
74
|
+
raw = readFileSync(path, { encoding: 'utf-8' });
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const md = parseLockText(raw);
|
|
80
|
+
if (md === null)
|
|
81
|
+
return;
|
|
82
|
+
const now = new Date().toISOString();
|
|
83
|
+
const payload = `${md.pid}\n${md.port}\n${md.token}\n${now}\n`;
|
|
84
|
+
try {
|
|
85
|
+
writeFileSync(path, payload.padEnd(512, ' '), { encoding: 'utf-8' });
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Best-effort
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Remove stale lock files for `serverName`. Returns count removed.
|
|
93
|
+
*
|
|
94
|
+
* Called by `runHttpServer` at startup before writing its own lock,
|
|
95
|
+
* preventing pile-up of dozens of `<server>-<port>.lock` files when HTTP
|
|
96
|
+
* servers exit abnormally (Windows OOM, taskkill, signal).
|
|
97
|
+
*
|
|
98
|
+
* A lock is stale when:
|
|
99
|
+
* - file is unreadable or has malformed payload (legacy / corrupt)
|
|
100
|
+
* - `spawnedAt` is older than the TTL
|
|
101
|
+
*/
|
|
102
|
+
export function sweepStaleLocks(serverName, ttlHours, root) {
|
|
103
|
+
const dir = root !== undefined ? root : self.lockDir();
|
|
104
|
+
if (!existsSync(dir))
|
|
105
|
+
return 0;
|
|
106
|
+
let removed = 0;
|
|
107
|
+
let entries;
|
|
108
|
+
try {
|
|
109
|
+
entries = readdirSync(dir);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return 0;
|
|
113
|
+
}
|
|
114
|
+
const prefix = `${serverName}-`;
|
|
115
|
+
const suffix = '.lock';
|
|
116
|
+
const now = Date.now();
|
|
117
|
+
const ttlMs = (ttlHours ?? DEFAULT_LOCK_TTL_HOURS) * 3600 * 1000;
|
|
118
|
+
for (const entry of entries) {
|
|
119
|
+
if (!entry.startsWith(prefix) || !entry.endsWith(suffix))
|
|
120
|
+
continue;
|
|
121
|
+
const full = join(dir, entry);
|
|
122
|
+
let raw;
|
|
123
|
+
try {
|
|
124
|
+
raw = readFileSync(full, { encoding: 'utf-8' });
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
try {
|
|
128
|
+
unlinkSync(full);
|
|
129
|
+
removed += 1;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
/* ignore */
|
|
133
|
+
}
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const meta = parseLockText(raw);
|
|
137
|
+
if (meta === null) {
|
|
138
|
+
try {
|
|
139
|
+
unlinkSync(full);
|
|
140
|
+
removed += 1;
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
/* ignore */
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (now - meta.spawnedAt.getTime() > ttlMs) {
|
|
148
|
+
try {
|
|
149
|
+
unlinkSync(full);
|
|
150
|
+
removed += 1;
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
/* ignore */
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return removed;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Write the 4-line lock payload (pid, port, token, ISO timestamp) padded
|
|
161
|
+
* to 512 bytes. Used by `runHttpServer` after the OS file lock is
|
|
162
|
+
* acquired.
|
|
163
|
+
*
|
|
164
|
+
* Returns the absolute path to the lock file.
|
|
165
|
+
*/
|
|
166
|
+
export function writeLockFile(serverName, port, token, root) {
|
|
167
|
+
const dir = locksDir(root);
|
|
168
|
+
const path = join(dir, `${serverName}-${port}.lock`);
|
|
169
|
+
const payload = `${process.pid}\n${port}\n${token}\n${new Date().toISOString()}\n`;
|
|
170
|
+
writeFileSync(path, payload.padEnd(512, ' '), { encoding: 'utf-8', mode: 0o600 });
|
|
171
|
+
return path;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/lifecycle/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface PreAuthSession {
|
|
2
|
+
sessionId: string;
|
|
3
|
+
clientId: string;
|
|
4
|
+
redirectUri: string;
|
|
5
|
+
state: string;
|
|
6
|
+
codeChallenge: string;
|
|
7
|
+
codeChallengeMethod: string;
|
|
8
|
+
/** Serialized CryptoKeyPair for later credential retrieval */
|
|
9
|
+
keyPairJwk: JsonWebKey;
|
|
10
|
+
passphrase: string;
|
|
11
|
+
expiresAt: number;
|
|
12
|
+
}
|
|
13
|
+
export interface IOAuthSessionCache {
|
|
14
|
+
save(session: PreAuthSession): void;
|
|
15
|
+
getAndDelete(sessionId: string): PreAuthSession | null;
|
|
16
|
+
}
|
|
17
|
+
export declare class InMemoryAuthCache implements IOAuthSessionCache {
|
|
18
|
+
private cache;
|
|
19
|
+
save(session: PreAuthSession): void;
|
|
20
|
+
getAndDelete(sessionId: string): PreAuthSession | null;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/oauth/cache.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,KAAK,EAAE,MAAM,CAAA;IACb,aAAa,EAAE,MAAM,CAAA;IACrB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,8DAA8D;IAC9D,UAAU,EAAE,UAAU,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IACnC,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAAA;CACvD;AAED,qBAAa,iBAAkB,YAAW,kBAAkB;IAC1D,OAAO,CAAC,KAAK,CAAoC;IAEjD,IAAI,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAOlC;IAED,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAMrD;CACF"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export class InMemoryAuthCache {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.cache = new Map();
|
|
4
|
+
}
|
|
5
|
+
save(session) {
|
|
6
|
+
this.cache.set(session.sessionId, session);
|
|
7
|
+
// Cleanup expired entries
|
|
8
|
+
const now = Math.floor(Date.now() / 1000);
|
|
9
|
+
for (const [id, sess] of this.cache) {
|
|
10
|
+
if (sess.expiresAt < now)
|
|
11
|
+
this.cache.delete(id);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
getAndDelete(sessionId) {
|
|
15
|
+
const sess = this.cache.get(sessionId);
|
|
16
|
+
if (!sess)
|
|
17
|
+
return null;
|
|
18
|
+
this.cache.delete(sessionId);
|
|
19
|
+
if (sess.expiresAt < Math.floor(Date.now() / 1000))
|
|
20
|
+
return null;
|
|
21
|
+
return sess;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/oauth/cache.ts"],"names":[],"mappings":"AAkBA,MAAM,OAAO,iBAAiB;IAA9B;QACU,UAAK,GAAG,IAAI,GAAG,EAA0B,CAAA;IAkBnD,CAAC;IAhBC,IAAI,CAAC,OAAuB;QAC1B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QAC1C,0BAA0B;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;QACzC,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YACpC,IAAI,IAAI,CAAC,SAAS,GAAG,GAAG;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QACjD,CAAC;IACH,CAAC;IAED,YAAY,CAAC,SAAiB;QAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAA;QACtC,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,CAAA;QACtB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAA;QAC5B,IAAI,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;YAAE,OAAO,IAAI,CAAA;QAC/D,OAAO,IAAI,CAAA;IACb,CAAC;CACF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { InMemoryAuthCache, type IOAuthSessionCache, type PreAuthSession } from './cache.js';
|
|
2
|
+
export { JWTIssuer } from './jwt-issuer.js';
|
|
3
|
+
export { verifyPKCE } from './pkce.js';
|
|
4
|
+
export { OAuthProvider, type OAuthProviderOptions } from './provider.js';
|
|
5
|
+
export { type IUserCredentialStore, SqliteUserStore } from './user-store.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,kBAAkB,EACvB,KAAK,cAAc,EACpB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,EACL,aAAa,EACb,KAAK,oBAAoB,EAC1B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,KAAK,oBAAoB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { InMemoryAuthCache } from './cache.js';
|
|
2
|
+
export { JWTIssuer } from './jwt-issuer.js';
|
|
3
|
+
export { verifyPKCE } from './pkce.js';
|
|
4
|
+
export { OAuthProvider } from './provider.js';
|
|
5
|
+
export { SqliteUserStore } from './user-store.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/oauth/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EAGlB,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AACtC,OAAO,EACL,aAAa,EAEd,MAAM,eAAe,CAAA;AACtB,OAAO,EAA6B,eAAe,EAAE,MAAM,iBAAiB,CAAA"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JWT Issuer and JWKS generation for TypeScript MCP servers.
|
|
3
|
+
* Uses jose (industry-standard, zero-dep, Web Crypto compatible).
|
|
4
|
+
*
|
|
5
|
+
* Two signing modes, selected at construction:
|
|
6
|
+
*
|
|
7
|
+
* - LOCAL single-user (credentialSecret unset): RSA-2048, RS256, kid="key-1",
|
|
8
|
+
* keys generated on first run and persisted to disk so they survive restarts
|
|
9
|
+
* on a real machine. Behavior is unchanged from the pre-stability-fix code.
|
|
10
|
+
*
|
|
11
|
+
* - HTTP multi-user (credentialSecret set): Ed25519, EdDSA, signing key DERIVED
|
|
12
|
+
* deterministically from CREDENTIAL_SECRET via HKDF-SHA256. No disk I/O. Every
|
|
13
|
+
* container replica converges on the same key without a shared volume or
|
|
14
|
+
* external secret store, so OAuth tokens survive container recreation
|
|
15
|
+
* (Watchtower :latest redeploys). kid is the base64url SHA-256 thumbprint of
|
|
16
|
+
* the raw public key.
|
|
17
|
+
*
|
|
18
|
+
* The two modes are different deployments that never exchange tokens
|
|
19
|
+
* (iss/aud are server-scoped); the per-mode algorithm split is permanent and
|
|
20
|
+
* intentional. Each process runs exactly one algorithm; the verify path uses
|
|
21
|
+
* that single alg, never a {RS256, EdDSA} union.
|
|
22
|
+
*/
|
|
23
|
+
import * as jose from 'jose';
|
|
24
|
+
export declare class JWTIssuer {
|
|
25
|
+
private serverName;
|
|
26
|
+
private keysDir;
|
|
27
|
+
private privateKeyPath;
|
|
28
|
+
private publicKeyPath;
|
|
29
|
+
private credentialSecret;
|
|
30
|
+
alg: 'RS256' | 'EdDSA';
|
|
31
|
+
private kid;
|
|
32
|
+
private privateKey;
|
|
33
|
+
private publicKey;
|
|
34
|
+
private okpX;
|
|
35
|
+
private _initialized;
|
|
36
|
+
constructor(serverName: string, keysDir?: string, credentialSecret?: string | null);
|
|
37
|
+
/** Must call before using issuer — loads/generates (RSA) or derives (EdDSA) keys. */
|
|
38
|
+
init(): Promise<void>;
|
|
39
|
+
private deriveEddsaKeys;
|
|
40
|
+
private loadOrGenerateRsaKeys;
|
|
41
|
+
/**
|
|
42
|
+
* Return JWKS payload for /.well-known/jwks.json. Always a `keys` array
|
|
43
|
+
* (multi-key-aware for future rotation). RSA mode emits an RSA JWK; EdDSA
|
|
44
|
+
* mode emits an OKP JWK.
|
|
45
|
+
*/
|
|
46
|
+
getJwks(): Promise<jose.JSONWebKeySet>;
|
|
47
|
+
/** Issue a JWT access token (typ="access") signed with the active alg. */
|
|
48
|
+
issueAccessToken(sub: string, expiresInSeconds?: number): Promise<string>;
|
|
49
|
+
/**
|
|
50
|
+
* Issue a JWT refresh token (typ="refresh") signed with the active alg.
|
|
51
|
+
*
|
|
52
|
+
* Defaults to a 1-year (31536000s) lifetime so long-running MCP clients can
|
|
53
|
+
* mint fresh access tokens without forcing the user back through the browser
|
|
54
|
+
* PKCE flow. The access token stays short-lived (1h); the refresh token is the
|
|
55
|
+
* renewal credential and is rotated on every use (the /token refresh handler
|
|
56
|
+
* issues a new refresh token each time), so the security control is rotation,
|
|
57
|
+
* not a short TTL. A short refresh TTL was the residual re-auth driver: a
|
|
58
|
+
* self-hosted server a user touches only intermittently (less than once a
|
|
59
|
+
* month) would silently expire its refresh token between sessions, forcing a
|
|
60
|
+
* fresh browser OAuth tab on the next use. With a 1-year floor, only a
|
|
61
|
+
* genuinely long idle gap re-prompts. Same key / iss / aud as access tokens;
|
|
62
|
+
* the typ claim is the only thing distinguishing them, and verifyAccessToken
|
|
63
|
+
* rejects typ="refresh" so a refresh token can never be used as an access
|
|
64
|
+
* token at the /mcp resource.
|
|
65
|
+
*/
|
|
66
|
+
issueRefreshToken(sub: string, expiresInSeconds?: number): Promise<string>;
|
|
67
|
+
/**
|
|
68
|
+
* Verify a JWT access token and return its payload. Throws on failure (bad
|
|
69
|
+
* signature, wrong issuer/audience, expired) — the same jose errors existing
|
|
70
|
+
* callers already catch. The verify path uses the single active algorithm,
|
|
71
|
+
* never a union. Additionally rejects tokens whose typ claim is "refresh"
|
|
72
|
+
* (throwing jose.errors.JWTClaimValidationFailed) so a refresh token cannot
|
|
73
|
+
* be replayed as an access token. Tokens with typ="access" OR a missing typ
|
|
74
|
+
* claim are accepted (the latter keeps already-issued pre-refresh-support
|
|
75
|
+
* tokens valid).
|
|
76
|
+
*/
|
|
77
|
+
verifyAccessToken(token: string): Promise<jose.JWTPayload>;
|
|
78
|
+
/**
|
|
79
|
+
* Verify a JWT refresh token and return its payload. Same key / audience /
|
|
80
|
+
* issuer checks as verifyAccessToken and throws the same jose errors on
|
|
81
|
+
* failure; uses the single active algorithm. Additionally asserts
|
|
82
|
+
* typ=="refresh" (throwing jose.errors.JWTClaimValidationFailed otherwise) so
|
|
83
|
+
* an access token can never be exchanged at the refresh grant.
|
|
84
|
+
*/
|
|
85
|
+
verifyRefreshToken(token: string): Promise<jose.JWTPayload>;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=jwt-issuer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jwt-issuer.d.ts","sourceRoot":"","sources":["../../src/oauth/jwt-issuer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAA;AAU5B,qBAAa,SAAS;IACpB,OAAO,CAAC,UAAU,CAAQ;IAC1B,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,gBAAgB,CAAe;IACvC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAA;IACtB,OAAO,CAAC,GAAG,CAAU;IACrB,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,SAAS,CAA8B;IAC/C,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,YAAY,CAAQ;IAE5B,YAAY,UAAU,EAAE,MAAM,EAAE,OAAO,SAAmB,EAAE,gBAAgB,GAAE,MAAM,GAAG,IAAW,EAOjG;IAED,qFAAqF;IAC/E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAc1B;YAEa,eAAe;YAYf,qBAAqB;IA6BnC;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAY3C;IAED,0EAA0E;IACpE,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,SAAO,GAAG,OAAO,CAAC,MAAM,CAAC,CAS5E;IAED;;;;;;;;;;;;;;;;OAgBG;IACG,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,gBAAgB,SAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CASjF;IAED;;;;;;;;;OASG;IACG,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAW/D;IAED;;;;;;OAMG;IACG,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,CAWhE;CACF"}
|