Skip to content

A 'pub-dev-search' mcp tool #103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 59 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
cf1b4b4
A pub search functionality
sigurdm May 2, 2025
65de883
Update pkgs/dart_tooling_mcp_server/lib/src/mixins/pub_dev_search.dart
sigurdm May 5, 2025
dbcabff
Update pkgs/dart_tooling_mcp_server/lib/src/mixins/pub_dev_search.dart
sigurdm May 5, 2025
ff7cc43
Detailed search term description
sigurdm May 5, 2025
a383fb5
Rename argument `search-query` -> `query`
sigurdm May 5, 2025
0fe6f8c
Remove unused import
sigurdm May 5, 2025
5426861
Underscores in tool name
sigurdm May 5, 2025
245d790
Readonlyhint: true
sigurdm May 5, 2025
8f5e9cb
Remove copy-paste waste
sigurdm May 5, 2025
ce713a1
Extract `dig` json utility
sigurdm May 5, 2025
cd3ba90
Remove unused query parameters
sigurdm May 5, 2025
f9219b4
Make client mockable
sigurdm May 5, 2025
f1f9f2c
Add test
sigurdm May 5, 2025
2b28b8d
Better golden file error message
sigurdm May 5, 2025
084cfe7
Privatify name
sigurdm May 5, 2025
c803a7b
Test failure
sigurdm May 5, 2025
e0afe2d
Run subqueries in parallel
sigurdm May 5, 2025
29888c8
Test and fix json dig
sigurdm May 5, 2025
7442c9c
Make pool top-level static
sigurdm May 5, 2025
9070d8a
Update pkgs/dart_tooling_mcp_server/test_fixtures/pub_dev_responses/R…
sigurdm May 5, 2025
b46121c
Update pkgs/dart_tooling_mcp_server/test_fixtures/pub_dev_responses/R…
sigurdm May 5, 2025
9e1283a
Update pkgs/dart_tooling_mcp_server/lib/src/utils/json.dart
sigurdm May 6, 2025
ab08279
json util, handle specified types
sigurdm May 6, 2025
77ccf09
Update pkgs/dart_tooling_mcp_server/lib/src/mixins/pub_dev_search.dart
sigurdm May 6, 2025
95da176
Handle generic types
sigurdm May 6, 2025
7a4c89c
Remove nonsense
sigurdm May 6, 2025
525ea44
Bump subosito/flutter-action from 1 to 2 in the github-actions group …
dependabot[bot] May 1, 2025
2093225
Making `instructions` optional. (#98)
domesticmouse May 1, 2025
b10e03c
Require roots for all CLI tools (#101)
jakemac53 May 2, 2025
29cd5b2
Add runtime errors resource and tool to clear errors. (#94)
jakemac53 May 2, 2025
adf7ee9
add option to log protocol messages to a Sink<String> (#102)
jakemac53 May 2, 2025
fed9134
Modify DTD connection verbiage in example client (#104)
kenzieschmoll May 2, 2025
4871a32
add test for server closing early to validate behavior (#105)
jakemac53 May 2, 2025
06c71c7
Continue making `instructions` optional. (#107)
domesticmouse May 4, 2025
23aa993
Release dart_mcp version 0.2.0 (#106)
jakemac53 May 5, 2025
009219c
Add supported tools to the MCP server README (#109)
kenzieschmoll May 5, 2025
346d7c4
add signature_help tool (#110)
jakemac53 May 5, 2025
f5c83bd
show thinking text and input/output token usage (#108)
jakemac53 May 5, 2025
cc41bfb
Tolerate missing sub-responses
sigurdm May 6, 2025
e483a8c
Report error when no packages found
sigurdm May 6, 2025
472d00b
merge
sigurdm May 6, 2025
b30e7f9
dartfmt
sigurdm May 6, 2025
10c723d
Extract result count constant
sigurdm May 9, 2025
1f06c85
End with period
sigurdm May 9, 2025
0745135
Use named records
sigurdm May 9, 2025
152457d
End with period
sigurdm May 9, 2025
524fac2
Break up lines
sigurdm May 9, 2025
b9d78aa
Extract test helper
sigurdm May 9, 2025
c3b41d8
Explain no 'or' operator in tool description
sigurdm May 12, 2025
d36f0f5
Merge
sigurdm May 12, 2025
5636292
Fix merge
sigurdm May 12, 2025
d0cdb29
Sort dependencies
sigurdm May 12, 2025
1e4524e
Address some of review
sigurdm May 13, 2025
e7f3935
Fix rename
sigurdm May 13, 2025
e3e187e
Refactor to use `runWithClient`
sigurdm May 13, 2025
9e6e34b
dynamic -> Object
sigurdm May 13, 2025
b808801
Use list of strings instead of list of objects
sigurdm May 16, 2025
6c98900
Restrict max number of listed identifiers
sigurdm May 16, 2025
a9c13b9
Report the publisher of the package
sigurdm May 16, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
show thinking text and input/output token usage (#108)
Makes the bot more interactive and also gives us a sense of total input/output token usage [Screen recording 2025-05-05 9.24.57 AM.webm](https://github.com/user-attachments/assets/993729bf-889a-485f-b295-9be52ca0e242)

Also makes a small fix to the tooling server so it is compatible again (it had lost compatibility due to a recent change which added a list schema with no items schema, which seems to be required for gemini tools).
  • Loading branch information
jakemac53 authored and sigurdm committed May 6, 2025
commit f5c83bdda3041ec9b9a5485afbacd51bbfe2ec20
5 changes: 5 additions & 0 deletions pkgs/dart_mcp/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.2.1-wip

- Update workflow example to show thinking spinner and input and output token
usage.

## 0.2.0

- Support protocol version 2025-03-26.
Expand Down
103 changes: 69 additions & 34 deletions pkgs/dart_mcp/example/workflow_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'dart:io';

import 'package:args/args.dart';
import 'package:async/async.dart';
import 'package:cli_util/cli_logging.dart';
import 'package:dart_mcp/client.dart';
import 'package:google_generative_ai/google_generative_ai.dart' as gemini;

Expand All @@ -23,6 +24,7 @@ void main(List<String> args) {

final parsedArgs = argParser.parse(args);
final serverCommands = parsedArgs['server'] as List<String>;
final logger = Logger.standard();
runZonedGuarded(
() {
WorkflowClient(
Expand All @@ -31,10 +33,11 @@ void main(List<String> args) {
verbose: parsedArgs.flag('verbose'),
dtdUri: parsedArgs.option('dtd'),
persona: parsedArgs.flag('dash') ? _dashPersona : null,
logger: logger,
);
},
(e, s) {
stderr.writeln('$e\n$s');
logger.stderr('$e\n$s');
},
);
}
Expand All @@ -58,11 +61,16 @@ final argParser =
);

final class WorkflowClient extends MCPClient with RootsSupport {
final Logger logger;
int totalInputTokens = 0;
int totalOutputTokens = 0;

WorkflowClient(
this.serverCommands, {
required String geminiApiKey,
String? dtdUri,
this.verbose = false,
required this.logger,
String? persona,
}) : model = gemini.GenerativeModel(
model: 'gemini-2.5-pro-preview-03-25',
Expand Down Expand Up @@ -120,11 +128,10 @@ final class WorkflowClient extends MCPClient with RootsSupport {

// Introduce yourself.
_addToHistory('Please introduce yourself and explain how you can help.');
final introResponse =
(await model.generateContent(
chatHistory,
tools: serverTools,
)).candidates.single.content;
final introResponse = await _generateContent(
context: chatHistory,
tools: serverTools,
);
_handleModelResponse(introResponse);

while (true) {
Expand All @@ -139,7 +146,7 @@ final class WorkflowClient extends MCPClient with RootsSupport {
case gemini.TextPart():
_chatToUser(part.text);
default:
print('Unrecognized response type from the model $response');
logger.stderr('Unrecognized response type from the model $response');
}
}
}
Expand All @@ -159,11 +166,10 @@ final class WorkflowClient extends MCPClient with RootsSupport {
'plan.';
_addToHistory(planPrompt);

final planResponse =
(await model.generateContent(
chatHistory,
tools: serverTools,
)).candidates.single.content;
final planResponse = await _generateContent(
context: chatHistory,
tools: serverTools,
);
_handleModelResponse(planResponse);

final userResponse = await _waitForInputAndAddToHistory();
Expand All @@ -189,11 +195,10 @@ final class WorkflowClient extends MCPClient with RootsSupport {
final nextMessage = continuation ?? await stdinQueue.next;
continuation = null;
_addToHistory(nextMessage);
final modelResponse =
(await model.generateContent(
chatHistory,
tools: serverTools,
)).candidates.single.content;
final modelResponse = await _generateContent(
context: chatHistory,
tools: serverTools,
);

for (var part in modelResponse.parts) {
switch (part) {
Expand All @@ -212,7 +217,9 @@ final class WorkflowClient extends MCPClient with RootsSupport {
'$result\n. Please proceed to the next step of the plan.';

default:
print('Unrecognized response type from the model: $modelResponse.');
logger.stderr(
'Unrecognized response type from the model: $modelResponse.',
);
}
}
}
Expand All @@ -232,34 +239,62 @@ final class WorkflowClient extends MCPClient with RootsSupport {
/// previous action.
Future<bool> _analyzeSentiment(String message) async {
if (message == 'y' || message == 'yes') return true;
final sentimentResult =
(await model.generateContent([
gemini.Content.text(
'Analyze the sentiment of the following response. If the response '
'indicates a need for any changes, then this is not an approval. '
'If you are highly confident that the user approves of running the '
'previous action then respond with a single character "y".',
),
gemini.Content.text(message),
])).candidates.single.content;
final sentimentResult = await _generateContent(
context: [
gemini.Content.text(
'Analyze the sentiment of the following response. If the response '
'indicates a need for any changes, then this is not an approval. '
'If you are highly confident that the user approves of running the '
'previous action then respond with a single character "y".',
),
gemini.Content.text(message),
],
);
final response = StringBuffer();
for (var part in sentimentResult.parts.whereType<gemini.TextPart>()) {
response.write(part.text.trim());
}
return response.toString() == 'y';
}

Future<gemini.Content> _generateContent({
required Iterable<gemini.Content> context,
List<gemini.Tool>? tools,
}) async {
final progress = logger.progress('thinking');
gemini.GenerateContentResponse? response;
try {
response = await model.generateContent(context, tools: tools);
return response.candidates.single.content;
} finally {
if (response != null) {
final inputTokens = response.usageMetadata?.promptTokenCount;
final outputTokens = response.usageMetadata?.candidatesTokenCount;
totalInputTokens += inputTokens ?? 0;
totalOutputTokens += outputTokens ?? 0;
progress.finish(
message:
'(input token usage: $totalInputTokens (+$inputTokens), output '
'token usage: $totalOutputTokens (+$outputTokens))',
showTiming: true,
);
} else {
progress.finish(message: 'failed', showTiming: true);
}
}
}

/// Prints `text` and adds it to the chat history
void _chatToUser(String text) {
final content = gemini.Content.text(text);
final dashText = StringBuffer();
for (var part in content.parts.whereType<gemini.TextPart>()) {
dashText.write(part.text);
}
print('\n$dashText\n');
chatHistory.add(
gemini.Content.model([gemini.TextPart(dashText.toString())]),
);
logger.stdout('\n$dashText');
// Add the non-personalized text to the context as it might lose some
// useful info.
chatHistory.add(gemini.Content.model([gemini.TextPart(text)]));
}

/// Handles a function call response from the model.
Expand Down Expand Up @@ -310,7 +345,7 @@ final class WorkflowClient extends MCPClient with RootsSupport {
),
);
if (result.protocolVersion != ProtocolVersion.latestSupported) {
print(
logger.stderr(
'Protocol version mismatch, expected '
'${ProtocolVersion.latestSupported}, got ${result.protocolVersion}, '
'disconnecting from server',
Expand All @@ -336,7 +371,7 @@ final class WorkflowClient extends MCPClient with RootsSupport {
),
);
connection.onLog.listen((event) {
print(
logger.stdout(
'Server Log(${event.level.name}): '
'${event.logger != null ? '[${event.logger}] ' : ''}${event.data}',
);
Expand Down
3 changes: 2 additions & 1 deletion pkgs/dart_mcp/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: dart_mcp
version: 0.2.0
version: 0.2.1-wip
description: A package for making MCP servers and clients.
repository: https://github.com/dart-lang/ai/tree/main/pkgs/dart_mcp
issue_tracker: https://github.com/dart-lang/ai/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Adart_mcp
Expand All @@ -17,6 +17,7 @@ dependencies:

dev_dependencies:
args: ^2.7.0
cli_util: ^0.4.2
dart_flutter_team_lints: ^3.2.1
google_generative_ai: ^0.4.6
test: ^1.25.15