fastmcp 4.7.2 → 4.8.1

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
@@ -1406,6 +1406,26 @@ server.addResource({
1406
1406
  });
1407
1407
  ```
1408
1408
 
1409
+ #### Subscribing to resource updates
1410
+
1411
+ Clients can subscribe to a resource with the MCP [`resources/subscribe`](https://modelcontextprotocol.io/specification/2025-06-18/server/resources#subscriptions) method to be notified whenever its contents change. FastMCP advertises the `subscribe` capability automatically for any server that exposes resources, tracks each client's subscriptions, and lets you emit an update with `sendResourceUpdated`:
1412
+
1413
+ ```ts
1414
+ server.addResource({
1415
+ uri: "file:///logs/app.log",
1416
+ name: "Application Logs",
1417
+ mimeType: "text/plain",
1418
+ async load() {
1419
+ return { text: await readLogFile() };
1420
+ },
1421
+ });
1422
+
1423
+ // Whenever the underlying data changes, notify subscribed clients:
1424
+ await server.sendResourceUpdated("file:///logs/app.log");
1425
+ ```
1426
+
1427
+ `sendResourceUpdated` only notifies clients that have subscribed to the given URI, so it is safe to call whenever your data changes. FastMCP also advertises the `listChanged` capability for resources and prompts and emits `notifications/resources/list_changed` / `notifications/prompts/list_changed` automatically when you add or remove resources, resource templates, or prompts at runtime.
1428
+
1409
1429
  ### Resource templates
1410
1430
 
1411
1431
  You can also define resource templates:
package/dist/FastMCP.cjs CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
 
9
9
 
10
- var _chunk553XY3CAcjs = require('./chunk-553XY3CA.cjs');
10
+ var _chunkMJCWKCHZcjs = require('./chunk-MJCWKCHZ.cjs');
11
11
 
12
12
 
13
13
 
@@ -41,5 +41,5 @@ var _chunkIX3HKAX4cjs = require('./chunk-IX3HKAX4.cjs');
41
41
 
42
42
 
43
43
 
44
- exports.AuthProvider = _chunkIX3HKAX4cjs.AuthProvider; exports.AzureProvider = _chunkIX3HKAX4cjs.AzureProvider; exports.DiscoveryDocumentCache = _chunk553XY3CAcjs.DiscoveryDocumentCache; exports.FastMCP = _chunk553XY3CAcjs.FastMCP; exports.FastMCPSession = _chunk553XY3CAcjs.FastMCPSession; exports.GitHubProvider = _chunkIX3HKAX4cjs.GitHubProvider; exports.GoogleProvider = _chunkIX3HKAX4cjs.GoogleProvider; exports.OAuthProvider = _chunkIX3HKAX4cjs.OAuthProvider; exports.ServerState = _chunk553XY3CAcjs.ServerState; exports.UnexpectedStateError = _chunk553XY3CAcjs.UnexpectedStateError; exports.UserError = _chunk553XY3CAcjs.UserError; exports.audioContent = _chunk553XY3CAcjs.audioContent; exports.getAuthSession = _chunkIX3HKAX4cjs.getAuthSession; exports.imageContent = _chunk553XY3CAcjs.imageContent; exports.requireAll = _chunkIX3HKAX4cjs.requireAll; exports.requireAny = _chunkIX3HKAX4cjs.requireAny; exports.requireAuth = _chunkIX3HKAX4cjs.requireAuth; exports.requireRole = _chunkIX3HKAX4cjs.requireRole; exports.requireScopes = _chunkIX3HKAX4cjs.requireScopes;
44
+ exports.AuthProvider = _chunkIX3HKAX4cjs.AuthProvider; exports.AzureProvider = _chunkIX3HKAX4cjs.AzureProvider; exports.DiscoveryDocumentCache = _chunkMJCWKCHZcjs.DiscoveryDocumentCache; exports.FastMCP = _chunkMJCWKCHZcjs.FastMCP; exports.FastMCPSession = _chunkMJCWKCHZcjs.FastMCPSession; exports.GitHubProvider = _chunkIX3HKAX4cjs.GitHubProvider; exports.GoogleProvider = _chunkIX3HKAX4cjs.GoogleProvider; exports.OAuthProvider = _chunkIX3HKAX4cjs.OAuthProvider; exports.ServerState = _chunkMJCWKCHZcjs.ServerState; exports.UnexpectedStateError = _chunkMJCWKCHZcjs.UnexpectedStateError; exports.UserError = _chunkMJCWKCHZcjs.UserError; exports.audioContent = _chunkMJCWKCHZcjs.audioContent; exports.getAuthSession = _chunkIX3HKAX4cjs.getAuthSession; exports.imageContent = _chunkMJCWKCHZcjs.imageContent; exports.requireAll = _chunkIX3HKAX4cjs.requireAll; exports.requireAny = _chunkIX3HKAX4cjs.requireAny; exports.requireAuth = _chunkIX3HKAX4cjs.requireAuth; exports.requireRole = _chunkIX3HKAX4cjs.requireRole; exports.requireScopes = _chunkIX3HKAX4cjs.requireScopes;
45
45
  //# sourceMappingURL=FastMCP.cjs.map
@@ -750,6 +750,14 @@ declare class FastMCPSession<T extends FastMCPSessionAuth = FastMCPSessionAuth>
750
750
  requestSampling(message: z.infer<typeof CreateMessageRequestSchema>["params"], options?: RequestOptions): Promise<SamplingResponse>;
751
751
  resourcesListChanged(resources: Resource<T>[]): void;
752
752
  resourceTemplatesListChanged(resourceTemplates: ResourceTemplate<T>[]): void;
753
+ /**
754
+ * Notifies the connected client that the contents of a resource have changed.
755
+ *
756
+ * The `notifications/resources/updated` notification is only sent when the
757
+ * client has subscribed to the URI via `resources/subscribe`; otherwise this
758
+ * is a no-op.
759
+ */
760
+ sendResourceUpdated(uri: string): Promise<void>;
753
761
  toolsListChanged(tools: Tool<T>[]): void;
754
762
  triggerListChangedNotification(method: string): Promise<void>;
755
763
  /**
@@ -766,6 +774,7 @@ declare class FastMCPSession<T extends FastMCPSessionAuth = FastMCPSessionAuth>
766
774
  private setupLoggingHandlers;
767
775
  private setupPromptHandlers;
768
776
  private setupResourceHandlers;
777
+ private setupResourceSubscriptionHandlers;
769
778
  private setupResourceTemplateHandlers;
770
779
  private setupRootsHandlers;
771
780
  private setupToolHandlers;
@@ -874,6 +883,17 @@ declare class FastMCP<T extends FastMCPSessionAuth = FastMCPSessionAuth> extends
874
883
  * Removes tools from the server.
875
884
  */
876
885
  removeTools(names: string[]): void;
886
+ /**
887
+ * Notifies subscribed clients that a resource's contents have changed.
888
+ *
889
+ * Sends a `notifications/resources/updated` notification to every connected
890
+ * session that has subscribed to `uri` via `resources/subscribe`. Sessions
891
+ * that have not subscribed to the URI are skipped, so it is safe to call this
892
+ * whenever the underlying data changes.
893
+ *
894
+ * @param uri - The URI of the resource whose contents changed.
895
+ */
896
+ sendResourceUpdated(uri: string): Promise<void>;
877
897
  /**
878
898
  * Starts the server.
879
899
  */
package/dist/FastMCP.d.ts CHANGED
@@ -750,6 +750,14 @@ declare class FastMCPSession<T extends FastMCPSessionAuth = FastMCPSessionAuth>
750
750
  requestSampling(message: z.infer<typeof CreateMessageRequestSchema>["params"], options?: RequestOptions): Promise<SamplingResponse>;
751
751
  resourcesListChanged(resources: Resource<T>[]): void;
752
752
  resourceTemplatesListChanged(resourceTemplates: ResourceTemplate<T>[]): void;
753
+ /**
754
+ * Notifies the connected client that the contents of a resource have changed.
755
+ *
756
+ * The `notifications/resources/updated` notification is only sent when the
757
+ * client has subscribed to the URI via `resources/subscribe`; otherwise this
758
+ * is a no-op.
759
+ */
760
+ sendResourceUpdated(uri: string): Promise<void>;
753
761
  toolsListChanged(tools: Tool<T>[]): void;
754
762
  triggerListChangedNotification(method: string): Promise<void>;
755
763
  /**
@@ -766,6 +774,7 @@ declare class FastMCPSession<T extends FastMCPSessionAuth = FastMCPSessionAuth>
766
774
  private setupLoggingHandlers;
767
775
  private setupPromptHandlers;
768
776
  private setupResourceHandlers;
777
+ private setupResourceSubscriptionHandlers;
769
778
  private setupResourceTemplateHandlers;
770
779
  private setupRootsHandlers;
771
780
  private setupToolHandlers;
@@ -874,6 +883,17 @@ declare class FastMCP<T extends FastMCPSessionAuth = FastMCPSessionAuth> extends
874
883
  * Removes tools from the server.
875
884
  */
876
885
  removeTools(names: string[]): void;
886
+ /**
887
+ * Notifies subscribed clients that a resource's contents have changed.
888
+ *
889
+ * Sends a `notifications/resources/updated` notification to every connected
890
+ * session that has subscribed to `uri` via `resources/subscribe`. Sessions
891
+ * that have not subscribed to the URI are skipped, so it is safe to call this
892
+ * whenever the underlying data changes.
893
+ *
894
+ * @param uri - The URI of the resource whose contents changed.
895
+ */
896
+ sendResourceUpdated(uri: string): Promise<void>;
877
897
  /**
878
898
  * Starts the server.
879
899
  */
package/dist/FastMCP.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  UserError,
8
8
  audioContent,
9
9
  imageContent
10
- } from "./chunk-KAGSBU3Z.js";
10
+ } from "./chunk-2PFUZL7I.js";
11
11
  import {
12
12
  AuthProvider,
13
13
  AzureProvider,
@@ -13,7 +13,9 @@ import {
13
13
  McpError,
14
14
  ReadResourceRequestSchema,
15
15
  RootsListChangedNotificationSchema,
16
- SetLevelRequestSchema
16
+ SetLevelRequestSchema,
17
+ SubscribeRequestSchema,
18
+ UnsubscribeRequestSchema
17
19
  } from "@modelcontextprotocol/sdk/types.js";
18
20
  import { EventEmitter } from "events";
19
21
  import { readFile } from "fs/promises";
@@ -357,6 +359,12 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
357
359
  * Used to track per-session state across multiple requests.
358
360
  */
359
361
  #sessionId;
362
+ /**
363
+ * Resource URIs the connected client has subscribed to via
364
+ * `resources/subscribe`. Used to scope `notifications/resources/updated`
365
+ * to interested clients only.
366
+ */
367
+ #subscriptions = /* @__PURE__ */ new Set();
360
368
  #utils;
361
369
  constructor({
362
370
  auth,
@@ -387,13 +395,13 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
387
395
  this.#capabilities.tools = {};
388
396
  }
389
397
  if (resources.length || resourcesTemplates.length) {
390
- this.#capabilities.resources = {};
398
+ this.#capabilities.resources = { listChanged: true, subscribe: true };
391
399
  }
392
400
  if (prompts.length) {
393
401
  for (const prompt of prompts) {
394
402
  this.addPrompt(prompt);
395
403
  }
396
- this.#capabilities.prompts = {};
404
+ this.#capabilities.prompts = { listChanged: true };
397
405
  }
398
406
  this.#capabilities.logging = {};
399
407
  this.#capabilities.completions = {};
@@ -414,6 +422,7 @@ var FastMCPSession = class extends FastMCPSessionEventEmitter {
414
422
  this.addResource(resource);
415
423
  }
416
424
  this.setupResourceHandlers();
425
+ this.setupResourceSubscriptionHandlers();
417
426
  if (resourcesTemplates.length) {
418
427
  for (const resourceTemplate of resourcesTemplates) {
419
428
  this.addResourceTemplate(resourceTemplate);
@@ -549,6 +558,27 @@ ${e instanceof Error ? e.stack : JSON.stringify(e)}`
549
558
  this.setupResourceTemplateHandlers();
