@j0hanz/superfetch 2.1.3 → 2.1.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.
package/dist/cache.js CHANGED
@@ -410,6 +410,30 @@ function appendServerOnClose(server, handler) {
410
410
  handler();
411
411
  };
412
412
  }
413
+ function attachInitializedGate(server) {
414
+ let initialized = false;
415
+ const previousInitialized = server.server.oninitialized;
416
+ server.server.oninitialized = () => {
417
+ initialized = true;
418
+ previousInitialized?.();
419
+ };
420
+ return () => initialized;
421
+ }
422
+ function getClientResourceCapabilities(server) {
423
+ const caps = server.server.getClientCapabilities();
424
+ if (!caps || !isRecord(caps)) {
425
+ return { listChanged: true, subscribe: true };
426
+ }
427
+ const { resources } = caps;
428
+ if (!isRecord(resources)) {
429
+ return { listChanged: true, subscribe: true };
430
+ }
431
+ const { listChanged, subscribe } = resources;
432
+ return {
433
+ listChanged: listChanged === true,
434
+ subscribe: subscribe === true,
435
+ };
436
+ }
413
437
  function registerResourceSubscriptionHandlers(server) {
414
438
  const subscriptions = new Set();
415
439
  server.server.setRequestHandler(SubscribeRequestSchema, (request) => {
@@ -438,9 +462,10 @@ function notifyResourceUpdate(server, uri, subscriptions) {
438
462
  });
439
463
  }
440
464
  export function registerCachedContentResource(server) {
465
+ const isInitialized = attachInitializedGate(server);
441
466
  const subscriptions = registerResourceSubscriptionHandlers(server);
442
467
  registerCacheContentResource(server);
443
- registerCacheUpdateSubscription(server, subscriptions);
468
+ registerCacheUpdateSubscription(server, subscriptions, isInitialized);
444
469
  }
445
470
  function buildCachedContentResponse(uri, cacheKey) {
446
471
  const cached = requireCacheEntry(cacheKey);
@@ -459,13 +484,18 @@ function registerCacheContentResource(server) {
459
484
  return buildCachedContentResponse(uri, cacheKey);
460
485
  });
461
486
  }
