@wrongstack/mcp 1.0.2 → 1.0.4

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.
@@ -6,9 +6,36 @@
6
6
  * - Reconnect parameters can be overridden via config in the future
7
7
  * - No scattered magic values across multiple files
8
8
  */
9
+ /**
10
+ * Protocol revisions this package actually implements, newest first.
11
+ *
12
+ * Deliberately conservative: a revision belongs here only once its wire
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.
19
+ *
20
+ * Adding a revision: implement its additions, then put it at the FRONT.
21
+ * `PROTOCOL_VERSION` follows automatically, and the negotiation below starts
22
+ * accepting a peer that asks for it.
23
+ */
24
+ export declare const SUPPORTED_PROTOCOL_VERSIONS: readonly string[];
25
+ /**
26
+ * Pick the version to answer an `initialize` with.
27
+ *
28
+ * The spec's rule: echo what the peer asked for when it is supported,
29
+ * otherwise reply with our own latest and let the peer decide whether it can
30
+ * continue. Before this existed the server ignored the request entirely and
31
+ * always answered `2024-11-05`, so a peer that asked for a newer revision was
32
+ * told it had been granted nothing of the sort — silently, with no way to see
33
+ * the mismatch.
34
+ */
35
+ export declare function negotiateProtocolVersion(requested: unknown): string;
9
36
  export declare const MCP_CONSTANTS: Readonly<{
10
- /** MCP protocol version advertised during handshake. */
11
- readonly PROTOCOL_VERSION: '2024-11-05';
37
+ /** MCP protocol version advertised during handshake: the newest we implement. */
38
+ readonly PROTOCOL_VERSION: string;
12
39
  /** Identity announced to MCP servers during `initialize`. */
13
40
  readonly CLIENT_INFO: Readonly<{
14
41
  name: "wrongstack";
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  export { authorizationHeaderForToken, authorizationServerMetadataUrls, canonicalMcpResource, createMcpAuthorizationRequest, discoverMcpAuthorization, exchangeMcpAuthorizationCode, type MCPAccessToken, type MCPAuthorizationChallenge, type MCPAuthorizationContext, type MCPAuthorizationDiscoveryOptions, type MCPAuthorizationDiscoveryResult, type MCPAuthorizationJsonFetcher, type MCPAuthorizationProvider, type MCPAuthorizationRequestOptions, type MCPAuthorizationServerMetadata, type MCPAuthorizationSession, type MCPProtectedResourceMetadata, type MCPTokenExchangeOptions, type MCPTokenRefreshOptions, type MCPTokenSet, parseAuthorizationServerMetadata, parseMcpAuthorizationCallback, parseMcpBearerChallenge, parseProtectedResourceMetadata, protectedResourceMetadataUrls, refreshMcpAccessToken, validateMcpAuthorizationServerMetadata, } from './authorization.js';
2
2
  export { type MCPAuthorizationCompleteInput, MCPAuthorizationManager, type MCPAuthorizationManagerOptions, type MCPAuthorizationStartInput, type MCPAuthorizationStartResult, type MCPAuthorizationStatus, } from './authorization-manager.js';
3
3
  export { MCPClient, type MCPClientOptions, type MCPListChangedListener, type MCPPageOptions, type MCPRequestOptions, type Transport, } from './client.js';
4
- export { MCP_CONSTANTS } from './constants.js';
4
+ export { MCP_CONSTANTS, negotiateProtocolVersion, SUPPORTED_PROTOCOL_VERSIONS, } from './constants.js';
5
5
  export { DEFAULT_MCP_INSERTION_MAX_BYTES, DEFAULT_MCP_RESOURCE_SCHEMES, type MCPContentProvenance, type MCPInsertionPolicy, type MCPPromptInsertion, type MCPResourceInsertion, preparePromptInsertion, prepareResourceInsertion, } from './content-selection.js';
6
6
  export type { ConnectionState, JsonRpcResponse, MCPTool, ToolCallResult, } from './contracts.js';
7
7
  export { addMcp, disableMcp, discoverMcp, enableMcp, listMcp, MCP_ENV_MASK, type McpManageDeps, type McpOpResult, type McpServerInfo, type McpServerInput, removeMcp, restartMcp, updateMcp, } from './manage.js';
package/dist/index.js CHANGED
@@ -875,9 +875,16 @@ function parseEmptyResult(value) {
875
875
  }
876
876
 
877
877
  // src/constants.ts
878
+ var SUPPORTED_PROTOCOL_VERSIONS = Object.freeze(["2024-11-05"]);
879
+ function negotiateProtocolVersion(requested) {
880
+ if (typeof requested === "string" && SUPPORTED_PROTOCOL_VERSIONS.includes(requested)) {
881
+ return requested;
882
+ }
883
+ return MCP_CONSTANTS.PROTOCOL_VERSION;
884
+ }
878
885
  var MCP_CONSTANTS = Object.freeze({
879
- /** MCP protocol version advertised during handshake. */
880
- PROTOCOL_VERSION: "2024-11-05",
886
+ /** MCP protocol version advertised during handshake: the newest we implement. */
887
+ PROTOCOL_VERSION: SUPPORTED_PROTOCOL_VERSIONS[0],
881
888
  /** Identity announced to MCP servers during `initialize`. */
882
889
  CLIENT_INFO: Object.freeze({
883
890
  name: "wrongstack",
@@ -1262,7 +1269,7 @@ import { Agent as UndiciAgent, fetch as undiciFetch } from "undici";
1262
1269
  import * as dns2 from "node:dns/promises";
1263
1270
  import * as net2 from "node:net";
1264
1271
  import { ConfigError } from "@wrongstack/core/types";
1265
- import { isPrivateIPv4 as isPrivateIPv42, isPrivateIPv6 as isPrivateIPv62 } from "@wrongstack/core/utils";
1272
+ import { embeddedIPv4, expandIPv6, isPrivateIPv4 as isPrivateIPv42, isPrivateIPv6 as isPrivateIPv62 } from "@wrongstack/core/utils";
1266
1273
  function isTlsUnsafeAllowed() {
1267
1274
  return process.env["WRONGSTACK_UNSAFE_MCP_TLS"] === "1";
1268
1275
  }
@@ -1351,6 +1358,11 @@ function classifyTransportAddress(address, family) {
1351
1358
  }
1352
1359
  return "blocked";
1353
1360
  }
1361
+ const groups = expandIPv6(v6);
1362
+ if (groups !== null) {
1363
+ const embedded = embeddedIPv4(groups);
1364
+ if (embedded !== void 0) return classifyTransportAddress(embedded, 4);
1365
+ }
1354
1366
  return isPrivateIPv62(v6) ? "private" : "public";
1355
1367
  }
1356
1368
  return "blocked";
@@ -1425,6 +1437,9 @@ function makeAbortError(method) {
1425
1437
  err.name = "AbortError";
1426
1438
  return err;
1427
1439
  }
1440
+ function nextJsonRpcId(id) {
1441
+ return id >= Number.MAX_SAFE_INTEGER ? 1 : id + 1;
1442
+ }
1428
1443
  function createTimeoutSignal(parent, timeoutMs) {
1429
1444
  const ctrl = new AbortController();
1430
1445
  const onAbort = () => ctrl.abort(parent?.reason);
@@ -1807,7 +1822,9 @@ var SSETransport = class extends BaseHTTPTransport {
1807
1822
  super(opts, "SSETransport");
1808
1823
  }
1809
1824
  genId() {
1810
- return this._nextId++;
1825
+ const id = this._nextId;
1826
+ this._nextId = nextJsonRpcId(id);
1827
+ return id;
1811
1828
  }
1812
1829
  /** Refresh tool list when server sends notifications/tools/list_changed. */
1813
1830
  async handleToolsListChanged() {
@@ -2121,7 +2138,9 @@ var StreamableHTTPTransport = class _StreamableHTTPTransport extends BaseHTTPTra
2121
2138
  super(opts, "StreamableHTTP");
2122
2139
  }
2123
2140
  genId() {
2124
- return this._nextId++;
2141
+ const id = this._nextId;
2142
+ this._nextId = nextJsonRpcId(id);
2143
+ return id;
2125
2144
  }
2126
2145
  consumeResponseText(text, requestId) {
2127
2146
  const envelopes = extractJsonRpcEnvelopes(text);
@@ -2166,6 +2185,7 @@ var StreamableHTTPTransport = class _StreamableHTTPTransport extends BaseHTTPTra
2166
2185
  const signal = this.abortController.signal;
2167
2186
  const startupTimer = setTimeout(() => this.abortController?.abort(), this.timeout);
2168
2187
  try {
2188
+ const initializeId = this.genId();
2169
2189
  const initFetchOpts = {
2170
2190
  method: "POST",
2171
2191
  headers: {
@@ -2175,7 +2195,7 @@ var StreamableHTTPTransport = class _StreamableHTTPTransport extends BaseHTTPTra
2175
2195
  },
2176
2196
  body: JSON.stringify({
2177
2197
  jsonrpc: "2.0",
2178
- id: this.genId(),
2198
+ id: initializeId,
2179
2199
  method: "initialize",
2180
2200
  params: {
2181
2201
  protocolVersion: MCP_CONSTANTS.PROTOCOL_VERSION,
@@ -2201,7 +2221,7 @@ var StreamableHTTPTransport = class _StreamableHTTPTransport extends BaseHTTPTra
2201
2221
  if (!data) {
2202
2222
  throw new Error("Could not parse initialize response");
2203
2223
  }
2204
- data = assertMatchingJsonRpcResult(data, this._nextId - 1, "initialize");
2224
+ data = assertMatchingJsonRpcResult(data, initializeId, "initialize");
2205
2225
  if (data.error) {
2206
2226
  throw new Error(`initialize failed: ${data.error.message}`);
2207
2227
  }
@@ -2451,6 +2471,7 @@ var MCPClient = class _MCPClient {
2451
2471
  this.disconnectListeners.delete(listener);
2452
2472
  }
2453
2473
  async connect() {
2474
+ if (this.state === "connected" || this.state === "connecting") return;
2454
2475
  this.state = "connecting";
2455
2476
  this._serverMetadata = void 0;
2456
2477
  try {
@@ -2794,7 +2815,7 @@ var MCPClient = class _MCPClient {
2794
2815
  gracefulTimer.unref?.();
2795
2816
  })
2796
2817
  ]);
2797
- if (gracefulTimer) clearTimeout(gracefulTimer);
2818
+ clearTimeout(gracefulTimer);
2798
2819
  if (gracefulRace === "timeout") {
2799
2820
  forceKillTree(child);
2800
2821
  let forceTimer;
@@ -2805,7 +2826,7 @@ var MCPClient = class _MCPClient {
2805
2826
  forceTimer.unref?.();
2806
2827
  })
2807
2828
  ]);
2808
- if (forceTimer) clearTimeout(forceTimer);
2829
+ clearTimeout(forceTimer);
2809
2830
  }
2810
2831
  child.stdout?.removeAllListeners();
2811
2832
  child.stderr?.removeAllListeners();
@@ -2826,7 +2847,8 @@ var MCPClient = class _MCPClient {
2826
2847
  err.name = "AbortError";
2827
2848
  return Promise.reject(err);
2828
2849
  }
2829
- const id = this.nextId++;
2850
+ const id = this.nextId;
2851
+ this.nextId = nextJsonRpcId(id);
2830
2852
  const req = { jsonrpc: "2.0", id, method, params };
2831
2853
  return new Promise((resolve, reject) => {
2832
2854
  const onAbort = signal ? () => {
@@ -5015,7 +5037,9 @@ var MCPServer = class {
5015
5037
  switch (method) {
5016
5038
  case "initialize":
5017
5039
  return {
5018
- protocolVersion: MCP_CONSTANTS.PROTOCOL_VERSION,
5040
+ protocolVersion: negotiateProtocolVersion(
5041
+ params?.protocolVersion
5042
+ ),
5019
5043
  capabilities: {
5020
5044
  tools: { listChanged: false },
5021
5045
  ...this.resources.length > 0 ? { resources: { subscribe: false, listChanged: false } } : {},
@@ -5420,11 +5444,9 @@ async function handleHttpRequest(server, req, res, token, log, boundHost) {
5420
5444
  req.on("end", () => {
5421
5445
  if (aborted) return;
5422
5446
  void server.handleMessage(body).then((out) => {
5423
- if (aborted) return;
5424
5447
  if (out === null) return send(202, "");
5425
5448
  return send(200, out);
5426
5449
  }).catch((err) => {
5427
- if (aborted) return;
5428
5450
  log?.warn?.(`MCP http handler error: ${toErrorMessage2(err)}`);
5429
5451
  send(500, JSON.stringify({ error: "internal error" }));
5430
5452
  });
@@ -5795,6 +5817,7 @@ export {
5795
5817
  MCP_OPERATION_LIMITS,
5796
5818
  SSEReader,
5797
5819
  SSETransport,
5820
+ SUPPORTED_PROTOCOL_VERSIONS,
5798
5821
  StreamableHTTPTransport,
5799
5822
  addMcp,
5800
5823
  authorizationHeaderForToken,
@@ -5809,6 +5832,7 @@ export {
5809
5832
  exchangeMcpAuthorizationCode,
5810
5833
  listMcp,
5811
5834
  manifestConfigHash,
5835
+ negotiateProtocolVersion,
5812
5836
  parseAuthorizationServerMetadata,
5813
5837
  parseGetPromptResult,
5814
5838
  parseListPromptsResult,
package/dist/server.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { type IncomingMessage, type ServerResponse } from 'node:http';
1
2
  import type { MCPPromptArgument, MCPPromptMessage, MCPResourceContents } from './protocol.js';
2
3
  /**
3
4
  * Server-side MCP. The mirror image of `MCPClient`: instead of consuming a
@@ -164,4 +165,5 @@ export interface ServeHttpHandle {
164
165
  * otherwise expose tool execution to the whole network unauthenticated.
165
166
  */
166
167
  export declare function serveHttp(server: MCPServer, opts?: ServeHttpOptions): Promise<ServeHttpHandle>;
168
+ export declare function handleHttpRequest(server: MCPServer, req: IncomingMessage, res: ServerResponse, token: string | undefined, log: MCPServerLogger | undefined, boundHost: string): Promise<void>;
167
169
  //# sourceMappingURL=server.d.ts.map
@@ -49,6 +49,8 @@ export interface HttpTransportOptions {
49
49
  * classifyToolError maps it to FATAL / not-retryable (user cancellation).
50
50
  */
51
51
  export declare function makeAbortError(method: string): Error;
52
+ /** Advance a JSON-RPC number id without leaving JavaScript's safe-integer range. */
53
+ export declare function nextJsonRpcId(id: number): number;
52
54
  export declare function createTimeoutSignal(parent: AbortSignal | undefined, timeoutMs: number): {
53
55
  signal: AbortSignal;
54
56
  dispose: () => void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wrongstack/mcp",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
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.2",
29
+ "@wrongstack/core": "1.0.4",
30
30
  "undici": "^8.10.0"
31
31
  },
32
32
  "devDependencies": {