Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions lib/mcp_dart.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ library;
// Common exports for all platforms
export 'src/types.dart'; // Exports shared types used across the MCP protocol.
export 'src/shared/uuid.dart'; // Exports UUID generation utilities.
export 'src/shared/logging.dart'; // Exports logging for customization

// Platform-specific exports
export 'src/exports.dart' // Stub export for other platforms
Expand Down
19 changes: 11 additions & 8 deletions lib/src/client/client.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'dart:async';
import 'package:mcp_dart/src/shared/logging.dart';
import 'package:mcp_dart/src/shared/protocol.dart';
import 'package:mcp_dart/src/shared/transport.dart';
import 'package:mcp_dart/src/types.dart';

final _logger = Logger("mcp_dart.client");

/// Options for configuring the MCP [Client].
class ClientOptions extends ProtocolOptions {
/// Capabilities to advertise as being supported by this client.
Expand Down Expand Up @@ -88,11 +91,11 @@ class Client extends Protocol {
const initializedNotification = JsonRpcInitializedNotification();
await notification(initializedNotification);

print(
_logger.debug(
"MCP Client Initialized. Server: ${result.serverInfo.name} ${result.serverInfo.version}, Protocol: ${result.protocolVersion}",
);
} catch (error) {
print("MCP Client Initialization Failed: $error");
_logger.error("MCP Client Initialization Failed: $error");
await close();
rethrow;
}
Expand Down Expand Up @@ -150,8 +153,8 @@ class Client extends Protocol {
requiredCapability = 'prompts or resources';
break;
default:
print(
"Warning: assertCapabilityForMethod called for potentially custom client request: $method",
_logger.warn(
"assertCapabilityForMethod called for potentially custom client request: $method",
);
supported = true;
}
Expand All @@ -175,8 +178,8 @@ class Client extends Protocol {
}
break;
default:
print(
"Warning: assertNotificationCapability called for potentially custom client notification: $method",
_logger.warn(
"assertNotificationCapability called for potentially custom client notification: $method",
);
}
}
Expand All @@ -199,8 +202,8 @@ class Client extends Protocol {
}
break;
default:
print(
"Info: Setting request handler for potentially custom method '$method'. Ensure client capabilities match.",
_logger.info(
"Setting request handler for potentially custom method '$method'. Ensure client capabilities match.",
);
}
}
Expand Down
53 changes: 31 additions & 22 deletions lib/src/client/stdio.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import 'package:mcp_dart/src/shared/stdio.dart'; // Adjust import path as needed
import 'package:mcp_dart/src/shared/transport.dart'; // Adjust import path as needed
// Assume types are defined in types.dart
import 'package:mcp_dart/src/types.dart'; // Adjust import path as needed
import 'package:mcp_dart/src/shared/logging.dart';

final _logger = Logger("mcp_dart.client.stdio");

/// Configuration parameters for launching the stdio server process.
class StdioServerParameters {
Expand Down Expand Up @@ -127,7 +130,9 @@ class StdioClientTransport implements Transport {
mode: mode, // Handles stdin/stdout/stderr piping/inheritance
);

print("StdioClientTransport: Process started (PID: ${_process?.pid})");
_logger.debug(
"StdioClientTransport: Process started (PID: ${_process?.pid})",
);

// --- Setup stream listeners ---

Expand All @@ -144,7 +149,7 @@ class StdioClientTransport implements Transport {
// Expose stderr via getter, let user handle it.
// Optionally add logging here:
_stderrSubscription = _process!.stderr.listen(
(data) => print(
(data) => _logger.debug(
"Server stderr: ${utf8.decode(data, allowMalformed: true)}",
),
onError: _onStreamError, // Report stderr stream errors too
Expand All @@ -158,15 +163,15 @@ class StdioClientTransport implements Transport {
return Future.value();
} catch (error, stackTrace) {
// Handle errors during Process.start()
print("StdioClientTransport: Failed to start process: $error");
_logger.error("StdioClientTransport: Failed to start process: $error");
_started = false; // Reset state
final startError = StateError(
"Failed to start server process: $error\n$stackTrace",
);
try {
onerror?.call(startError);
} catch (e) {
print("Error in onerror handler: $e");
_logger.warn("Error in onerror handler: $e");
}
throw startError; // Rethrow to signal failure
}
Expand All @@ -193,7 +198,7 @@ class StdioClientTransport implements Transport {

/// Internal handler for when the process's stdout stream closes.
void _onStdoutDone() {
print("StdioClientTransport: Process stdout closed.");
_logger.debug("StdioClientTransport: Process stdout closed.");
// Consider if this should trigger close() - depends if server exiting is expected.
// Maybe only close if the process has also exited?
// close(); // Optionally close transport when stdout ends
Expand All @@ -207,7 +212,7 @@ class StdioClientTransport implements Transport {
try {
onerror?.call(streamError);
} catch (e) {
print("Error in onerror handler: $e");
_logger.warn("Error in onerror handler: $e");
}
// Consider if stream errors should trigger close()
// close();
Expand All @@ -222,7 +227,7 @@ class StdioClientTransport implements Transport {
try {
onmessage?.call(message);
} catch (e) {
print("Error in onmessage handler: $e");
_logger.warn("Error in onmessage handler: $e");
onerror?.call(StateError("Error in onmessage handler: $e"));
}
} catch (error) {
Expand All @@ -232,9 +237,9 @@ class StdioClientTransport implements Transport {
try {
onerror?.call(parseError);
} catch (e) {
print("Error in onerror handler: $e");
_logger.warn("Error in onerror handler: $e");
}
print(
_logger.error(
"StdioClientTransport: Error processing read buffer: $parseError. Skipping data.",
);
// Consider clearing buffer or attempting recovery depending on error type.
Expand All @@ -247,7 +252,7 @@ class StdioClientTransport implements Transport {

/// Internal handler for when the process exits.
void _onProcessExit(int exitCode) {
print("StdioClientTransport: Process exited with code $exitCode.");
_logger.debug("StdioClientTransport: Process exited with code $exitCode.");
if (!_exitCompleter.isCompleted) {
_exitCompleter.complete(); // Signal exit if not already closing
}
Expand All @@ -256,14 +261,16 @@ class StdioClientTransport implements Transport {

/// Internal handler for errors retrieving the process exit code.
void _onProcessExitError(dynamic error, StackTrace stackTrace) {
print("StdioClientTransport: Error waiting for process exit: $error");
_logger.debug(
"StdioClientTransport: Error waiting for process exit: $error",
);
final Error exitError = (error is Error)
? error
: StateError("Process exit error: $error\n$stackTrace");
try {
onerror?.call(exitError);
} catch (e) {
print("Error in onerror handler: $e");
_logger.warn("Error in onerror handler: $e");
}
if (!_exitCompleter.isCompleted) {
_exitCompleter.completeError(exitError);
Expand All @@ -279,7 +286,7 @@ class StdioClientTransport implements Transport {
Future<void> close() async {
if (!_started) return; // Already closed or never started

print("StdioClientTransport: Closing transport...");
_logger.debug("StdioClientTransport: Closing transport...");

// Mark as closing immediately to prevent further sends/starts
_started = false;
Expand All @@ -297,13 +304,13 @@ class StdioClientTransport implements Transport {
_process = null; // Clear reference

if (processToKill != null) {
print(
_logger.debug(
"StdioClientTransport: Terminating process (PID: ${processToKill.pid})...",
);
// Attempt graceful termination first
bool killed = processToKill.kill(io.ProcessSignal.sigterm);
if (!killed) {
print(
_logger.debug(
"StdioClientTransport: Failed to send SIGTERM or process already exited.",
);
// If SIGTERM fails or wasn't sent (e.g., process already dead),
Expand All @@ -312,15 +319,15 @@ class StdioClientTransport implements Transport {
// Give a short grace period for SIGTERM before SIGKILL
try {
await _exitCompleter.future.timeout(const Duration(seconds: 2));
print("StdioClientTransport: Process terminated gracefully.");
_logger.debug("StdioClientTransport: Process terminated gracefully.");
} on TimeoutException {
print(
_logger.warn(
"StdioClientTransport: Process did not exit after SIGTERM, sending SIGKILL.",
);
processToKill.kill(io.ProcessSignal.sigkill); // Force kill
} catch (e) {
// Error waiting for exit after SIGTERM (might have exited quickly)
print(
_logger.error(
"StdioClientTransport: Error waiting for process exit after SIGTERM: $e",
);
}
Expand All @@ -336,9 +343,9 @@ class StdioClientTransport implements Transport {
try {
onclose?.call();
} catch (e) {
print("Error in onclose handler: $e");
_logger.warn("Error in onclose handler: $e");
}
print("StdioClientTransport: Transport closed.");
_logger.debug("StdioClientTransport: Transport closed.");
}

/// Sends a [JsonRpcMessage] to the server process via its stdin.
Expand All @@ -361,14 +368,16 @@ class StdioClientTransport implements Transport {
// Flushing stdin might be necessary depending on the server's reading behavior.
await currentProcess.stdin.flush();
} catch (error, stackTrace) {
print("StdioClientTransport: Error writing to process stdin: $error");
_logger.warn(
"StdioClientTransport: Error writing to process stdin: $error",
);
final Error sendError = (error is Error)
? error
: StateError("Process stdin write error: $error\n$stackTrace");
try {
onerror?.call(sendError);
} catch (e) {
print("Error in onerror handler: $e");
_logger.warn("Error in onerror handler: $e");
}
// Consider closing the transport on stdin write failure
close();
Expand Down
13 changes: 8 additions & 5 deletions lib/src/server/mcp.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import 'dart:async';

import 'package:mcp_dart/src/shared/logging.dart';
import 'package:mcp_dart/src/shared/protocol.dart';
import 'package:mcp_dart/src/shared/transport.dart';
import 'package:mcp_dart/src/shared/uri_template.dart';
import 'package:mcp_dart/src/types.dart';

import 'server.dart';

final _logger = Logger("mcp_dart.server.mcp");

typedef CompleteCallback = Future<List<String>> Function(String value);

class CompletableDef {
Expand Down Expand Up @@ -244,7 +247,7 @@ class McpServer {
registeredTool.callback(args: toolArgs, extra: extra),
);
} catch (error) {
print("Error executing tool '$toolName': $error");
_logger.warn("Error executing tool '$toolName': $error");
return CallToolResult.fromContent(
content: [TextContent(text: error.toString())],
isError: true,
Expand Down Expand Up @@ -295,7 +298,7 @@ class McpServer {
try {
return _createCompletionResult(await completer(argInfo.value));
} catch (e) {
print(
_logger.warn(
"Error during prompt argument completion for '${ref.name}.${argInfo.name}': $e",
);
throw McpError(ErrorCode.internalError.value, "Completion failed");
Expand All @@ -319,7 +322,7 @@ class McpServer {
try {
return _createCompletionResult(await completer(argInfo.value));
} catch (e) {
print(
_logger.warn(
"Error during resource template completion for '${ref.uri}' variable '${argInfo.name}': $e",
);
throw McpError(ErrorCode.internalError.value, "Completion failed");
Expand Down Expand Up @@ -359,7 +362,7 @@ class McpServer {
)
.toList();
} catch (e) {
print("Error listing resources for template: $e");
_logger.warn("Error listing resources for template: $e");
return <Resource>[];
}
});
Expand Down Expand Up @@ -475,7 +478,7 @@ class McpServer {
throw StateError("No callback found");
}
} catch (error) {
print("Error executing prompt '$name': $error");
_logger.warn("Error executing prompt '$name': $error");
if (error is McpError) rethrow;
throw McpError(
ErrorCode.internalError.value,
Expand Down
15 changes: 9 additions & 6 deletions lib/src/server/server.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'dart:async';

import 'package:mcp_dart/src/shared/logging.dart';
import 'package:mcp_dart/src/shared/protocol.dart';
import 'package:mcp_dart/src/types.dart';

final _logger = Logger("mcp_dart.server");

/// Options for configuring the MCP [Server].
class ServerOptions extends ProtocolOptions {
/// Capabilities to advertise as being supported by this server.
Expand Down Expand Up @@ -129,8 +132,8 @@ class Server extends Protocol {
break;

default:
print(
"Warning: assertCapabilityForMethod called for unknown server-sent request method: $method",
_logger.warn(
"assertCapabilityForMethod called for unknown server-sent request method: $method",
);
}
}
Expand Down Expand Up @@ -183,8 +186,8 @@ class Server extends Protocol {
break;

default:
print(
"Warning: assertNotificationCapability called for unknown server-sent notification method: $method",
_logger.warn(
"assertNotificationCapability called for unknown server-sent notification method: $method",
);
}
}
Expand Down Expand Up @@ -243,8 +246,8 @@ class Server extends Protocol {
break;

default:
print(
"Info: Setting request handler for potentially custom method '$method'. Ensure server capabilities match.",
_logger.info(
"Setting request handler for potentially custom method '$method'. Ensure server capabilities match.",
);
}
}
Expand Down
Loading