@wrongstack/mcp 1.0.19 → 1.0.26

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.
@@ -0,0 +1,24 @@
1
+ export interface MCPOAuthCallbackServerOptions {
2
+ /** Fixed port when the authorization server requires a preregistered URI. */
3
+ port?: number | undefined;
4
+ path?: string | undefined;
5
+ timeoutMs?: number | undefined;
6
+ signal?: AbortSignal | undefined;
7
+ }
8
+ export interface MCPOAuthCallbackServer {
9
+ /** Exact redirect URI to pass to the authorization request. */
10
+ redirectUri: string;
11
+ /** Resolves with the full callback URL the browser was redirected to. */
12
+ waitForCallback(): Promise<string>;
13
+ close(): void;
14
+ }
15
+ /**
16
+ * Loopback redirect receiver for the manual OAuth flow.
17
+ *
18
+ * Without this the user had to copy the callback URL out of the browser address
19
+ * bar and paste it back into a second command. The listener binds 127.0.0.1
20
+ * only — never a routable interface — and answers exactly one path, so nothing
21
+ * else on the machine can reach it or enumerate its responses.
22
+ */
23
+ export declare function startMcpOAuthCallbackServer(options?: MCPOAuthCallbackServerOptions): Promise<MCPOAuthCallbackServer>;
24
+ //# sourceMappingURL=authorization-callback-server.d.ts.map
@@ -0,0 +1,96 @@
1
+ export interface MCPAuthorizationChallenge {
2
+ status: 401;
3
+ resource: string;
4
+ resourceMetadataUrl?: string | undefined;
5
+ scopes: string[];
6
+ rawScheme: 'Bearer';
7
+ }
8
+ export interface MCPProtectedResourceMetadata {
9
+ resource: string;
10
+ authorizationServers: string[];
11
+ scopesSupported: string[];
12
+ }
13
+ export interface MCPAuthorizationServerMetadata {
14
+ issuer: string;
15
+ authorizationEndpoint: string;
16
+ tokenEndpoint: string;
17
+ registrationEndpoint?: string | undefined;
18
+ authorizationResponseIssuerParameterSupported?: boolean | undefined;
19
+ scopesSupported: string[];
20
+ }
21
+ export interface MCPAuthorizationDiscoveryResult {
22
+ resourceMetadataUrl: string;
23
+ authorizationServerMetadataUrl: string;
24
+ protectedResource: MCPProtectedResourceMetadata;
25
+ authorizationServer: MCPAuthorizationServerMetadata;
26
+ }
27
+ export type MCPAuthorizationJsonFetcher = (url: string, signal?: AbortSignal | undefined) => Promise<unknown | undefined>;
28
+ export interface MCPAuthorizationDiscoveryOptions {
29
+ challengeHeader?: string | null | undefined;
30
+ signal?: AbortSignal | undefined;
31
+ timeoutMs?: number | undefined;
32
+ maxResponseBytes?: number | undefined;
33
+ lookup?: BrowserCompatibleDnsLookup | undefined;
34
+ /** Test/host override. Production callers should use the pinned default. */
35
+ fetchJson?: MCPAuthorizationJsonFetcher | undefined;
36
+ }
37
+ export type BrowserCompatibleDnsLookup = (hostname: string) => Promise<readonly {
38
+ address: string;
39
+ family: number;
40
+ }[]>;
41
+ /** Non-2xx answer from an OAuth endpoint, carrying the HTTP status. */
42
+ export declare class MCPOAuthHttpError extends Error {
43
+ readonly status: number;
44
+ readonly name = "MCPOAuthHttpError";
45
+ constructor(message: string, status: number);
46
+ }
47
+ export declare function canonicalMcpResource(rawUrl: string): string;
48
+ export declare function parseMcpBearerChallenge(header: string | null, resource: string): MCPAuthorizationChallenge;
49
+ /** RFC 9728 fallback order for an MCP endpoint when no challenge URL exists. */
50
+ export declare function protectedResourceMetadataUrls(resource: string): string[];
51
+ /** RFC 8414 + OIDC discovery order required by the MCP authorization spec. */
52
+ export declare function authorizationServerMetadataUrls(issuer: string): string[];
53
+ export declare function parseProtectedResourceMetadata(value: unknown, expectedResource: string): MCPProtectedResourceMetadata;
54
+ export declare function parseAuthorizationServerMetadata(value: unknown, expectedIssuer: string): MCPAuthorizationServerMetadata;
55
+ /**
56
+ * Discover and validate MCP OAuth metadata without following redirects. The
57
+ * default fetcher resolves once and opens the socket to that exact IP, making
58
+ * discovery resistant to DNS rebinding.
59
+ */
60
+ export declare function discoverMcpAuthorization(resource: string, options?: MCPAuthorizationDiscoveryOptions): Promise<MCPAuthorizationDiscoveryResult>;
61
+ export declare function challengeParameter(parameters: string, name: string): string | undefined;
62
+ export declare function validateMetadataUrl(value: string): string | undefined;
63
+ export declare function record(value: unknown, label: string): Record<string, unknown>;
64
+ export declare function requiredString(value: unknown, field: string): string;
65
+ export declare function optionalString(value: unknown, field: string): string | undefined;
66
+ export declare function optionalBoolean(value: unknown, field: string): boolean | undefined;
67
+ export declare function boundedStringArray(value: unknown, field: string, maxItems: number): string[];
68
+ export declare function optionalStringArray(value: unknown, field: string, maxItems: number): string[];
69
+ export declare function secureOAuthUrl(value: string, label: string): URL;
70
+ export declare function discoverFirst<T>(candidates: readonly string[], fetchJson: MCPAuthorizationJsonFetcher, signal: AbortSignal | undefined, parse: (value: unknown) => T, label: string): Promise<{
71
+ url: string;
72
+ value: T;
73
+ }>;
74
+ export declare function requestPinnedJson(rawUrl: string, options: {
75
+ method?: 'GET' | 'POST' | undefined;
76
+ body?: string | undefined;
77
+ headers?: Record<string, string> | undefined;
78
+ signal?: AbortSignal | undefined;
79
+ timeoutMs?: number | undefined;
80
+ maxResponseBytes?: number | undefined;
81
+ lookup?: BrowserCompatibleDnsLookup | undefined;
82
+ allowedLoopbackHostname?: string | undefined;
83
+ /** Names the endpoint in errors — token calls were reported as "discovery". */
84
+ label?: string | undefined;
85
+ }): Promise<unknown | undefined>;
86
+ export declare function resolvePinnedAddress(url: URL, options: {
87
+ lookup?: BrowserCompatibleDnsLookup | undefined;
88
+ allowedLoopbackHostname?: string | undefined;
89
+ }): Promise<{
90
+ address: string;
91
+ family: 4 | 6;
92
+ }>;
93
+ export declare function assertDiscoveryAddressAllowed(address: string, family: 4 | 6, hostname: string, allowedLoopbackHostname: string | undefined): void;
94
+ export declare function unbracket(hostname: string): string;
95
+ export declare function isLoopbackHttp(url: URL): boolean;
96
+ //# sourceMappingURL=authorization-discovery.d.ts.map
@@ -1,24 +1,50 @@
1
1
  import { type MCPAuthorizationDiscoveryOptions, type MCPAuthorizationDiscoveryResult, type MCPTokenExchangeOptions, type MCPTokenSet } from './authorization.js';