550
559
  this.triggerListChangedNotification("notifications/resources/list_changed");
551
560
  }
561
+ /**
562
+ * Notifies the connected client that the contents of a resource have changed.
563
+ *
564
+ * The `notifications/resources/updated` notification is only sent when the
565
+ * client has subscribed to the URI via `resources/subscribe`; otherwise this
566
+ * is a no-op.
567
+ */
568
+ async sendResourceUpdated(uri) {
569
+ if (!this.#subscriptions.has(uri)) {
570
+ return;
571
+ }
572
+ try {
573
+ await this.#server.sendResourceUpdated({ uri });
574
+ } catch (error) {
575
+ this.#logger.error(
576
+ `[FastMCP error] failed to send resources/updated notification for '${uri}'.
577
+
578
+ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
579
+ );
580
+ }
581
+ }
552
582
  toolsListChanged(tools) {
553
583
  const allowedTools = tools.filter(
554
584
  (tool) => tool.canAccess ? tool.canAccess(this.#auth) : true
@@ -997,6 +1027,16 @@ ${error instanceof Error ? error.stack : JSON.stringify(error)}`
997
1027
  }
998
1028
  );
999
1029
  }
1030
+ setupResourceSubscriptionHandlers() {
1031
+ this.#server.setRequestHandler(SubscribeRequestSchema, (request) => {
1032
+ this.#subscriptions.add(request.params.uri);
1033
+ return {};
1034
+ });
1035
+ this.#server.setRequestHandler(UnsubscribeRequestSchema, (request) => {
1036
+ this.#subscriptions.delete(request.params.uri);
1037
+ return {};
1038
+ });
1039
+ }
1000
1040
  setupResourceTemplateHandlers() {
1001
1041
  let cachedResourceTemplatesList = null;
1002
1042
  this.#server.setRequestHandler(
@@ -1599,6 +1639,21 @@ var FastMCP = class extends FastMCPEventEmitter {
1599
1639
  this.#toolsListChanged(this.#tools);
1600
1640
  }
1601
1641
  }
