@@ -13,6 +13,8 @@ import 'package:dart_mcp_server/src/server.dart';
1313import 'package:dart_mcp_server/src/utils/analytics.dart' ;
1414import 'package:dart_mcp_server/src/utils/constants.dart' ;
1515import 'package:devtools_shared/devtools_shared.dart' ;
16+ import 'package:dtd/dtd.dart' ;
17+ import 'package:json_rpc_2/json_rpc_2.dart' ;
1618import 'package:test/test.dart' ;
1719import 'package:unified_analytics/testing.dart' ;
1820import 'package:unified_analytics/unified_analytics.dart' as ua;
@@ -98,6 +100,215 @@ void main() {
98100 });
99101 });
100102
103+ group ('sampling service extension' , () {
104+ List <String > extractResponse (DTDResponse response) {
105+ final responseContent =
106+ response.result['content' ] as Map <String , Object ?>;
107+ return (responseContent['text' ] as String ).split ('\n ' );
108+ }
109+
110+ test ('can make a sampling request with text' , () async {
111+ final dtdClient = testHarness.fakeEditorExtension.dtd;
112+ final response = await dtdClient.call (
113+ McpServiceConstants .serviceName,
114+ McpServiceConstants .samplingRequest,
115+ params: {
116+ 'messages' : [
117+ {
118+ 'role' : 'user' ,
119+ 'content' : {'type' : 'text' , 'text' : 'hello world' },
120+ },
121+ ],
122+ 'maxTokens' : 512 ,
123+ },
124+ );
125+ expect (extractResponse (response), [
126+ 'TOKENS: 512' ,
127+ '[user] hello world' ,
128+ ]);
129+ });
130+
131+ test ('can make a sampling request with an image' , () async {
132+ final dtdClient = testHarness.fakeEditorExtension.dtd;
133+ final response = await dtdClient.call (
134+ McpServiceConstants .serviceName,
135+ McpServiceConstants .samplingRequest,
136+ params: {
137+ 'messages' : [
138+ {
139+ 'role' : 'user' ,
140+ 'content' : {
141+ 'type' : 'image' ,
142+ 'data' : 'fake-data' ,
143+ 'mimeType' : 'image/png' ,
144+ },
145+ },
146+ ],
147+ 'maxTokens' : 256 ,
148+ },
149+ );
150+ expect (extractResponse (response), [
151+ 'TOKENS: 256' ,
152+ '[user] image/png' ,
153+ ]);
154+ });
155+
156+ test ('can make a sampling request with audio' , () async {
157+ final dtdClient = testHarness.fakeEditorExtension.dtd;
158+ final response = await dtdClient.call (
159+ McpServiceConstants .serviceName,
160+ McpServiceConstants .samplingRequest,
161+ params: {
162+ 'messages' : [
163+ {
164+ 'role' : 'user' ,
165+ 'content' : {
166+ 'type' : 'audio' ,
167+ 'data' : 'fake-data' ,
168+ 'mimeType' : 'audio' ,
169+ },
170+ },
171+ ],
172+ 'maxTokens' : 256 ,
173+ },
174+ );
175+ expect (extractResponse (response), ['TOKENS: 256' , '[user] audio' ]);
176+ });
177+
178+ test ('can make a sampling request with an embedded resource' , () async {
179+ final dtdClient = testHarness.fakeEditorExtension.dtd;
180+ final response = await dtdClient.call (
181+ McpServiceConstants .serviceName,
182+ McpServiceConstants .samplingRequest,
183+ params: {
184+ 'messages' : [
185+ {
186+ 'role' : 'user' ,
187+ 'content' : {
188+ 'type' : 'resource' ,
189+ 'resource' : {'uri' : 'www.google.com' , 'text' : 'Google' },
190+ },
191+ },
192+ ],
193+ 'maxTokens' : 256 ,
194+ },
195+ );
196+ expect (extractResponse (response), [
197+ 'TOKENS: 256' ,
198+ '[user] www.google.com' ,
199+ ]);
200+ });
201+
202+ test ('can make a sampling request with mixed content' , () async {
203+ final dtdClient = testHarness.fakeEditorExtension.dtd;
204+ final response = await dtdClient.call (
205+ McpServiceConstants .serviceName,
206+ McpServiceConstants .samplingRequest,
207+ params: {
208+ 'messages' : [
209+ {
210+ 'role' : 'user' ,
211+ 'content' : {'type' : 'text' , 'text' : 'hello world' },
212+ },
213+ {
214+ 'role' : 'user' ,
215+ 'content' : {
216+ 'type' : 'image' ,
217+ 'data' : 'fake-data' ,
218+ 'mimeType' : 'image/jpeg' ,
219+ },
220+ },
221+ ],
222+ 'maxTokens' : 128 ,
223+ },
224+ );
225+ expect (extractResponse (response), [
226+ 'TOKENS: 128' ,
227+ '[user] hello world' ,
228+ '[user] image/jpeg' ,
229+ ]);
230+ });
231+
232+ test ('can handle user and assistant messages' , () async {
233+ final dtdClient = testHarness.fakeEditorExtension.dtd;
234+ final response = await dtdClient.call (
235+ McpServiceConstants .serviceName,
236+ McpServiceConstants .samplingRequest,
237+ params: {
238+ 'messages' : [
239+ {
240+ 'role' : 'user' ,
241+ 'content' : {'type' : 'text' , 'text' : 'Hi! I have a question.' },
242+ },
243+ {
244+ 'role' : 'assistant' ,
245+ 'content' : {'type' : 'text' , 'text' : 'What is your question?' },
246+ },
247+ {
248+ 'role' : 'user' ,
249+ 'content' : {'type' : 'text' , 'text' : 'How big is the sun?' },
250+ },
251+ ],
252+ 'maxTokens' : 512 ,
253+ },
254+ );
255+ expect (extractResponse (response), [
256+ 'TOKENS: 512' ,
257+ '[user] Hi! I have a question.' ,
258+ '[assistant] What is your question?' ,
259+ '[user] How big is the sun?' ,
260+ ]);
261+ });
262+
263+ test ('forwards all messages, even those with unknown types' , () async {
264+ final dtdClient = testHarness.fakeEditorExtension.dtd;
265+ final response = await dtdClient.call (
266+ McpServiceConstants .serviceName,
267+ McpServiceConstants .samplingRequest,
268+ params: {
269+ 'messages' : [
270+ {
271+ 'role' : 'user' ,
272+ 'content' : {
273+ // Not of type text, image, audio, or resource.
274+ 'type' : 'unknown' ,
275+ 'text' : 'Hi there!' ,
276+ 'data' : 'Hi there!' ,
277+ },
278+ },
279+ ],
280+ 'maxTokens' : 512 ,
281+ },
282+ );
283+ expect (extractResponse (response), ['TOKENS: 512' , 'UNKNOWN' ]);
284+ });
285+
286+ test ('throws for invalid requests' , () async {
287+ final dtdClient = testHarness.fakeEditorExtension.dtd;
288+ try {
289+ await dtdClient.call (
290+ McpServiceConstants .serviceName,
291+ McpServiceConstants .samplingRequest,
292+ params: {
293+ 'messages' : [
294+ {
295+ 'role' : 'dog' , // Invalid role.
296+ 'content' : {
297+ 'type' : 'text' ,
298+ 'text' : 'Hi! I have a question.' ,
299+ },
300+ },
301+ ],
302+ 'maxTokens' : 512 ,
303+ },
304+ );
305+ fail ('Expected an RpcException to be thrown.' );
306+ } catch (e) {
307+ expect (e, isA <RpcException >());
308+ }
309+ });
310+ });
311+
101312 group ('dart cli tests' , () {
102313 test ('can perform a hot reload' , () async {
103314 final exampleApp = await Directory .systemTemp.createTemp ('dart_app' );
0 commit comments