2
+ import { type MCPClientRegistration, type MCPClientRegistrationOptions } from './authorization-registration.js';
2
3
  import type { MCPAuthorizationStateEvent, MCPVaultTokenStore } from './token-store.js';
3
4
  type DiscoverAuthorization = (resource: string, options?: MCPAuthorizationDiscoveryOptions) => Promise<MCPAuthorizationDiscoveryResult>;
4
5
  type ExchangeAuthorizationCode = (options: MCPTokenExchangeOptions) => Promise<MCPTokenSet>;
6
+ type RegisterClient = (options: MCPClientRegistrationOptions) => Promise<MCPClientRegistration>;
5
7
  export interface MCPAuthorizationManagerOptions {
6
8
  store: MCPVaultTokenStore;
7
9
  pendingTtlMs?: number | undefined;
8
10
  discover?: DiscoverAuthorization | undefined;
9
11
  exchange?: ExchangeAuthorizationCode | undefined;
12
+ register?: RegisterClient | undefined;
13
+ /** `client_name` sent during dynamic registration. */
14
+ clientName?: string | undefined;
10
15
  now?: (() => number) | undefined;
11
16
  onStateChange?: ((event: MCPAuthorizationStateEvent) => void) | undefined;
12
17
  }
13
18
  export interface MCPAuthorizationStartInput {
14
19
  serverName: string;
15
20
  resource: string;
16
- clientId: string;
21
+ /** Omit to reuse a stored identity or dynamically register one (RFC 7591). */
22
+ clientId?: string | undefined;
17
23
  redirectUri: string;
18
24
  scopes?: readonly string[] | undefined;
19
25
  challengeHeader?: string | null | undefined;
20
26
  signal?: AbortSignal | undefined;
21
27
  }
