4
4
5
5
part of 'server.dart' ;
6
6
7
+ typedef ReadResourceHandler =
8
+ FutureOr <ReadResourceResult ?> Function (ReadResourceRequest );
9
+
7
10
/// A mixin for MCP servers which support the `resources` capability.
8
11
///
9
- /// Servers should add resources using the [addResource] method, typically
10
- /// inside the [initialize] method, but they may also be added after
11
- /// initialization if needed.
12
+ /// Servers should add [Resource] s using the [addResource] method, typically
13
+ /// inside the [initialize] method or constructor , but they may also be added
14
+ /// after initialization if needed.
12
15
///
13
16
/// Resources can later be removed using [removeResource] , or the client can be
14
17
/// notified of updates using [updateResource] .
15
18
///
16
19
/// Implements the `subscribe` and `listChanges` capabilities for clients, so
17
20
/// they can be notified of changes to resources.
18
21
///
22
+ /// Any [ResourceTemplate] s, should typically be added in [initialize] method or
23
+ /// the constructor using [addResourceTemplate] . There is no notification
24
+ /// protocol for templates which are added after a client requests them once, so
25
+ /// they should be added eagerly.
26
+ ///
19
27
/// See https://modelcontextprotocol.io/docs/concepts/resources.
20
28
base mixin ResourcesSupport on MCPServer {
21
29
/// The current resources by URI.
22
30
final Map <String , Resource > _resources = {};
23
31
24
32
/// The current resource implementations by URI.
25
- final Map <String , FutureOr <ReadResourceResult > Function (ReadResourceRequest )>
26
- _resourceImpls = {};
33
+ final Map <String , ReadResourceHandler > _resourceImpls = {};
34
+
35
+ /// All the resource templates supported by this server, see
36
+ /// [addResourceTemplate] .
37
+ final List <({ResourceTemplate template, ReadResourceHandler handler})>
38
+ _resourceTemplates = [];
27
39
28
40
/// The list of currently subscribed resources by URI.
29
41
final Set <String > _subscribedResources = {};
@@ -39,6 +51,10 @@ base mixin ResourcesSupport on MCPServer {
39
51
@override
40
52
FutureOr <InitializeResult > initialize (InitializeRequest request) async {
41
53
registerRequestHandler (ListResourcesRequest .methodName, _listResources);
54
+ registerRequestHandler (
55
+ ListResourceTemplatesRequest .methodName,
56
+ _listResourceTemplates,
57
+ );
42
58
43
59
registerRequestHandler (ReadResourceRequest .methodName, _readResource);
44
60
@@ -57,7 +73,7 @@ base mixin ResourcesSupport on MCPServer {
57
73
/// If this server is already initialized and still connected to a client,
58
74
/// then the client will be notified that the resources list has changed.
59
75
///
60
- /// Throws a [StateError] if there is already a [Tool ] registered with the
76
+ /// Throws a [StateError] if there is already a [Resource ] registered with the
61
77
/// same name.
62
78
void addResource (
63
79
Resource resource,
@@ -77,6 +93,43 @@ base mixin ResourcesSupport on MCPServer {
77
93
}
78
94
}
79
95
96
+ /// Adds the [ResourceTemplate] [template] with [handler] .
97
+ ///
98
+ /// When reading resources, first regular resources added by [addResource]
99
+ /// are prioritized. Then, we call the [handler] for each [template] , in the
100
+ /// order they were added (using this method), and the first one to return a
101
+ /// non-null response wins. This package does not automatically handle
102
+ /// matching of templates and handlers must accept URIs in any form.
103
+ ///
104
+ /// Throws a [StateError] if there is already a template registered with the
105
+ /// same uri template.
106
+ void addResourceTemplate (
107
+ ResourceTemplate template,
108
+ ReadResourceHandler handler,
109
+ ) {
110
+ if (_resourceTemplates.any (
111
+ (t) => t.template.uriTemplate == template.uriTemplate,
112
+ )) {
113
+ throw StateError (
114
+ 'Failed to add resource template ${template .name }, there is '
115
+ 'already a resource template with the same uri pattern '
116
+ '${template .uriTemplate }.' ,
117
+ );
118
+ }
119
+ _resourceTemplates.add ((template: template, handler: handler));
120
+ }
121
+
122
+ /// Lists all the [ResourceTemplate] s currently available.
123
+ ListResourceTemplatesResult _listResourceTemplates (
124
+ ListResourceTemplatesRequest request,
125
+ ) {
126
+ return ListResourceTemplatesResult (
127
+ resourceTemplates: [
128
+ for (var descriptor in _resourceTemplates) descriptor.template,
129
+ ],
130
+ );
131
+ }
132
+
80
133
/// Notifies the client that [resource] has been updated.
81
134
///
82
135
/// The implementation of that resource can optionally be updated, otherwise
@@ -121,13 +174,23 @@ base mixin ResourcesSupport on MCPServer {
121
174
///
122
175
/// Throws an [ArgumentError] if it does not exist (this gets translated into
123
176
/// a generic JSON RPC2 error response).
124
- FutureOr <ReadResourceResult > _readResource (ReadResourceRequest request) {
177
+ FutureOr <ReadResourceResult > _readResource (
178
+ ReadResourceRequest request,
179
+ ) async {
125
180
final impl = _resourceImpls[request.uri];
126
181
if (impl == null ) {
127
- throw ArgumentError .value (request.uri, 'uri' , 'Resource not found' );
182
+ // Check if it matches any resource template.
183
+ for (var descriptor in _resourceTemplates) {
184
+ final response = await descriptor.handler (request);
185
+ if (response != null ) return response;
186
+ }
128
187
}
129
188
130
- return impl (request);
189
+ final response = await impl? .call (request);
190
+ if (response == null ) {
191
+ throw ArgumentError .value (request.uri, 'uri' , 'Resource not found' );
192
+ }
193
+ return response;
131
194
}
132
195
133
196
/// Subscribes the client to the resource at `request.uri` .
0 commit comments