강좌를 시작한다. 여섯 편에 걸쳐 서버 하나를 키운다. 매 편이 한 가지를 더하고, 각 단계는 그 자체로 돌아가는 완성품이다.
첫 편에서 만드는 건 아무것도 안 하는 서버다. 도구도 없고 리소스도 없다. 그런데 이 단계가 없으면 뒤가 전부 무너진다.
전부다
import 'dart:async';
import 'dart:io';
import 'package:mcp_server/mcp_server.dart';
void main(List<String> args) async {
const config = McpServerConfig(
name: 'Course',
version: '1.0.0',
capabilities: ServerCapabilities(),
);
final server = McpServer.createServer(config);
final transport = McpServer.createStdioTransport().get();
server.connect(transport);
stderr.writeln('course step1: up, nothing registered');
await Completer<void>().future;
}
ServerCapabilities() 가 비어 있다. 아직 내놓을 게 없다고 말하는 것이다. 다음 편부터 여기가 채워진다.
마지막 줄의 Completer<void>().future 는 영원히 완료되지 않는다. main 이 끝나면 프로세스가 죽으니, 죽지 않게 붙잡아 두는 것이다.
붙어서 물어본다
클라이언트 없이 손으로 찔러 볼 수 있다. stdio 로 도는 서버라 그냥 줄을 밀어 넣으면 된다.
{ printf '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}}\n'
sleep 2; } | dart run bin/step1.dart
돌아온 것.
{"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-06-18","serverInfo":{"name":"Course","version":"1.0.0"},"capabilities":{}}}
serverInfo 에 우리가 적은 이름이 그대로 있다. 이게 첫 편에서 확인할 전부다. 서버가 살아 있고, 규약대로 자기를 소개한다.