@ractive-ch/tab-mcp 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,7 +10,7 @@ and gets an MCP address. The user adds that address to an AI app and pairs it by
10
10
  a code from the page on the relay's sign-in page. The relay then forwards the AI app's
11
11
  requests to the page. It knows nothing about the tools themselves.
12
12
 
13
- **Status: 0.2.0.** Until 1.0, a new minor version can still change settings and the API.
13
+ **Status: 0.3.0.** Until 1.0, a new minor version can still change settings and the API.
14
14
  The relay speaks MCP revision 2026-07-28 only and accepts AI apps that register through
15
15
  OAuth Client ID Metadata Documents; see [compatibility](https://github.com/ractive/tab-mcp/blob/main/docs/compatibility.md)
16
16
  for which apps work.
@@ -32,7 +32,7 @@ Or with Docker (linux/amd64 and linux/arm64):
32
32
  docker run -d -p 127.0.0.1:8080:8080 -v tab-mcp-data:/data \
33
33
  -e TAB_MCP_PUBLIC_URL=https://relay.example.com \
34
34
  -e TAB_MCP_ALLOWED_ORIGINS=https://app.example.com \
35
- ractive/tab-mcp:0.2.0
35
+ ractive/tab-mcp:0.3.0
36
36
  ```
37
37
 
38
38
  The image is also on `ghcr.io/ractive/tab-mcp`. Put the relay behind a reverse proxy
@@ -62,6 +62,7 @@ The most important:
62
62
  | `TAB_MCP_SQLITE_PATH` | `tab-mcp.db` | the SQLite file for grants and token hashes |
63
63
  | `TAB_MCP_TRUST_PROXY` | `none` | `x-real-ip` or `x-forwarded-for` behind a proxy |
64
64
  | `TAB_MCP_PORT` | `8080` | listen port |
65
+ | `TAB_MCP_IDENTITY` | `code` | `oidc` pairs AI apps by the same sign-in at your OpenID Connect provider instead of a code (identity binding; needs the `TAB_MCP_OIDC_*` and `TAB_MCP_TAB_ASSERTION_*` settings) |
65
66
 
66
67
  `GET /healthz` answers `200` with the version, for health checks.
67
68
 
package/config.d.ts CHANGED
@@ -7,9 +7,27 @@ export type LogLevel = "error" | "warn" | "info" | "debug";
7
7
  export type StoreKind = "sqlite" | "memory";
8
8
  /**
9
9
  * How a browser at `/authorize` proves it belongs to the tab's user (DEC-042):
10
- * the 6-digit code shown in the tab (DEC-028), or, later, identity binding.
10
+ * the 6-digit code shown in the tab (DEC-028), or identity binding at an
11
+ * OpenID Connect provider (DEC-075).
11
12
  */
12
13
  export type PairingKind = "code" | "oidc";
14
+ /** Claims the relay may compare as the subject (DEC-079); never mutable ones. */
15
+ export type SubjectClaim = "sub" | "oid";
16
+ /** The OpenID Connect provider and the tab assertion signer (DEC-079). */
17
+ export interface OidcConfig {
18
+ /** The provider's issuer (discovery at `/.well-known/openid-configuration`). */
19
+ issuer: string;
20
+ /** The relay's client at the provider. */
21
+ clientId: string;
22
+ /** Its secret; absent for a public client (PKCE only). */
23
+ clientSecret?: string;
24
+ /** The ID token claim compared with the tab's subject; default `sub`. */
25
+ subjectClaim?: SubjectClaim;
26
+ /** Expected `iss` of the tab assertions (the web app's backend). */
27
+ assertionIssuer: string;
28
+ /** JWKS of the web app's backend that signs tab assertions. */
29
+ assertionJwksUrl: string;
30
+ }
13
31
  /** The limits a site may set for its own tab sessions (DEC-045). */
14
32
  export interface SiteLimits {
15
33
  maxMessageBytes: number;
@@ -26,7 +44,7 @@ export interface SiteConfig {
26
44
  origin: string;
27
45
  /** The app name `/authorize` shows; wins over the name the page sends. */
28
46
  name?: string;
29
- /** Pairing method; `code` (the default) is the only one so far. */
47
+ /** Pairing method; default `identity` of the instance (`code`). */
30
48
  pairing?: PairingKind;
31
49
  /** Overrides of the instance-wide limits for this site's sessions. */
32
50
  limits?: Partial<SiteLimits>;
@@ -61,6 +79,13 @@ export interface RelayConfig {
61
79
  allowedOrigins: string[] | "*";
62
80
  /** Sites with their own name, pairing method or limits (DEC-045). */
63
81
  sites: SiteConfig[];
82
+ /**
83
+ * The pairing of sites without their own (`allowedOrigins` included):
84
+ * `code` (default, DEC-028) or `oidc` (identity binding, DEC-079).
85
+ */
86
+ identity: PairingKind;
87
+ /** The provider and assertion signer; required when a site uses `oidc`. */
88
+ oidc?: OidcConfig;
64
89
  /** Hosts whose Client ID Metadata Documents are accepted; empty = any. */
65
90
  clientIdHosts: string[];
66
91
  trustProxy: TrustProxy;
package/exchange.d.ts CHANGED
@@ -15,6 +15,7 @@ export declare const RelayErrorCode: {
15
15
  readonly tooLarge: -31006;
16
16
  readonly badResponse: -31007;
17
17
  readonly storeUnavailable: -31008;
18
+ readonly signedOut: -31009;
18
19
  };
19
20
  export type CancelReason = "client_closed" | "timeout" | "error";
20
21
  /**
package/identity.d.ts ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Identity binding (DEC-075 to DEC-079): the tab assertion the web app's
3
+ * backend signs for its user, and the key an address is bound to.
4
+ */
5
+ import type { OidcConfig } from "./config.js";
6
+ import { type KeySource } from "./jwt.js";
7
+ /** The `typ` header of a tab assertion (explicit typing, RFC 8725 §3.11). */
8
+ export declare const TAB_ASSERTION_TYP = "tab-mcp-identity+jwt";
9
+ /** Longest lifetime of a tab assertion. */
10
+ export declare const TAB_ASSERTION_MAX_LIFETIME_MS = 60000;
11
+ /** Clock leeway for `exp`, `iat` and `nbf`. */
12
+ export declare const CLOCK_LEEWAY_MS = 5000;
13
+ /** The text for a person other than the page's user (DEC-077, DEC-079). */
14
+ export declare const MISMATCH_TEXT = "This AI tool is logged in as someone other than the page.";
15
+ /**
16
+ * The binding key: SHA-256 over the provider's issuer and the subject, so the
17
+ * subject is never stored in the clear (DEC-077).
18
+ */
19
+ export declare function identityKey(issuer: string, subject: string): string;
20
+ /** Verifies tab assertions (DEC-076) and remembers used `jti`s. */
21
+ export declare class TabAssertionVerifier {
22
+ #private;
23
+ constructor(oidc: OidcConfig, keys?: KeySource);
24
+ /**
25
+ * Check an assertion for an MCP address; returns the binding key. Throws
26
+ * `JwtError` with a short reason for the log.
27
+ */
28
+ verify(token: string, address: string, now?: number): Promise<string>;
29
+ }
package/index.d.ts CHANGED
@@ -3,7 +3,8 @@
3
3
  * a web page open in a browser tab.
4
4
  */
5
5
  export { addressIdFromSecret, newSecret } from "./address.js";
6
- export { ConfigError, configFromEnv, DEFAULT_CONFIG, type LogLevel, type RelayConfig, resolveConfig, type StoreKind, type TrustProxy, } from "./config.js";
6
+ export { ConfigError, configFromEnv, DEFAULT_CONFIG, type LogLevel, type OidcConfig, type PairingKind, type RelayConfig, resolveConfig, type StoreKind, type SubjectClaim, type TrustProxy, } from "./config.js";
7
+ export { TAB_ASSERTION_TYP } from "./identity.js";
7
8
  export { createLogger, type LogFields, type Logger } from "./log.js";
8
9
  export { SUBPROTOCOL } from "./protocol.js";
9
10
  export { type CreateRelayOptions, createRelay, type Relay, VERSION, } from "./relay.js";