462
- function registerCacheUpdateSubscription(server, subscriptions) {
487
+ function registerCacheUpdateSubscription(server, subscriptions, isInitialized) {
463
488
  const unsubscribe = onCacheUpdate(({ cacheKey }) => {
464
- const resourceUri = toResourceUri(cacheKey);
465
- if (!resourceUri)
489
+ if (!server.isConnected() || !isInitialized())
466
490
  return;
467
- notifyResourceUpdate(server, resourceUri, subscriptions);
468
- if (server.isConnected()) {
491
+ const { listChanged, subscribe } = getClientResourceCapabilities(server);
492
+ if (subscribe) {
493
+ const resourceUri = toResourceUri(cacheKey);
494
+ if (resourceUri) {
495
+ notifyResourceUpdate(server, resourceUri, subscriptions);
496
+ }
497
+ }
498
+ if (listChanged) {
469
499
  server.sendResourceListChanged();
470
500
  }
471
501
  });
package/dist/http.d.ts CHANGED
@@ -4,6 +4,7 @@ interface SessionEntry {
4
4
  readonly transport: StreamableHTTPServerTransport;
5
5
  createdAt: number;
6
6
  lastSeen: number;
7
+ protocolInitialized: boolean;
7
8
  }
8
9
  interface McpRequestParams {
9
10
  _meta?: Record<string, unknown>;
package/dist/http.js CHANGED
@@ -988,6 +988,14 @@ function respondServerBusy(res, requestId) {
988
988
  function respondBadRequest(res, id) {
989
989
  sendJsonRpcErrorOrNoContent(res, -32000, 'Bad Request: Missing session ID or not an initialize request', 400, id);
990
990
  }
991
+ function respondSessionNotInitialized(res, requestId) {
992
+ sendJsonRpcErrorOrNoContent(res, -32000, 'Bad Request: Session not initialized', 400, requestId);
993
+ }
994
+ function isAllowedBeforeInitialized(method) {
995
+ return (method === 'initialize' ||
996
+ method === 'notifications/initialized' ||
997
+ method === 'ping');
998
+ }
991
999
  function createTimeoutController() {
992
1000
  let initTimeout = null;
993
1001
  return {
@@ -1116,6 +1124,7 @@ async function connectTransportOrThrow({ transport, clearInitTimeout, releaseSlo
1116
1124
  logError('Failed to initialize MCP session', error instanceof Error ? error : undefined);
1117
1125
  throw error;
1118
1126
  }
1127
+ return mcpServer;
1119
1128
  }
1120
1129
  function evictExpiredSessionsWithClose(store) {
1121
1130
  const evicted = store.evictExpired();
@@ -1156,9 +1165,14 @@ function reserveSessionIfPossible({ options, res, requestId, }) {
1156
1165
  }
1157
1166
  return true;
1158
1167
  }
1159
- function resolveExistingSessionTransport(store, sessionId, res, requestId) {
1168
+ function resolveExistingSessionTransport(store, sessionId, res, requestId, method) {
1160
1169
  const existingSession = store.get(sessionId);
1161
1170
  if (existingSession) {
1171
+ if (!existingSession.protocolInitialized &&
1172
+ !isAllowedBeforeInitialized(method)) {
1173
+ respondSessionNotInitialized(res, requestId);
1174
+ return null;
1175
+ }
1162
1176
  store.touch(sessionId);
1163
1177
  return existingSession.transport;
1164
1178
  }
@@ -1172,7 +1186,17 @@ function createSessionContext() {
1172
1186
  const transport = createSessionTransport({ tracker, timeoutController });
1173
1187
  return { tracker, timeoutController, transport };
1174
1188
  }
1175
- function finalizeSessionIfValid({ store, transport, tracker, clearInitTimeout, res, requestId, }) {
1189
+ function attachSessionInitializedHandler(server, store, sessionId) {
1190
+ const previousInitialized = server.server.oninitialized;
1191
+ server.server.oninitialized = () => {
1192
+ const entry = store.get(sessionId);
1193
+ if (entry) {
1194
+ entry.protocolInitialized = true;
1195
+ }
1196
+ previousInitialized?.();
1197
+ };
1198
+ }
1199
+ function finalizeSessionIfValid({ store, transport, mcpServer, tracker, clearInitTimeout, res, requestId, }) {
1176
1200
  const { sessionId } = transport;
1177
1201
  if (typeof sessionId !== 'string') {
1178
1202
  clearInitTimeout();
@@ -1184,12 +1208,13 @@ function finalizeSessionIfValid({ store, transport, tracker, clearInitTimeout, r
1184
1208
  store,
1185
1209
  transport,
1186
1210
  sessionId,
1211
+ mcpServer,
1187
1212
  tracker,
1188
1213
  clearInitTimeout,
1189
1214
  });
1190
1215
  return true;
1191
1216
  }
1192
- function finalizeSession({ store, transport, sessionId, tracker, clearInitTimeout, }) {
1217
+ function finalizeSession({ store, transport, sessionId, mcpServer, tracker, clearInitTimeout, }) {
1193
1218
  clearInitTimeout();
1194
1219
  tracker.markInitialized();
1195
1220
  tracker.releaseSlot();
@@ -1198,7 +1223,9 @@ function finalizeSession({ store, transport, sessionId, tracker, clearInitTimeou
1198
1223
  transport,
1199
1224
  createdAt: now,
1200
1225
  lastSeen: now,
1226
+ protocolInitialized: false,
1201
1227
  });
1228
+ attachSessionInitializedHandler(mcpServer, store, sessionId);
1202
1229
  const previousOnClose = transport.onclose;
1203
1230
  transport.onclose = composeCloseHandlers(previousOnClose, () => {
1204
1231
  store.remove(sessionId);
@@ -1215,7 +1242,7 @@ async function createAndConnectTransport({ options, res, requestId, }) {
1215
1242
  if (!reserveSessionIfPossible(reserveArgs))
1216
1243
  return null;
1217
1244
  const { tracker, timeoutController, transport } = createSessionContext();
1218
- await connectTransportOrThrow({
1245
+ const mcpServer = await connectTransportOrThrow({
1219
1246
  transport,
1220
1247
  clearInitTimeout: timeoutController.clear,
1221
1248
  releaseSlot: tracker.releaseSlot,
@@ -1223,6 +1250,7 @@ async function createAndConnectTransport({ options, res, requestId, }) {
1223
1250
  if (!finalizeSessionIfValid({
1224
1251
  store: options.sessionStore,
1225
1252
  transport,
1253
+ mcpServer,
1226
1254
  tracker,
1227
1255
  clearInitTimeout: timeoutController.clear,
1228
1256
  res,
@@ -1235,7 +1263,7 @@ async function createAndConnectTransport({ options, res, requestId, }) {
1235
1263
  export async function resolveTransportForPost({ res, body, sessionId, options, }) {
1236
1264
  const requestId = body.id ?? null;
1237
1265
  if (sessionId) {
1238
- return resolveExistingSessionTransport(options.sessionStore, sessionId, res, requestId);
1266
+ return resolveExistingSessionTransport(options.sessionStore, sessionId, res, requestId, body.method);
1239
1267
  }
1240
1268
  if (!isInitializeRequest(body)) {
1241
1269
  respondBadRequest(res, requestId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@j0hanz/superfetch",
3
- "version": "2.1.3",
3
+ "version": "2.1.4",
4
4
  "mcpName": "io.github.j0hanz/superfetch",
5
5
  "description": "Intelligent web content fetcher MCP server that converts HTML to clean, AI-readable Markdown",
6
6
  "type": "module",
@@ -64,15 +64,15 @@
64
64
  "@eslint/js": "^9.39.2",
65
65
  "@trivago/prettier-plugin-sort-imports": "^6.0.2",
66
66
  "@types/express": "^5.0.6",
67
- "@types/node": "^22.19.5",
67
+ "@types/node": "^22.19.6",
68
68
  "eslint": "^9.23.2",
69
69
  "eslint-config-prettier": "^10.1.8",
70
70
  "eslint-plugin-de-morgan": "^2.0.0",
71
71
  "eslint-plugin-depend": "^1.4.0",
72
72
  "eslint-plugin-sonarjs": "^3.0.5",
73
73
  "eslint-plugin-unused-imports": "^4.3.0",
74
- "knip": "^5.80.2",
75
- "prettier": "^3.7.4",
74
+ "knip": "^5.81.0",
75
+ "prettier": "^3.8.0",
76
76
  "tsx": "^4.21.0",
77
77
  "typescript": "^5.9.3",
78
78
  "typescript-eslint": "^8.53.0"