Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,157 @@
|
||||
module mcp
|
||||
|
||||
struct MockTransport {
|
||||
mut:
|
||||
incoming []string
|
||||
sent []string
|
||||
closed bool
|
||||
}
|
||||
|
||||
fn (mut transport MockTransport) send(message string) ! {
|
||||
transport.sent << message
|
||||
}
|
||||
|
||||
fn (mut transport MockTransport) receive() !string {
|
||||
if transport.incoming.len == 0 {
|
||||
return error('no messages queued in MockTransport')
|
||||
}
|
||||
message := transport.incoming[0]
|
||||
transport.incoming = if transport.incoming.len == 1 {
|
||||
[]string{}
|
||||
} else {
|
||||
transport.incoming[1..].clone()
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
fn (mut transport MockTransport) close() {
|
||||
transport.closed = true
|
||||
}
|
||||
|
||||
fn test_initialize_sends_the_mcp_handshake() {
|
||||
mut transport := &MockTransport{
|
||||
incoming: [
|
||||
new_response(1, InitializeResult{
|
||||
protocol_version: protocol_version
|
||||
capabilities: '{"tools":{}}'
|
||||
server_info: Implementation{
|
||||
name: 'mock-server'
|
||||
version: '1.0.0'
|
||||
}
|
||||
}, ResponseError{}).encode(),
|
||||
]
|
||||
}
|
||||
mut client := new_client(transport, ClientConfig{
|
||||
client_info: Implementation{
|
||||
name: 'mcp-test-client'
|
||||
version: '0.1.0'
|
||||
}
|
||||
capabilities: '{"roots":{"listChanged":true}}'
|
||||
})
|
||||
|
||||
result := client.initialize()!
|
||||
|
||||
assert result.server_info.name == 'mock-server'
|
||||
assert transport.sent.len == 2
|
||||
|
||||
request := decode_request(transport.sent[0])!
|
||||
params := request.decode_params[InitializeParams]()!
|
||||
assert request.method == 'initialize'
|
||||
assert params.protocol_version == protocol_version
|
||||
assert params.client_info.name == 'mcp-test-client'
|
||||
assert params.capabilities == '{"roots":{"listChanged":true}}'
|
||||
|
||||
notification := decode_notification(transport.sent[1])!
|
||||
assert notification.method == 'notifications/initialized'
|
||||
assert notification.params == ''
|
||||
}
|
||||
|
||||
fn test_request_buffers_server_messages_after_initialize() {
|
||||
mut transport := &MockTransport{
|
||||
incoming: [
|
||||
new_response(1, InitializeResult{
|
||||
protocol_version: protocol_version
|
||||
capabilities: '{"tools":{}}'
|
||||
server_info: Implementation{
|
||||
name: 'mock-server'
|
||||
version: '1.0.0'
|
||||
}
|
||||
}, ResponseError{}).encode(),
|
||||
new_notification('notifications/tools/list_changed', empty).encode(),
|
||||
new_request('server-1', 'roots/list', empty).encode(),
|
||||
new_response(2, true, ResponseError{}).encode(),
|
||||
]
|
||||
}
|
||||
mut client := new_client(transport, ClientConfig{})
|
||||
client.initialize()!
|
||||
|
||||
response := client.request_message('ping', empty)!
|
||||
|
||||
assert response.result == 'true'
|
||||
assert transport.sent.len == 3
|
||||
assert decode_request(transport.sent[2])!.method == 'ping'
|
||||
|
||||
notifications := client.take_notifications()
|
||||
assert notifications.len == 1
|
||||
assert notifications[0].method == 'notifications/tools/list_changed'
|
||||
|
||||
requests := client.take_requests()
|
||||
assert requests.len == 1
|
||||
assert requests[0].method == 'roots/list'
|
||||
assert requests[0].id == '"server-1"'
|
||||
}
|
||||
|
||||
fn test_parse_sse_messages_reads_json_rpc_events() {
|
||||
body := 'event: message\r\n' +
|
||||
'data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progress":0.5}}\r\n' +
|
||||
'\r\n' + 'event: message\r\n' + 'data: {"jsonrpc":"2.0","id":1,"result":true}\r\n' + '\r\n'
|
||||
|
||||
messages := parse_sse_messages(body)!
|
||||
|
||||
assert messages.len == 2
|
||||
assert decode_notification(messages[0])!.method == 'notifications/progress'
|
||||
assert decode_response(messages[1])!.result == 'true'
|
||||
}
|
||||
|
||||
fn test_stdio_messages_handle_partial_reads() {
|
||||
payload := new_notification('notifications/initialized', empty).encode()
|
||||
frame := encode_stdio_message(payload)
|
||||
assert frame.ends_with('\n')
|
||||
mut buffer := frame[..frame.len - 1]
|
||||
|
||||
try_extract_stdio_message(buffer) or { assert err.msg() == NoFrameError{}.msg() }
|
||||
|
||||
buffer += frame[frame.len - 1..]
|
||||
extracted := try_extract_stdio_message(buffer)!
|
||||
buffer = extracted.remaining
|
||||
message := extracted.message
|
||||
|
||||
assert buffer == ''
|
||||
assert decode_notification(message)!.method == 'notifications/initialized'
|
||||
}
|
||||
|
||||
fn test_stdio_messages_strip_embedded_newlines() {
|
||||
payload := '{"jsonrpc":"2.0",\n"method":"ping"}'
|
||||
frame := encode_stdio_message(payload)
|
||||
assert frame == '{"jsonrpc":"2.0","method":"ping"}\n'
|
||||
}
|
||||
|
||||
fn test_stdio_messages_skip_blank_lines() {
|
||||
first := encode_stdio_message(new_notification('a', empty).encode())
|
||||
second := encode_stdio_message(new_notification('b', empty).encode())
|
||||
buffer := first + '\n\n' + second
|
||||
|
||||
frame_a := try_extract_stdio_message(buffer)!
|
||||
assert decode_notification(frame_a.message)!.method == 'a'
|
||||
frame_b := try_extract_stdio_message(frame_a.remaining)!
|
||||
assert decode_notification(frame_b.message)!.method == 'b'
|
||||
}
|
||||
|
||||
fn test_close_delegates_to_the_transport() {
|
||||
mut transport := &MockTransport{}
|
||||
mut client := new_client(transport, ClientConfig{})
|
||||
|
||||
client.close()
|
||||
|
||||
assert transport.closed
|
||||
}
|
||||
Reference in new issue
Block a user