1642
+ /**
1643
+ * Notifies subscribed clients that a resource's contents have changed.
1644
+ *
1645
+ * Sends a `notifications/resources/updated` notification to every connected
1646
+ * session that has subscribed to `uri` via `resources/subscribe`. Sessions
1647
+ * that have not subscribed to the URI are skipped, so it is safe to call this
1648
+ * whenever the underlying data changes.
1649
+ *
1650
+ * @param uri - The URI of the resource whose contents changed.
1651
+ */
1652
+ async sendResourceUpdated(uri) {
1653
+ await Promise.all(
1654
+ this.#sessions.map((session) => session.sendResourceUpdated(uri))
1655
+ );
1656
+ }
1602
1657
  /**
1603
1658
  * Starts the server.
1604
1659
  */
@@ -1636,6 +1691,17 @@ var FastMCP = class extends FastMCPEventEmitter {
1636
1691
  version: this.#options.version
1637
1692
  });
1638
1693
  await session.connect(transport);
1694
+ let stdinClosed = false;
1695
+ const onStdinClose = () => {
1696
+ if (stdinClosed) return;
1697
+ stdinClosed = true;
1698
+ process.stdin.off("close", onStdinClose);
1699
+ process.stdin.off("end", onStdinClose);
1700
+ transport.close().catch(() => {
1701
+ });
1702
+ };
1703
+ process.stdin.on("close", onStdinClose);
1704
+ process.stdin.on("end", onStdinClose);
1639
1705
  this.#sessions.push(session);
