Packages

A brief description of your MECP package. Replace this with an actual description of what your package does.

Current section

Files

Jump to
mecp lib tcp_server.ex
Raw

lib/tcp_server.ex

defmodule JsonrpcServer.TcpServer do
use GenServer
require Logger
def start_link(port) do
GenServer.start_link(__MODULE__, port, name: __MODULE__)
end
@impl true
def init(port) do
{:ok, listen_socket} =
:gen_tcp.listen(port, [
:binary,
packet: :line,
active: false,
reuseaddr: true
])
Logger.info("JSON-RPC TCP server listening on port #{port}")
# Start accepting connections
spawn_link(fn -> accept_loop(listen_socket) end)
{:ok, %{listen_socket: listen_socket, port: port}}
end
defp accept_loop(listen_socket) do
case :gen_tcp.accept(listen_socket) do
{:ok, client_socket} ->
Logger.info("New client connected")
# Spawn a process to handle this client
spawn(fn -> __MODULE__.handle_client(client_socket) end)
# Continue accepting new connections
accept_loop(listen_socket)
{:error, reason} ->
Logger.error("Failed to accept connection: #{inspect(reason)}")
accept_loop(listen_socket)
end
end
def handle_client(socket, rest \\ "") do
case :gen_tcp.recv(socket, 0) do
{:ok, data} ->
IO.inspect({:got, data})
json_data = rest <> data
case Jason.decode(json_data) do
{:ok, request} ->
response = __MODULE__.handle_jsonrpc_request(request)
response_json = Jason.encode!(response) <> "\n"
:gen_tcp.send(socket, response_json)
# Continue handling requests from this client
__MODULE__.handle_client(socket)
{:error, error} ->
__MODULE__.handle_client(socket, json_data)
end
{:error, :closed} ->
Logger.info("Client disconnected")
:gen_tcp.close(socket)
{:error, reason} ->
Logger.error("Error receiving data: #{inspect(reason)}")
:gen_tcp.close(socket)
end
end
def handle_jsonrpc_request(%{"jsonrpc" => "2.0", "method" => method, "id" => id} = request) do
params = Map.get(request, "params", [])
IO.inspect({:request, method, id, params})
res =
try do
dispatch_method(method, params)
catch
a, b ->
{:error, -32600, inspect({a, b})}
end
case res do
{:ok, result} ->
%{
"jsonrpc" => "2.0",
"result" => result,
"id" => id
}
{:error, code, message} ->
create_error_response(id, code, message)
end
end
def handle_jsonrpc_request(%{"jsonrpc" => "2.0", "method" => method} = request) do
# Notification (no id field) - don't send response
IO.inspect({:notification, method, request["id"]})
params = Map.get(request, "params", [])
dispatch_method(method, params)
nil
end
def handle_jsonrpc_request(%{"id" => id}) do
create_error_response(id, -32600, "Invalid Request")
end
def handle_jsonrpc_request(_) do
create_error_response(nil, -32600, "Invalid Request")
end
# Protocol version validation
def validate_protocol_version("2024-11-05"), do: :ok
# Also support older version
def validate_protocol_version("2024-10-07"), do: :ok
def validate_protocol_version(version) do
{:error,
"Unsupported protocol version: #{version}. Supported versions: 2024-11-05, 2024-10-07"}
end
def dispatch_method("initialize", params) do
client_info = Map.get(params, "clientInfo", %{})
protocol_version = Map.get(params, "protocolVersion", "2024-11-05")
capabilities = Map.get(params, "capabilities", %{})
Logger.info("MCP Initialize request from client: #{inspect(client_info)}")
Logger.info("Protocol version: #{protocol_version}")
Logger.info("Client capabilities: #{inspect(capabilities)}")
# Validate protocol version
case validate_protocol_version(protocol_version) do
:ok ->
result = %{
"protocolVersion" => "2024-11-05",
"capabilities" => %{"tools" => %{}},
"serverInfo" => %{
"name" => "elixir-mcp-server",
"version" => "1.0.0"
},
"instructions" => "use this server to do elixir tasks"
}
{:ok, result}
{:error, message} ->
{:error, -32602, message}
end
end
def dispatch_method("prompts/list", params) do
{:ok, %{"prompts" => []}}
end
def dispatch_method("resources/list", params) do
{:ok, %{"resources" => []}}
end
def dispatch_method("tools/list", params) do
{:ok, JsonrpcServer.Tools.get_tools_list()}
end
def dispatch_method("tools/call", params) do
JsonrpcServer.Tools.dispatch_tool_call(params)
end
def dispatch_method(method, _params) do
{:error, -32601, "Method not found: #{method}"}
end
def create_error_response(id, code, message) do
%{
"jsonrpc" => "2.0",
"error" => %{
"code" => code,
"message" => message
},
"id" => id
}
end
def test() do
JsonrpcServer.Tools.test()
end
end