28
+ export interface MCPAuthorizationLoginInput {
29
+ serverName: string;
30
+ resource: string;
31
+ clientId?: string | undefined;
32
+ scopes?: readonly string[] | undefined;
33
+ challengeHeader?: string | null | undefined;
34
+ signal?: AbortSignal | undefined;
35
+ /** Fixed loopback port, when the server only accepts a preregistered URI. */
36
+ port?: number | undefined;
37
+ /** How long to wait for the browser redirect. */
38
+ timeoutMs?: number | undefined;
39
+ }
40
+ export interface MCPAuthorizationLoginHandle {
41
+ /** Available immediately: show this to the user. */
42
+ started: MCPAuthorizationStartResult;
43
+ /** Settles when the browser redirect arrives and the code is exchanged. */
44
+ completion: Promise<MCPAuthorizationStatus>;
45
+ /** Abandon the flow and release the loopback port. */
46
+ cancel(): void;
47
+ }
22
48
  export interface MCPAuthorizationStartResult {
23
49
  serverName: string;
24
50
  resource: string;
@@ -26,6 +52,8 @@ export interface MCPAuthorizationStartResult {
26
52
  redirectUri: string;
27
53
  scopes: string[];
28
54
  expiresAt: number;
55
+ /** How the client identity was obtained, for surfacing in status output. */
56
+ clientIdSource: 'explicit' | 'stored' | 'registered';
29
57
  }
30
58
  export interface MCPAuthorizationCompleteInput {
31
59
  serverName: string;
@@ -50,13 +78,30 @@ export declare class MCPAuthorizationManager {
50
78
  private readonly options;
51
79
  private readonly pending;
52
80
  private readonly starting;
81
+ /** Registrations reused across servers that share one authorization server. */
82
+ private readonly registrations;
53
83
  private readonly pendingTtlMs;
54
84
  private readonly discover;
55
85
  private readonly exchange;
86
+ private readonly register;
56
87
  private readonly now;
57
88
  constructor(options: MCPAuthorizationManagerOptions);
58
89
  begin(input: MCPAuthorizationStartInput): Promise<MCPAuthorizationStartResult>;
59
90
  complete(input: MCPAuthorizationCompleteInput): Promise<MCPAuthorizationStatus>;
91
+ /**
92
+ * Bind a loopback receiver and start the flow, returning as soon as the URL
93
+ * exists so a surface can show it without blocking. Completion is awaited
94
+ * separately — a slash command returns its message immediately and reports
95
+ * the outcome through `onStateChange`. The two-step `begin`/`complete` pair
96
+ * stays for surfaces that cannot host a listener.
97
+ */
98
+ beginLogin(input: MCPAuthorizationLoginInput): Promise<MCPAuthorizationLoginHandle>;
99
+ /**
100
+ * Identity precedence: an explicit id, then the one already stored for this
101
+ * server (so re-authorizing does not orphan a registration), then the
102
+ * per-issuer cache, then a fresh dynamic registration.
103
+ */
104
+ private resolveClientIdentity;
60
105
  status(serverName: string, resource: string): Promise<MCPAuthorizationStatus>;
61
106
  disconnect(serverName: string, resource: string): Promise<boolean>;
62
107
  private pruneExpired;
@@ -0,0 +1,39 @@
1
+ import { type BrowserCompatibleDnsLookup, type MCPAuthorizationServerMetadata, requestPinnedJson } from './authorization-discovery.js';
2
+ /**
3
+ * Identity this client presents to an authorization server. `clientSecret` is
4
+ * only present when the server refused the public-client registration we ask
5
+ * for and issued a confidential client anyway; it is a credential and must
6
+ * reach the vault-backed store, never config or logs.
7
+ */
8
+ export interface MCPClientRegistration {
9
+ clientId: string;
10
+ clientSecret?: string | undefined;
11
+ issuer: string;
12
+ registeredAt: string;
13
+ }
14
+ export interface MCPClientRegistrationOptions {
15
+ authorizationServer: MCPAuthorizationServerMetadata;
16
+ redirectUris: readonly string[];
17
+ clientName?: string | undefined;
18
+ clientUri?: string | undefined;
19
+ scopes?: readonly string[] | undefined;
20
+ /** Canonical MCP resource, used only to allow loopback development servers. */
21
+ resource?: string | undefined;
22
+ signal?: AbortSignal | undefined;
23
+ timeoutMs?: number | undefined;
24
+ maxResponseBytes?: number | undefined;
25
+ lookup?: BrowserCompatibleDnsLookup | undefined;
26
+ /** Test/host override. Production callers should use the pinned default. */
27
+ requestJson?: typeof requestPinnedJson | undefined;
28
+ }
29
+ /**
30
+ * RFC 7591 dynamic client registration.
31
+ *
32
+ * Most hosted MCP servers issue no preregistered client IDs at all, so without
33
+ * this the only way to authorize was for the user to find a client ID by hand.
34
+ * We register as a public client (`token_endpoint_auth_method: "none"`) because
35
+ * a CLI cannot keep a secret; a server that overrides that and returns one is
36
+ * still honored, but the secret goes straight to the vault.
37
+ */
38
+ export declare function registerMcpOAuthClient(options: MCPClientRegistrationOptions): Promise<MCPClientRegistration>;
39
+ //# sourceMappingURL=authorization-registration.d.ts.map
@@ -1,3 +1,5 @@
1
+ import { type BrowserCompatibleDnsLookup, type MCPAuthorizationChallenge, type MCPAuthorizationServerMetadata } from './authorization-discovery.js';
2
+ export { authorizationServerMetadataUrls, canonicalMcpResource, discoverMcpAuthorization, type MCPAuthorizationChallenge, type MCPAuthorizationDiscoveryOptions, type MCPAuthorizationDiscoveryResult, type MCPAuthorizationJsonFetcher, type MCPAuthorizationServerMetadata, MCPOAuthHttpError, type MCPProtectedResourceMetadata, parseAuthorizationServerMetadata, parseMcpBearerChallenge, parseProtectedResourceMetadata, protectedResourceMetadataUrls, } from './authorization-discovery.js';
1
3
  export interface MCPAccessToken {
2
4
  accessToken: string;
3
5
  tokenType?: string | undefined;
@@ -11,42 +13,6 @@ export interface MCPAuthorizationContext {
11
13
  resource: string;
12
14
  signal?: AbortSignal | undefined;
13
15
  }
14
- export interface MCPAuthorizationChallenge {
15
- status: 401;
16
- resource: string;
17
- resourceMetadataUrl?: string | undefined;
18
- scopes: string[];
19
- rawScheme: 'Bearer';
20
- }
21
- export interface MCPProtectedResourceMetadata {
22
- resource: string;
23
- authorizationServers: string[];
24
- scopesSupported: string[];
25
- }
26
- export interface MCPAuthorizationServerMetadata {
27
- issuer: string;
28
- authorizationEndpoint: string;
29
- tokenEndpoint: string;
30
- registrationEndpoint?: string | undefined;
31
- authorizationResponseIssuerParameterSupported?: boolean | undefined;
32
- scopesSupported: string[];
33
- }
34
- export interface MCPAuthorizationDiscoveryResult {
35
- resourceMetadataUrl: string;
36
- authorizationServerMetadataUrl: string;
37
- protectedResource: MCPProtectedResourceMetadata;
38
- authorizationServer: MCPAuthorizationServerMetadata;
39
- }
40
- export type MCPAuthorizationJsonFetcher = (url: string, signal?: AbortSignal | undefined) => Promise<unknown | undefined>;
41
- export interface MCPAuthorizationDiscoveryOptions {
42
- challengeHeader?: string | null | undefined;
43
- signal?: AbortSignal | undefined;
44
- timeoutMs?: number | undefined;
45
- maxResponseBytes?: number | undefined;
46
- lookup?: BrowserCompatibleDnsLookup | undefined;
47
- /** Test/host override. Production callers should use the pinned default. */
48
- fetchJson?: MCPAuthorizationJsonFetcher | undefined;
49
- }
50
16
  export interface MCPAuthorizationSession {
51
17
  authorizationUrl: string;
52
18
  state: string;
@@ -70,6 +36,8 @@ export interface MCPAuthorizationRequestOptions {
70
36
  export interface MCPTokenExchangeOptions {
71
37
  authorizationServer: MCPAuthorizationServerMetadata;
72
38
  clientId: string;
39
+ /** Only set when dynamic registration issued a confidential client. */
40
+ clientSecret?: string | undefined;
73
41
  redirectUri: string;
74
42
  resource: string;
75
43
  code: string;
@@ -82,6 +50,8 @@ export interface MCPTokenExchangeOptions {
82
50
  export interface MCPTokenRefreshOptions {
83
51
  authorizationServer: MCPAuthorizationServerMetadata;
84
52
  clientId: string;
53
+ /** Only set when dynamic registration issued a confidential client. */
54
+ clientSecret?: string | undefined;
85
55
  resource: string;
86
56
  refreshToken: string;
87
57
  signal?: AbortSignal | undefined;
@@ -89,10 +59,6 @@ export interface MCPTokenRefreshOptions {
89
59
  maxResponseBytes?: number | undefined;
90
60
  lookup?: BrowserCompatibleDnsLookup | undefined;
91
61
  }
92
- type BrowserCompatibleDnsLookup = (hostname: string) => Promise<readonly {
93
- address: string;
94
- family: number;
95
- }[]>;
96
62
  /**
97
63
  * Host-owned bridge to vault-backed OAuth state. The MCP package never stores
98
64
  * access or refresh tokens itself and never exposes them through config.
@@ -102,36 +68,15 @@ export interface MCPAuthorizationProvider {
102
68
  /** Refresh/discover/reauthorize. Return true to retry the HTTP request once. */
103
69
  handleUnauthorized?(challenge: MCPAuthorizationChallenge, context: MCPAuthorizationContext): Promise<boolean>;
104
70
  }
105
- /** Non-2xx answer from an OAuth endpoint, carrying the HTTP status. */
106
- export declare class MCPOAuthHttpError extends Error {
107
- readonly status: number;
108
- readonly name = "MCPOAuthHttpError";
109
- constructor(message: string, status: number);
110
- }
111
- export declare function canonicalMcpResource(rawUrl: string): string;
112
71
  export declare function authorizationHeaderForToken(token: MCPAccessToken, expectedResource: string, now?: number): string;
113
- export declare function parseMcpBearerChallenge(header: string | null, resource: string): MCPAuthorizationChallenge;
114
- /** RFC 9728 fallback order for an MCP endpoint when no challenge URL exists. */
115
- export declare function protectedResourceMetadataUrls(resource: string): string[];
116
- /** RFC 8414 + OIDC discovery order required by the MCP authorization spec. */
117
- export declare function authorizationServerMetadataUrls(issuer: string): string[];
118
- export declare function parseProtectedResourceMetadata(value: unknown, expectedResource: string): MCPProtectedResourceMetadata;
119
- export declare function parseAuthorizationServerMetadata(value: unknown, expectedIssuer: string): MCPAuthorizationServerMetadata;
120
72
  /**
121
73
  * Re-validate the normalized authorization-server shape before it is accepted
122
74
  * from host-owned persistence. PKCE support is established during discovery;
123
75
  * this guard protects the persisted endpoints and bounded fields themselves.
124
76
  */
125
77
  export declare function validateMcpAuthorizationServerMetadata(value: unknown): MCPAuthorizationServerMetadata;
126
- /**
127
- * Discover and validate MCP OAuth metadata without following redirects. The
128
- * default fetcher resolves once and opens the socket to that exact IP, making
129
- * discovery resistant to DNS rebinding.
130
- */
131
- export declare function discoverMcpAuthorization(resource: string, options?: MCPAuthorizationDiscoveryOptions): Promise<MCPAuthorizationDiscoveryResult>;
132
78
  export declare function createMcpAuthorizationRequest(options: MCPAuthorizationRequestOptions): MCPAuthorizationSession;
133
79
  export declare function parseMcpAuthorizationCallback(callbackUrl: string, session: Pick<MCPAuthorizationSession, 'redirectUri' | 'state' | 'issuer' | 'requireIssuerParameter'>): string;
134
80
  export declare function exchangeMcpAuthorizationCode(options: MCPTokenExchangeOptions): Promise<MCPTokenSet>;
135
81
  export declare function refreshMcpAccessToken(options: MCPTokenRefreshOptions): Promise<MCPTokenSet>;
136
- export {};
137
82
  //# sourceMappingURL=authorization.d.ts.map
@@ -0,0 +1,15 @@
1
+ import type { MCPPageOptions, MCPRequestOptions } from './client-types.js';
2
+ import { type MCPGetPromptResult, type MCPListPromptsResult, type MCPListResourcesResult, type MCPListResourceTemplatesResult, type MCPReadResourceResult } from './protocol.js';
3
+ export declare class MCPCapabilityClient {
4
+ private readonly requestCapability;
5
+ private readonly requireResourceSubscriptions;
6
+ constructor(requestCapability: <T>(capability: 'resources' | 'prompts', method: string, params: unknown, parse: (value: unknown) => T, opts: MCPRequestOptions) => Promise<T>, requireResourceSubscriptions: (method: string) => void);
7
+ listResources(opts?: MCPPageOptions): Promise<MCPListResourcesResult>;
8
+ listResourceTemplates(opts?: MCPPageOptions): Promise<MCPListResourceTemplatesResult>;
9
+ readResource(uri: string, opts?: MCPRequestOptions): Promise<MCPReadResourceResult>;
10
+ subscribeResource(uri: string, opts?: MCPRequestOptions): Promise<void>;
11
+ unsubscribeResource(uri: string, opts?: MCPRequestOptions): Promise<void>;
12
+ listPrompts(opts?: MCPPageOptions): Promise<MCPListPromptsResult>;
13
+ getPrompt(name: string, args?: Record<string, string> | undefined, opts?: MCPRequestOptions): Promise<MCPGetPromptResult>;
14
+ }
15
+ //# sourceMappingURL=client-capabilities.d.ts.map
@@ -0,0 +1,22 @@
1
+ import type { MCPClientOptions, ToolsChangedListener } from './client-types.js';
2
+ import type { ConnectionState, MCPTool } from './contracts.js';
3
+ import type { ServerRequestResponder } from './elicitation.js';
4
+ import type { MCPServerMetadata } from './protocol.js';
5
+ import { SSETransport, StreamableHTTPTransport } from './transport.js';
6
+ export interface ClientHttpConnectionHost {
7
+ opts: MCPClientOptions;
8
+ state: ConnectionState;
9
+ sseTransport: SSETransport | undefined;
10
+ disconnectListeners: Set<() => void>;
11
+ _tools: MCPTool[];
12
+ _toolsCache: MCPTool[] | undefined;
13
+ toolsChangedListeners: Set<ToolsChangedListener>;
14
+ emitCapabilityChanged: (capability: 'resources' | 'prompts') => void;
15
+ emitResourceUpdated: (uri: string) => void;
16
+ _serverMetadata: MCPServerMetadata | undefined;
17
+ httpTransport: StreamableHTTPTransport | undefined;
18
+ serverRequests: ServerRequestResponder;
19
+ }
20
+ export declare function connectSSE(host: ClientHttpConnectionHost): Promise<void>;
21
+ export declare function connectStreamableHTTP(host: ClientHttpConnectionHost): Promise<void>;
22
+ //# sourceMappingURL=client-http-connection.d.ts.map
@@ -1,4 +1,10 @@
1
1
  import { type ChildProcess } from 'node:child_process';
2
+ /** Resolve an HTTP Bearer token without ever persisting the secret itself. */
3
+ export declare function resolveHttpBearerHeaders(options: {
4
+ headers?: Record<string, string> | undefined;
5
+ bearerTokenEnv?: string | undefined;
6
+ name: string;
7
+ }, env?: NodeJS.ProcessEnv): Record<string, string> | undefined;
2
8
  /**
3
9
  * Force-kill a child and its descendants. On Windows a stdio server is launched
4
10
  * through a `.cmd` shim with `shell: true`, so `child` is the `cmd.exe` wrapper
@@ -0,0 +1,70 @@
1
+ import type { MCPAuthorizationProvider } from './authorization.js';
2
+ import type { MCPTool } from './contracts.js';
3
+ import type { MCPClientElicitationHandler } from './elicitation.js';
4
+ export type Transport = 'stdio' | 'sse' | 'streamable-http';
5
+ export interface MCPClientOptions {
6
+ name: string;
7
+ transport: Transport;
8
+ command?: string | undefined;
9
+ args?: string[] | undefined;
10
+ env?: Record<string, string> | undefined;
11
+ url?: string | undefined;
12
+ headers?: Record<string, string> | undefined;
13
+ /** Resolve an HTTP Bearer token from this environment variable at connect time. */
14
+ bearerTokenEnv?: string | undefined;
15
+ startupTimeoutMs?: number | undefined;
16
+ requestTimeoutMs?: number | undefined;
17
+ /**
18
+ * Working directory for a stdio server process. Presets address the project
19
+ * as `--project-root .` / `server-filesystem .`, which resolve against the
20
+ * spawn cwd — without this the child inherited WrongStack's PROCESS cwd, so
21
+ * a host serving a project from elsewhere (WebUI, ACP sessions with their
22
+ * own cwd) pointed those servers at the wrong directory.
23
+ */
24
+ cwd?: string | undefined;
25
+ /** Host-owned, vault-backed authorization for HTTP transports. */
26
+ authorizationProvider?: MCPAuthorizationProvider | undefined;
27
+ /**
28
+ * Allowlist of env var names to forward from the parent process (process.env)
29
+ * to the child. Values are resolved at spawn time and merged into `env`
30
+ * via the `extra` path of `buildChildEnv` (unfiltered). This is how built-in
31
+ * MCP server presets (GitHub, Slack, Brave Search, …) get their API tokens
32
+ * without storing them in config.json or being scrubbed by the secret filter.
33
+ */
34
+ passthroughEnv?: string[] | undefined;
35
+ /**
36
+ * Resolution-bound private-network policy for HTTP transports. Default:
37
+ * private/LAN targets are blocked at dial time (DNS-rebinding safe); the
38
+ * flag opts this server in. See MCPServerConfig.allowPrivateNetworks.
39
+ */
40
+ allowPrivateNetworks?: boolean | undefined;
41
+ /**
42
+ * Answers `elicitation/create`. The capability is declared only when this is
43
+ * set, so a host that can never ask a user should leave it unset.
44
+ */
45
+ elicitation?: MCPClientElicitationHandler | undefined;
46
+ }
47
+ export interface MCPRequestOptions {
48
+ signal?: AbortSignal | undefined;
49
+ }
50
+ export interface MCPPageOptions extends MCPRequestOptions {
51
+ cursor?: string | undefined;
52
+ }
53
+ export interface JsonRpcRequest {
54
+ jsonrpc: '2.0';
55
+ id: number;
56
+ method: string;
57
+ params?: unknown | undefined;
58
+ }
59
+ export type JsonRpcServerRequest = {
60
+ jsonrpc: '2.0';
61
+ id: number | string;
62
+ method: string;
63
+ params?: unknown | undefined;
64
+ };
65
+ export type ExitListener = (name: string, code: number | null, signal: string | null) => void;
66
+ export type ToolsChangedListener = (name: string, tools: MCPTool[]) => void;
67
+ export type MCPListChangedListener = (name: string) => void;
68
+ /** `notifications/resources/updated` — the payload of a live subscription. */
69
+ export type MCPResourceUpdatedListener = (name: string, uri: string) => void;
70
+ //# sourceMappingURL=client-types.d.ts.map
package/dist/client.d.ts CHANGED
@@ -1,56 +1,13 @@
1
- import type { MCPAuthorizationProvider } from './authorization.js';
1
+ import type { ExitListener, MCPClientOptions, MCPListChangedListener, MCPPageOptions, MCPRequestOptions, MCPResourceUpdatedListener, ToolsChangedListener } from './client-types.js';
2
+ import type { ConnectionState, JsonRpcResponse, MCPTool, ToolCallResult } from './contracts.js';
3
+ import { ServerRequestResponder, type UrlElicitation } from './elicitation.js';
2
4
  import { type MCPGetPromptResult, type MCPListPromptsResult, type MCPListResourcesResult, type MCPListResourceTemplatesResult, type MCPReadResourceResult, type MCPServerMetadata } from './protocol.js';
3
5
  export { forceKillTree } from './client-process.js';
4
6
  export { quoteWindowsArg } from './client-protocol-helpers.js';
5
- export type Transport = 'stdio' | 'sse' | 'streamable-http';
6
- export interface MCPClientOptions {
7
- name: string;
8
- transport: Transport;
9
- command?: string | undefined;
10
- args?: string[] | undefined;
11
- env?: Record<string, string> | undefined;
12
- url?: string | undefined;
13
- headers?: Record<string, string> | undefined;
14
- startupTimeoutMs?: number | undefined;
15
- requestTimeoutMs?: number | undefined;
16
- /**
17
- * Working directory for a stdio server process. Presets address the project
18
- * as `--project-root .` / `server-filesystem .`, which resolve against the
19
- * spawn cwd — without this the child inherited WrongStack's PROCESS cwd, so
20
- * a host serving a project from elsewhere (WebUI, ACP sessions with their
21
- * own cwd) pointed those servers at the wrong directory.
22
- */
23
- cwd?: string | undefined;
24
- /** Host-owned, vault-backed authorization for HTTP transports. */
25
- authorizationProvider?: MCPAuthorizationProvider | undefined;
26
- /**
27
- * Allowlist of env var names to forward from the parent process (process.env)
28
- * to the child. Values are resolved at spawn time and merged into `env`
29
- * via the `extra` path of `buildChildEnv` (unfiltered). This is how built-in
30
- * MCP server presets (GitHub, Slack, Brave Search, …) get their API tokens
31
- * without storing them in config.json or being scrubbed by the secret filter.
32
- */
33
- passthroughEnv?: string[] | undefined;
34
- /**
35
- * Resolution-bound private-network policy for HTTP transports. Default:
36
- * private/LAN targets are blocked at dial time (DNS-rebinding safe); the
37
- * flag opts this server in. See MCPServerConfig.allowPrivateNetworks.
38
- */
39
- allowPrivateNetworks?: boolean | undefined;
40
- }
41
- import type { ConnectionState, JsonRpcResponse, MCPTool, ToolCallResult } from './contracts.js';
42
7
  export type { ConnectionState, JsonRpcResponse, MCPTool, ToolCallResult };
43
- export interface MCPRequestOptions {
44
- signal?: AbortSignal | undefined;
45
- }
46
- export interface MCPPageOptions extends MCPRequestOptions {
47
- cursor?: string | undefined;
48
- }
49
- type ExitListener = (name: string, code: number | null, signal: string | null) => void;
50
- type ToolsChangedListener = (name: string, tools: MCPTool[]) => void;
51
- export type MCPListChangedListener = (name: string) => void;
52
8
  export declare class MCPClient {
53
9
  readonly opts: MCPClientOptions;
10
+ private readonly capabilityClient;
54
11
  /**
55
12
  * Maximum bytes the rx buffer may accumulate before the connection is
56
13
  * forcefully closed. A well-behaved JSON-RPC server emits newline-delimited
@@ -96,8 +53,11 @@ export declare class MCPClient {
96
53
  private readonly toolsChangedListeners;
97
54
  private readonly resourcesChangedListeners;
98
55
  private readonly promptsChangedListeners;
56
+ private readonly resourceUpdatedListeners;
99
57
  /** Notified when an HTTP transport (SSE or streamable-http) disconnects. */
100
58
  private readonly disconnectListeners;
59
+ /** Answers server→client requests (ping, elicitation) on every transport. */
60
+ private readonly serverRequests;
101
61
  constructor(opts: MCPClientOptions);
102
62
  getState(): ConnectionState;
103
63
  getServerMetadata(): MCPServerMetadata | undefined;
@@ -124,6 +84,11 @@ export declare class MCPClient {
124
84
  callTool(name: string, input: unknown, opts?: {
125
85
  signal?: AbortSignal | undefined;
126
86
  }): Promise<ToolCallResult>;
87
+ /**
88
+ * Put the pages of a `-32042` tool-call error to the user (consent first,
89
+ * nothing opened without it), on the run whose call got the error.
90
+ */
91
+ presentUrlElicitations(elicitations: readonly UrlElicitation[], signal: AbortSignal): ReturnType<ServerRequestResponder['presentUrl']>;
127
92
  listResources(opts?: MCPPageOptions): Promise<MCPListResourcesResult>;
128
93
  listResourceTemplates(opts?: MCPPageOptions): Promise<MCPListResourceTemplatesResult>;
129
94
  readResource(uri: string, opts?: MCPRequestOptions): Promise<MCPReadResourceResult>;
@@ -157,8 +122,13 @@ export declare class MCPClient {
157
122
  removeToolsChangedListener(listener: ToolsChangedListener): void;
158
123
  addResourcesChangedListener(listener: MCPListChangedListener): void;
159
124
  removeResourcesChangedListener(listener: MCPListChangedListener): void;
125
+ addResourceUpdatedListener(listener: MCPResourceUpdatedListener): void;
126
+ removeResourceUpdatedListener(listener: MCPResourceUpdatedListener): void;
160
127
  addPromptsChangedListener(listener: MCPListChangedListener): void;
161
128
  removePromptsChangedListener(listener: MCPListChangedListener): void;
129
+ private emitResourceUpdated;
162
130
  private emitCapabilityChanged;
131
+ private clientHttpConnectionHost;
163
132
  }
133
+ export type { MCPClientOptions, MCPListChangedListener, MCPPageOptions, MCPRequestOptions, MCPResourceUpdatedListener, Transport, } from './client-types.js';
164
134
  //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1,18 @@
1
+ import type { MCPServerConfig } from '@wrongstack/core/types';
2
+ interface ExpandableFields {
3
+ command?: string | undefined;
4
+ args?: string[] | undefined;
5
+ env?: Record<string, string> | undefined;
6
+ url?: string | undefined;
7
+ headers?: Record<string, string> | undefined;
8
+ }
9
+ /** Every environment variable a server entry reads through placeholders, sorted. */
10
+ export declare function mcpEnvPlaceholders(cfg: Partial<ExpandableFields>): string[];
11
+ /**
12
+ * The expandable fields with every placeholder resolved from `env`. A variable
13
+ * that is unset (or empty) with no default is an error naming it: sending the
14
+ * literal `${TOKEN}` would only fail later, as an opaque 401.
15
+ */
16
+ export declare function expandMcpEnvPlaceholders(cfg: Readonly<MCPServerConfig>, env?: NodeJS.ProcessEnv): ExpandableFields;
17
+ export {};
18
+ //# sourceMappingURL=config-env.d.ts.map
@@ -11,11 +11,32 @@
11
11
  *
12
12
  * Deliberately conservative: a revision belongs here only once its wire
13
13
  * requirements are met, because the version we send in `initialize` is a
14
- * promise about what the peer may then use. WrongStack speaks the 2024-11-05
15
- * shape — tools, resources, prompts, sampling — and does not implement the
16
- * additions of later revisions (elicitation, structured tool output, resource
17
- * links), so listing one of those would advertise capabilities that are not
18
- * there.
14
+ * promise about what the peer may then use.
15
+ *
16
+ * WrongStack speaks the 2024-11-05 shape: tools, resources, prompts,
17
+ * pagination, list_changed, resource subscriptions and cancellation. It does
18
+ * NOT implement sampling (deliberately denied — see below), nor most additions
19
+ * of later revisions: resource links, `completion/complete`, or progress
20
+ * notifications. Listing a newer revision here would advertise capabilities
21
+ * that are not there.
22
+ *
23
+ * Known drift, accepted: the HTTP layer already implements Streamable HTTP
24
+ * (added 2025-03-26), the `MCP-Protocol-Version` header and OAuth resource
25
+ * indicators (2025-06-18), and the client answers form- and URL-mode
26
+ * elicitation (2025-06-18 / 2025-11-25) and reads structured tool output
27
+ * (2025-06-18), even though we negotiate 2024-11-05. Those ride on the
28
+ * transport, the 401 challenge and the declared client capability rather than
29
+ * on the negotiated revision, so servers accept them; the declaration is
30
+ * narrower than the behavior.
31
+ *
32
+ * Not gaps, by the spec's own reckoning: Roots, Sampling and Logging were all
33
+ * Deprecated in revision 2026-07-28 (SEP-2577), with new implementations told
34
+ * not to adopt them. Not having them is alignment, not lag.
35
+ *
36
+ * The current revision (2026-07-28) also replaced the `initialize` handshake
37
+ * with per-request version negotiation plus a mandatory `server/discover` RPC.
38
+ * Reaching it is an architectural change, not another entry in this list; the
39
+ * spec keeps a backward-compatibility path for handshake-based clients.
19
40
  *
20
41
  * Adding a revision: implement its additions, then put it at the FRONT.
21
42
  * `PROTOCOL_VERSION` follows automatically, and the negotiation below starts