@wrongstack/mcp 1.0.24 → 1.0.27

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/dist/manage.d.ts CHANGED
@@ -17,6 +17,10 @@ export interface McpServerInput {
17
17
  bearerTokenEnv?: string | undefined;
18
18
  allowedTools?: string[] | undefined;
19
19
  permission?: Permission | undefined;
20
+ /** Connect/startup timeout in ms. Defaults to 10s when omitted. */
21
+ startupTimeoutMs?: number | undefined;
22
+ /** Per-request JSON-RPC timeout in ms. Defaults to 60s when omitted. */
23
+ requestTimeoutMs?: number | undefined;
20
24
  /** Lazy connect — spawn the process only on first tool call (see config). */
21
25
  lazy?: boolean | undefined;
22
26
  /** Env var names to forward from parent process at spawn time. */
@@ -88,4 +88,14 @@ export declare function parseListResourceTemplatesResult(value: unknown): MCPLis
88
88
  export declare function parseReadResourceResult(value: unknown): MCPReadResourceResult;
89
89
  export declare function parseListPromptsResult(value: unknown): MCPListPromptsResult;
90
90
  export declare function parseGetPromptResult(value: unknown): MCPGetPromptResult;
91
+ /**
92
+ * Extract the `uri` from a `notifications/resources/updated` payload.
93
+ *
94
+ * The notification is the whole point of `resources/subscribe`, which the
95
+ * client already supported — every transport parsed the three `list_changed`
96
+ * notifications and silently dropped this one, so a subscription produced
97
+ * nothing. Returns `undefined` for a malformed payload rather than throwing:
98
+ * a notification has no reply, so a bad one is ignored, not answered.
99
+ */
100
+ export declare function resourceUpdatedUri(params: unknown): string | undefined;
91
101
  //# sourceMappingURL=protocol.d.ts.map
@@ -1,10 +1,11 @@
1
1
  import type { MCPServerConfig } from '@wrongstack/core/types';
2
- import type { MCPAuthorizationManager, MCPAuthorizationStartResult, MCPAuthorizationStatus } from './authorization-manager.js';
2
+ import type { MCPAuthorizationLoginHandle, MCPAuthorizationLoginInput, MCPAuthorizationManager, MCPAuthorizationStartResult, MCPAuthorizationStatus } from './authorization-manager.js';
3
3
  import type { ServerSlot } from './registry-slots.js';
4
4
  export declare function requireAuthorizationManager(manager: MCPAuthorizationManager | undefined): MCPAuthorizationManager;
5
5
  export declare function requireHttpServerConfig(servers: Map<string, ServerSlot>, disabledServers: Map<string, MCPServerConfig>, name: string): MCPServerConfig;
6
+ export declare function loginRegistryAuthorization(manager: MCPAuthorizationManager | undefined, cfg: MCPServerConfig, name: string, input: Omit<MCPAuthorizationLoginInput, 'serverName' | 'resource'>): Promise<MCPAuthorizationLoginHandle>;
6
7
  export declare function beginRegistryAuthorization(manager: MCPAuthorizationManager | undefined, cfg: MCPServerConfig, name: string, input: {
7
- clientId: string;
8
+ clientId?: string | undefined;
8
9
  redirectUri: string;
9
10
  scopes?: readonly string[] | undefined;
10
11
  challengeHeader?: string | null | undefined;
@@ -15,6 +15,7 @@ export interface RegistryConnectContext {
15
15
  cacheDir?: string | undefined;
16
16
  cwd?: string | undefined;
17
17
  authorizationProviderFactory?: MCPRegistryOptions['authorizationProviderFactory'] | undefined;
18
+ elicitationHandler?: MCPRegistryOptions['elicitationHandler'] | undefined;
18
19
  operationListeners: Set<MCPOperationListener>;
19
20
  ensureConnected: (name: string) => Promise<MCPClient>;
20
21
  recordOperation: (slot: ServerSlot, kind: MCPOperationKind, reason?: string, failureKind?: MCPFailureKind, durationMs?: number, retain?: boolean) => void;
@@ -1,6 +1,7 @@
1
1
  import type { MCPServerConfig, Tool } from '@wrongstack/core/types';
2
2
  import type { MCPClient } from './client.js';
3
3
  import type { ConnectionState, MCPTool } from './contracts.js';
4
+ import type { ElicitationRequester } from './elicitation.js';
4
5
  import type { MCPServerOperationState } from './operations.js';
5
6
  import type { MCPPrompt, MCPResource, MCPResourceTemplate, MCPServerMetadata } from './protocol.js';
6
7
  export interface ServerSlot {
@@ -40,6 +41,11 @@ export interface ServerSlot {
40
41
  connecting?: Promise<MCPClient | undefined> | undefined;
41
42
  /** Whether this lazy server's resolver wrappers are registered (register once). */
42
43
  registeredLazy: boolean;
44
+ /**
45
+ * Runs with a tool call to this server in flight, oldest first. A server
46
+ * elicitation asks the newest — the spec does not tie a request to a call.
47
+ */
48
+ activeCallers?: ElicitationRequester[] | undefined;
43
49
  /** Bounded, payload-free operational telemetry for this server. */
44
50
  operations: MCPServerOperationState;
45
51
  }
@@ -4,6 +4,7 @@ import type { Logger, MCPServerConfig } from '@wrongstack/core/types';
4
4
  import type { MCPAuthorizationProvider } from './authorization.js';
5
5
  import type { MCPAuthorizationManager } from './authorization-manager.js';
6
6
  import type { ConnectionState } from './contracts.js';
7
+ import type { MCPElicitationHandler } from './elicitation.js';
7
8
  import type { MCPPrompt, MCPResource, MCPResourceTemplate, MCPServerMetadata } from './protocol.js';
8
9
  export interface MCPRegistryOptions {
9
10
  toolRegistry: ToolRegistry;
@@ -20,6 +21,11 @@ export interface MCPRegistryOptions {
20
21
  lazyMode?: boolean | undefined;
21
22
  authorizationProviderFactory?: ((server: Readonly<MCPServerConfig>) => MCPAuthorizationProvider | undefined) | undefined;
22
23
  authorizationManager?: MCPAuthorizationManager | undefined;
24
+ /**
25
+ * Puts a server's `elicitation/create` form in front of the user. Without
26
+ * it the capability is not declared, so servers never send the request.
27
+ */
28
+ elicitationHandler?: MCPElicitationHandler | undefined;
23
29
  }
24
30
  export interface MCPRegistryCatalog {
25
31
  name: string;
@@ -20,13 +20,17 @@ export declare class MCPRegistry {
20
20
  private readonly idleTimeoutMs;
21
21
  private readonly authorizationProviderFactory?;
22
22
  private readonly authorizationManager?;
23
+ private readonly elicitationHandler?;
23
24
  private readonly operationListeners;
25
+ /** Starts still in flight, for {@link whenStarted}. */
26
+ private readonly startups;
24
27
  /** Single shared idle sweep timer (started lazily; unref'd; cleared on stopAll). */
25
28
  private idleTimer?;
26
29
  constructor(opts: MCPRegistryOptions);
27
30
  private requireSlot;
31
+ loginAuthorization(name: string, input: Omit<import('./authorization-manager.js').MCPAuthorizationLoginInput, 'serverName' | 'resource'>): Promise<import('./authorization-manager.js').MCPAuthorizationLoginHandle>;
28
32
  beginAuthorization(name: string, input: {
29
- clientId: string;
33
+ clientId?: string | undefined;
30
34
  redirectUri: string;
31
35
  scopes?: readonly string[] | undefined;
32
36
  challengeHeader?: string | null | undefined;
@@ -37,6 +41,12 @@ export declare class MCPRegistry {
37
41
  disconnectAuthorization(name: string): Promise<boolean>;
38
42
  private requireHttpServerConfig;
39
43
  start(cfg: MCPServerConfig): Promise<void>;
44
+ /**
45
+ * Resolves once every start already in flight has connected or failed. A
46
+ * host that starts its servers in the background and then runs a single
47
+ * turn waits here, so the turn's tool list includes theirs.
48
+ */
49
+ whenStarted(): Promise<void>;
40
50
  /** Record an intentionally disabled configuration without opening a transport. */
41
51
  markDisabled(cfg: MCPServerConfig): void;
42
52
  /** Remove residual operational/configuration state after a management delete. */
@@ -175,6 +185,7 @@ export declare class MCPRegistry {
175
185
  */
176
186
  private readonly onToolsChanged;
177
187
  private readonly onResourcesChanged;
188
+ private readonly onResourceUpdated;
178
189
  private readonly onPromptsChanged;
179
190
  private addCatalogListeners;
180
191
  private removeCatalogListeners;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The model-facing text of a successful call: the rendered content, plus the
3
+ * structured result when the content does not already contain it, plus a
4
+ * notice when the result breaks the tool's own `outputSchema`.
5
+ */
6
+ export declare function renderStructuredResult(rendered: string, content: unknown, structured: Record<string, unknown> | undefined, outputSchema: Record<string, unknown> | undefined): string;
7
+ //# sourceMappingURL=structured-result.d.ts.map
@@ -4,16 +4,20 @@ export interface MCPStoredAuthorization {
4
4
  serverName: string;
5
5
  resource: string;
6
6
  clientId: string;
7
+ /** Present only for a confidential client issued by dynamic registration. */
8
+ clientSecret?: string | undefined;
7
9
  authorizationServer: MCPAuthorizationServerMetadata;
8
10
  tokenSet: MCPTokenSet;
9
11
  updatedAt: string;
10
12
  }
11
13
  export interface MCPAuthorizationStateEvent {
12
14
  serverName: string;
13
- state: 'authorized' | 'refreshed' | 'reauth_required' | 'removed';
15
+ state: 'authorized' | 'refreshed' | 'reauth_required' | 'removed' | 'failed';
14
16
  resource: string;
15
17
  expiresAt?: number | undefined;
16
18
  scopes?: string[] | undefined;
19
+ /** Non-secret reason, set for `failed`. Never contains a token or code. */
20
+ message?: string | undefined;
17
21
  }
18
22
  export interface MCPRefreshingAuthorizationProviderOptions {
19
23
  serverName: string;
@@ -1,4 +1,17 @@
1
- import type { MCPTool } from './contracts.js';
1
+ import type { MCPTool, ToolCallResult } from './contracts.js';
2
+ /**
3
+ * The ONE reading of a `tools/call` response, shared by every transport. A
4
+ * JSON-RPC error becomes an error result; `structuredContent` is kept only
5
+ * when it is the object the spec requires.
6
+ */
7
+ export declare function toToolCallResult(response: {
8
+ result?: unknown | undefined;
9
+ error?: {
10
+ message: string;
11
+ code?: number | undefined;
12
+ data?: unknown;
13
+ } | undefined;
14
+ }): ToolCallResult;
2
15
  /**
3
16
  * Collect every page of `tools/list`.
4
17
  *
@@ -1,6 +1,7 @@
1
1
  import * as https from 'node:https';
2
2
  import { type MCPAuthorizationProvider } from './authorization.js';
3
3
  import type { ConnectionState, MCPTool } from './contracts.js';
4
+ import { type ServerRequest, ServerRequestResponder } from './elicitation.js';
4
5
  import type { MCPServerMetadata } from './protocol.js';
5
6
  import { type TransportDnsLookup } from './transport-security.js';
6
7
  export interface HttpTransportOptions {
@@ -43,18 +44,21 @@ export interface HttpTransportOptions {
43
44
  * record is policy-checked before the dial.
44
45
  */
45
46
  lookup?: TransportDnsLookup | undefined;
47
+ /** The owning client's answerer for server→client requests (shared across reconnects). */
48
+ serverRequests?: ServerRequestResponder | undefined;
46
49
  }
47
50
  /**
48
51
  * Abort error whose `name` is `'AbortError'` so the core executor's
49
52
  * classifyToolError maps it to FATAL / not-retryable (user cancellation).
50
53
  */
54
+ /**
55
+ * `scheme://host:port` of a server URL, for log lines. The path and query can
56
+ * hold an expanded `${SECRET}` (config-env), and userinfo is a credential.
57
+ */
58
+ export declare function urlOriginForLog(raw: string): string;
51
59
  export declare function makeAbortError(method: string): Error;
52
60
  /** Advance a JSON-RPC number id without leaving JavaScript's safe-integer range. */
53
61
  export declare function nextJsonRpcId(id: number): number;
54
- export declare function createTimeoutSignal(parent: AbortSignal | undefined, timeoutMs: number): {
55
- signal: AbortSignal;
56
- dispose: () => void;
57
- };
58
62
  /**
59
63
  * Fields and methods shared by all HTTP-based MCP transports.
60
64
  * Subclasses override `connect()`, `close()`, `callTool()`, `request()`.
@@ -80,8 +84,10 @@ export declare abstract class BaseHTTPTransport {
80
84
  protected readonly disconnectHandlers: Array<() => void>;
81
85
  protected readonly toolsChangedListeners: Set<(tools: MCPTool[]) => void>;
82
86
  protected readonly resourcesChangedListeners: Set<() => void>;
87
+ protected readonly resourceUpdatedListeners: Set<(uri: string) => void>;
83
88
  protected readonly promptsChangedListeners: Set<() => void>;
84
89
  protected protocolVersion?: string | undefined;
90
+ protected readonly serverRequests: ServerRequestResponder;
85
91
  constructor(opts: HttpTransportOptions, transportName: string);
86
92
  getState(): ConnectionState;
87
93
  protected fetchWithAuthorization(input: string | URL, init: RequestInit, signal?: AbortSignal | undefined): Promise<Response>;
@@ -91,6 +97,7 @@ export declare abstract class BaseHTTPTransport {
91
97
  onToolsChanged(cb: (tools: MCPTool[]) => void): () => void;
92
98
  onResourcesChanged(cb: () => void): () => void;
93
99
  onPromptsChanged(cb: () => void): () => void;
100
+ onResourceUpdated(cb: (uri: string) => void): () => void;
94
101
  /**
95
102
  * Fire all disconnect handlers. Subclasses call this when the connection
96
103
  * drops so the registry can schedule reconnects.
@@ -98,6 +105,7 @@ export declare abstract class BaseHTTPTransport {
98
105
  protected notifyDisconnect(): void;
99
106
  protected notifyResourcesChanged(): void;
100
107
  protected notifyPromptsChanged(): void;
108
+ protected notifyResourceUpdated(uri: string): void;
101
109
  private dispatcherFetch;
102
110
  private pinnedDispatcher;
103
111
  private applyPinnedDispatcher;
@@ -117,6 +125,20 @@ export declare abstract class BaseHTTPTransport {
117
125
  * these same TLS options plus the resolution-bound lookup.
118
126
  */
119
127
  protected applyTlsAgent(fetchOpts: RequestInit): void;
128
+ /**
129
+ * Request timeout for a call. It holds while the server waits on the user's
130
+ * elicitation answer — that wait is the user typing, not the server stalling.
131
+ */
132
+ protected requestTimeoutSignal(parent: AbortSignal | undefined, timeoutMs: number): {
133
+ signal: AbortSignal;
134
+ dispose: () => void;
135
+ };
136
+ /**
137
+ * Answer a server→client request. Over HTTP the reply is POSTed back like
138
+ * any client message; the server acknowledges it with 202. A failed reply is
139
+ * not retried — the server times its request out on its side.
140
+ */
141
+ protected replyToServer(request: ServerRequest, url: string, headers: Record<string, string>): Promise<void>;
120
142
  /** Generate the next JSON-RPC request id. Subclasses provide the counter. */
121
143
  protected abstract genId(): number;
122
144
  }
@@ -11,7 +11,20 @@ export declare class StreamableHTTPTransport extends BaseHTTPTransport {
11
11
  private sessionId?;
12
12
  constructor(opts: HttpTransportOptions);
13
13
  protected genId(): number;
14
- private consumeResponseText;
14
+ /**
15
+ * Read a POST's reply. A `text/event-stream` reply is consumed event by
16
+ * event: the server may put notifications and its OWN requests (elicitation)
17
+ * ahead of our response and then wait for our answer to them, so buffering
18
+ * the whole body first would deadlock the call.
19
+ */
20
+ private readResponse;
21
+ /**
22
+ * Dispatch the notifications and server requests in `text`, and return the
23
+ * response to `requestId` — falling back to the first other response, which
24
+ * the caller's id check then rejects.
25
+ */
26
+ private takeEnvelopes;
27
+ private sessionHeaders;
15
28
  private handleNotification;
16
29
  private refreshTools;
17
30
  connect(): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import type { Permission, Tool } from '@wrongstack/core/types';
2
2
  import type { MCPClient } from './client.js';
3
3
  import type { MCPTool } from './contracts.js';
4
+ import type { ElicitationRequester } from './elicitation.js';
4
5
  /**
5
6
  * Resolves the live client for a tool call. A plain {@link MCPClient} for eager
6
7
  * servers, or a thunk that connects-on-demand for lazy/dormant servers (the
@@ -8,11 +9,12 @@ import type { MCPTool } from './contracts.js';
8
9
  */
9
10
  export type MCPClientResolver = MCPClient | (() => Promise<MCPClient>);
10
11
  export interface MCPToolCallObserver {
11
- onStart(): void;
12
+ /** `caller` is the run making the call — who a server elicitation mid-call should ask. */
13
+ onStart(caller?: ElicitationRequester | undefined): void;
12
14
  onFinish(result: {
13
15
  durationMs: number;
14
16
  ok: boolean;
15
- }): void;
17
+ }, caller?: ElicitationRequester | undefined): void;
16
18
  }
17
19
  export declare function wrapMCPTool(serverName: string, mcpTool: MCPTool, client: MCPClientResolver, permission?: Permission, observer?: MCPToolCallObserver | undefined): Tool;
18
20
  //# sourceMappingURL=wrap-tool.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/mcp",
3
- "version": "1.0.24",
3
+ "version": "1.0.27",
4
4
  "license": "MIT",
5
5
  "description": "WrongStack Model Context Protocol client and registry: stdio, SSE, and streamable HTTP transports.",
6
6
  "repository": {
@@ -26,7 +26,7 @@
26
26
  "!dist/**/*.map"
27
27
  ],
28
28
  "dependencies": {
29
- "@wrongstack/core": "1.0.24",
29
+ "@wrongstack/core": "1.0.27",
30
30
  "undici": "^8.10.2"
31
31
  },
32
32
  "devDependencies": {