Skip to content

Commit c1a944d

Browse files
authored
feat(mcp_dart_cli): enhance CLI with serve command, docs, and tests (#55)
* feat(mcp_dart_cli): enhance CLI with serve command, docs, and tests * fix(mcp_dart): correct StdioClientTransport stderr handling (v1.1.2) - Always use ProcessStartMode.normal for proper stdin/stdout piping - Fix inverted stderr mode logic: 'normal' now exposes stderr via getter without internal listening, 'inheritStdio' now pipes to parent stderr - Update version to 1.1.2 * feat(mcp_dart_cli): add doctor and inspect commands (v0.1.2) - Add 'doctor' command to check project configuration and test connectivity - Add 'inspect' command to interact with MCP servers: - List capabilities (tools, resources, prompts) - Execute tools, read resources, get prompts - Support local projects and external servers (stdio/HTTP) - Support sampling/createMessage requests from servers - Add utility classes: McpConnection, InspectPrinter, InspectHandlers - Update documentation with command usage examples - Update version to 0.1.2 * docs(templates): add mcp_dart serve option to simple template README * ci: unify publish workflows with auto version detection - Merge publish_core.yml and publish_cli.yml into single publish.yml - Add workflow_dispatch trigger with package selection dropdown - Auto-read version from pubspec.yaml (no manual input needed) - Add tag existence check to fail early on duplicate releases - Add dry_run option to test without publishing to pub.dev - Auto-generate GitHub releases with release notes * docs: update README with CLI quick start guide - Replace inline code example with CLI-based quick start - Add CLI commands table (create, serve, doctor, inspect) - Show Claude Desktop configuration example * fix pana
1 parent 8219c63 commit c1a944d

36 files changed

Lines changed: 1968 additions & 128 deletions

.github/workflows/publish.yml

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: Publish to pub.dev
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
package:
7+
description: 'Package to publish'
8+
required: true
9+
type: choice
10+
options:
11+
- mcp_dart
12+
- mcp_dart_cli
13+
dry_run:
14+
description: 'Dry run (create release but skip pub.dev publish)'
15+
required: false
16+
type: boolean
17+
default: false
18+
19+
jobs:
20+
create-release:
21+
runs-on: ubuntu-latest
22+
permissions:
23+
contents: write # Required to create tags and releases
24+
outputs:
25+
tag: ${{ steps.create-tag.outputs.tag }}
26+
version: ${{ steps.get-version.outputs.version }}
27+
working_directory: ${{ steps.set-config.outputs.working_directory }}
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
- name: Set package configuration
32+
id: set-config
33+
run: |
34+
if [ "${{ inputs.package }}" = "mcp_dart" ]; then
35+
echo "working_directory=." >> "$GITHUB_OUTPUT"
36+
echo "tag_prefix=v" >> "$GITHUB_OUTPUT"
37+
else
38+
echo "working_directory=packages/mcp_dart_cli" >> "$GITHUB_OUTPUT"
39+
echo "tag_prefix=mcp_dart_cli-v" >> "$GITHUB_OUTPUT"
40+
fi
41+
42+
- name: Get version from pubspec.yaml
43+
id: get-version
44+
working-directory: ${{ steps.set-config.outputs.working_directory }}
45+
run: |
46+
VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //')
47+
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
48+
echo "📦 Package: ${{ inputs.package }}"
49+
echo "📦 Detected version: $VERSION"
50+
51+
- name: Check if tag already exists
52+
run: |
53+
TAG="${{ steps.set-config.outputs.tag_prefix }}${{ steps.get-version.outputs.version }}"
54+
if git rev-parse "$TAG" >/dev/null 2>&1; then
55+
echo "❌ Error: Tag $TAG already exists!"
56+
echo " This version has already been released."
57+
echo " Please bump the version in pubspec.yaml first."
58+
exit 1
59+
fi
60+
echo "✅ Tag $TAG does not exist, proceeding..."
61+
62+
- name: Create and push tag
63+
id: create-tag
64+
run: |
65+
TAG="${{ steps.set-config.outputs.tag_prefix }}${{ steps.get-version.outputs.version }}"
66+
git config user.name "github-actions[bot]"
67+
git config user.email "github-actions[bot]@users.noreply.github.com"
68+
git tag -a "$TAG" -m "Release ${{ inputs.package }} ${{ steps.get-version.outputs.version }}"
69+
git push origin "$TAG"
70+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
71+
echo "🏷️ Created and pushed tag: $TAG"
72+
73+
- name: Create GitHub Release
74+
uses: softprops/action-gh-release@v2
75+
with:
76+
tag_name: ${{ steps.create-tag.outputs.tag }}
77+
name: ${{ inputs.package }} ${{ steps.get-version.outputs.version }}
78+
generate_release_notes: true
79+
draft: false
80+
81+
publish:
82+
needs: create-release
83+
if: ${{ !inputs.dry_run }}
84+
permissions:
85+
id-token: write # Required for authentication using OIDC
86+
uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1
87+
with:
88+
working-directory: ${{ needs.create-release.outputs.working_directory }}

.github/workflows/publish_cli.yml

Lines changed: 0 additions & 14 deletions
This file was deleted.

.github/workflows/publish_core.yml

Lines changed: 0 additions & 14 deletions
This file was deleted.

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 1.1.2
2+
3+
- **Fixed StdioClientTransport stderr handling**: Corrected process mode to always use `ProcessStartMode.normal` to ensure stdin/stdout piping works correctly. Fixed inverted stderr mode logic where `stderrMode: normal` now properly exposes stderr via getter (without internal listening), and `stderrMode: inheritStdio` now manually pipes stderr to parent process.
4+
15
## 1.1.1
26

37
- **Structured Content Support**: Added explicit `structuredContent` field to `CallToolResult` with automatic backward compatibility support.

README.md

Lines changed: 34 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -75,95 +75,59 @@ It's also backward compatible with previous versions including `2025-06-18`, `20
7575
- 🔐 **[OAuth Authentication](https://github.com/leehack/mcp_dart/blob/main/example/authentication/)** - OAuth2 guides and examples
7676
- 📝 For resources, prompts, and other features, see the Server and Client guides
7777

78-
## Quick Start Example
79-
80-
Below is the simplest way to create an MCP server:
81-
82-
```dart
83-
import 'package:mcp_dart/mcp_dart.dart';
84-
85-
void main() async {
86-
McpServer server = McpServer(
87-
Implementation(name: "mcp-example-server", version: "1.0.0"),
88-
options: ServerOptions(
89-
capabilities: ServerCapabilities(
90-
resources: ServerCapabilitiesResources(),
91-
tools: ServerCapabilitiesTools(),
92-
),
93-
),
94-
);
95-
96-
server.registerTool(
97-
"calculate",
98-
description: 'Perform basic arithmetic operations',
99-
inputSchema: ToolInputSchema(
100-
properties: {
101-
'operation': JsonSchema.string(
102-
enumValues: ['add', 'subtract', 'multiply', 'divide'],
103-
),
104-
'a': JsonSchema.number(),
105-
'b': JsonSchema.number(),
106-
},
107-
required: ['operation', 'a', 'b'],
108-
),
109-
callback: (args, extra) async {
110-
final operation = args['operation'];
111-
final a = args['a'];
112-
final b = args['b'];
113-
return CallToolResult.fromContent(
114-
content: [
115-
TextContent(
116-
text: switch (operation) {
117-
'add' => 'Result: ${a + b}',
118-
'subtract' => 'Result: ${a - b}',
119-
'multiply' => 'Result: ${a * b}',
120-
'divide' => 'Result: ${a / b}',
121-
_ => throw Exception('Invalid operation'),
122-
},
123-
),
124-
],
125-
);
126-
},
127-
);
128-
129-
server.connect(StdioServerTransport());
130-
}
131-
```
132-
133-
### Running Your Server
78+
## Quick Start with CLI
13479

135-
Compile your MCP server to an executable:
80+
The fastest way to create an MCP server is using the `mcp_dart_cli`:
13681

13782
```bash
138-
dart compile exe example/server_stdio.dart -o ./server_stdio
83+
# Install the CLI
84+
dart pub global activate mcp_dart_cli
85+
86+
# Create a new project
87+
mcp_dart create my_server
88+
89+
# Navigate and run
90+
cd my_server
91+
mcp_dart serve
13992
```
14093

141-
Or run it directly with JIT:
94+
Your server is now running! Use `mcp_dart inspect` to test it:
14295

14396
```bash
144-
dart run example/server_stdio.dart
97+
mcp_dart inspect # List all capabilities
98+
mcp_dart inspect --tool add --json-args '{"a": 1, "b": 2}' # Call a tool
14599
```
146100

101+
### CLI Commands
102+
103+
| Command | Description |
104+
|---------|-------------|
105+
| `create` | Scaffold a new MCP server project |
106+
| `serve` | Run your server (stdio or HTTP) |
107+
| `doctor` | Check project health and connectivity |
108+
| `inspect` | Test and debug server capabilities |
109+
110+
📖 **[Full CLI Documentation](https://github.com/leehack/mcp_dart/tree/main/packages/mcp_dart_cli)**
111+
147112
### Connecting to AI Hosts
148113

149-
To configure your server with AI hosts like Claude Desktop:
114+
Configure your server with AI hosts like Claude Desktop:
150115

151116
```json
152117
{
153118
"mcpServers": {
154-
"calculator_jit": {
155-
"command": "path/to/dart",
156-
"args": [
157-
"/path/to/server_stdio.dart"
158-
]
159-
},
160-
"calculator_aot": {
161-
"command": "path/to/compiled/server_stdio",
162-
},
119+
"my_server": {
120+
"command": "mcp_dart",
121+
"args": ["serve"],
122+
"cwd": "/path/to/my_server"
123+
}
163124
}
164125
}
165126
```
166127

128+
> [!TIP]
129+
> For manual server implementation or advanced use cases, see the [Server Guide](https://github.com/leehack/mcp_dart/blob/main/doc/server-guide.md).
130+
167131
## Authentication
168132

169133
This library supports OAuth2 authentication with PKCE for both clients and servers. For complete authentication guides and examples, see the [OAuth Authentication documentation](https://github.com/leehack/mcp_dart/blob/main/example/authentication/).

doc/getting-started.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Add the MCP Dart SDK to your `pubspec.yaml`:
88

99
```yaml
1010
dependencies:
11-
mcp_dart: ^1.0.0
11+
mcp_dart: ^1.1.2
1212
```
1313
1414
Then run:

doc/quick-reference.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Fast lookup guide for common MCP Dart SDK operations.
77
```yaml
88
# pubspec.yaml
99
dependencies:
10-
mcp_dart: ^1.0.0
10+
mcp_dart: ^1.1.2
1111
```
1212
1313
```bash

lib/src/client/stdio.dart

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import 'dart:async';
2-
import 'dart:convert'; // For utf8 encoding if needed
2+
33
import 'dart:io' as io; // Use 'io' prefix
44
import 'dart:typed_data'; // For Uint8List
55

@@ -113,21 +113,15 @@ class StdioClientTransport implements Transport {
113113
);
114114
}
115115
_started = true;
116-
117-
final mode = (_serverParams.stderrMode == io.ProcessStartMode.normal)
118-
? io.ProcessStartMode.normal // Use normal for pipe access
119-
: io.ProcessStartMode.inheritStdio; // More direct inheritance
120-
121116
try {
122117
// Start the process.
123118
_process = await io.Process.start(
124119
_serverParams.command,
125120
_serverParams.args,
126121
workingDirectory: _serverParams.workingDirectory,
127-
environment:
128-
_serverParams.environment, // Use provided or inherit Dart default
129-
runInShell: false, // Generally safer
130-
mode: mode, // Handles stdin/stdout/stderr piping/inheritance
122+
environment: _serverParams.environment,
123+
runInShell: false,
124+
mode: io.ProcessStartMode.normal, // Always use normal to enable piping
131125
);
132126

133127
_logger.debug(
@@ -147,12 +141,12 @@ class StdioClientTransport implements Transport {
147141
// Listen to stderr if piped
148142
if (_serverParams.stderrMode == io.ProcessStartMode.normal) {
149143
// Expose stderr via getter, let user handle it.
150-
// Optionally add logging here:
144+
// Do NOT listen here, as that would prevent the user from listening.
145+
} else {
146+
// Inherit stderr (manually pipe to parent stderr)
151147
_stderrSubscription = _process!.stderr.listen(
152-
(data) => _logger.debug(
153-
"Server stderr: ${utf8.decode(data, allowMalformed: true)}",
154-
),
155-
onError: _onStreamError, // Report stderr stream errors too
148+
(data) => io.stderr.add(data),
149+
onError: _onStreamError,
156150
);
157151
}
158152

llms.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
MCP Dart is the official Dart implementation of the Model Context Protocol, enabling seamless integration between AI applications and external data sources, tools, and services. Build production-ready MCP servers and clients for Dart VM, Flutter, and web platforms.
88

9-
**Version**: 1.1.0
9+
**Version**: 1.1.2
1010
**Protocol**: MCP 2025-06-18 (backward compatible with 2025-03-26, 2024-11-05, 2024-10-07)
1111
**Repository**: https://github.com/leehack/mcp_dart
1212
**Package**: https://pub.dev/packages/mcp_dart
@@ -18,7 +18,7 @@ Add to your `pubspec.yaml`:
1818

1919
```yaml
2020
dependencies:
21-
mcp_dart: ^1.1.0
21+
mcp_dart: ^1.1.2
2222
```
2323

2424
Then run:

packages/mcp_dart_cli/CHANGELOG.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,25 @@
1+
## 0.1.2
2+
3+
- **`serve` command** for running MCP servers:
4+
- Supports stdio and HTTP transport (`--transport http`)
5+
- `--watch` flag for automatic server restart on file changes
6+
7+
- **`doctor` command** for checking project configuration:
8+
- Dynamic verification that starts the server and tests all tools, resources, and prompts
9+
- Detailed status output for each check
10+
11+
- **`inspect` command** for interacting with MCP servers:
12+
- `--url` flag for connecting via Streamable HTTP
13+
- `--wait` flag to wait for server notifications
14+
- `--resource` and `--prompt` flags for reading resources and prompts
15+
- `sampling/createMessage` request handler for LLM-based tools
16+
- Detailed tool schema information in capabilities listing
17+
118
## 0.1.1
219

3-
- Add gha for mcp_dart_cli
20+
- Add GitHub Actions workflows for mcp_dart_cli
421

522
## 0.1.0
623

724
- Initial release of the `mcp_dart_cli` package.
8-
- Supports creating new MCP servers with `mcp_dart create`.
25+
- `create` command for creating new MCP servers from templates.

0 commit comments

Comments
 (0)