1640
1706
  session.once("error", () => {
1641
1707
  this.#removeSession(session);
@@ -1643,6 +1709,8 @@ var FastMCP = class extends FastMCPEventEmitter {
1643
1709
  if (transport.onclose) {
1644
1710
  const originalOnClose = transport.onclose;
1645
1711
  transport.onclose = () => {
1712
+ process.stdin.off("close", onStdinClose);
1713
+ process.stdin.off("end", onStdinClose);
1646
1714
  this.#removeSession(session);
1647
1715
  if (originalOnClose) {
1648
1716
  originalOnClose();
@@ -1650,6 +1718,8 @@ var FastMCP = class extends FastMCPEventEmitter {
1650
1718
  };
1651
1719
  } else {
1652
1720
  transport.onclose = () => {
1721
+ process.stdin.off("close", onStdinClose);
1722
+ process.stdin.off("end", onStdinClose);
1653
1723
  this.#removeSession(session);
1654
1724
  };
1655
1725
  }
@@ -2296,4 +2366,4 @@ export {
2296
2366
  FastMCPSession,
2297
2367
  FastMCP
2298
2368
  };
2299
- //# sourceMappingURL=chunk-KAGSBU3Z.js.map
2369
+ //# sourceMappingURL=chunk-2PFUZL7I.js.map