diff --git a/src/server/src/fbi/handlers/mcp_servers.gleam b/src/server/src/fbi/handlers/mcp_servers.gleam index 7e4969a..f0ff4ca 100644 --- a/src/server/src/fbi/handlers/mcp_servers.gleam +++ b/src/server/src/fbi/handlers/mcp_servers.gleam @@ -2,10 +2,12 @@ import fbi/context.{type Context} import fbi/db/connection import fbi/db/mcp_servers import fbi/json/mcp_server as mcp_server_json +import gleam/dict.{type Dict} import gleam/dynamic/decode import gleam/http import gleam/int import gleam/json +import gleam/list import gleam/option.{type Option, None, Some} import wisp.{type Request, type Response} @@ -18,7 +20,11 @@ pub fn handle_global(req: Request, ctx: Context) -> Response { } } -pub fn handle_global_one(req: Request, ctx: Context, id_str: String) -> Response { +pub fn handle_global_one( + req: Request, + ctx: Context, + id_str: String, +) -> Response { case int.parse(id_str) { Error(_) -> wisp.bad_request("Invalid MCP server ID") Ok(id) -> @@ -97,6 +103,17 @@ fn show(ctx: Context, id: Int) -> Response { } } +fn encode_string_list(xs: List(String)) -> String { + json.array(xs, json.string) |> json.to_string() +} + +fn encode_string_dict(d: Dict(String, String)) -> String { + dict.to_list(d) + |> list.map(fn(pair) { #(pair.0, json.string(pair.1)) }) + |> json.object + |> json.to_string() +} + fn decode_body( body: decode.Dynamic, ) -> Result( @@ -111,14 +128,25 @@ fn decode_body( None, decode.optional(decode.string), ) - use args_json <- decode.optional_field("args_json", "[]", decode.string) + use args <- decode.optional_field("args", [], decode.list(decode.string)) use url <- decode.optional_field( "url", None, decode.optional(decode.string), ) - use env_json <- decode.optional_field("env_json", "{}", decode.string) - decode.success(#(name, server_type, command, args_json, url, env_json)) + use env <- decode.optional_field( + "env", + dict.new(), + decode.dict(decode.string, decode.string), + ) + decode.success(#( + name, + server_type, + command, + encode_string_list(args), + url, + encode_string_dict(env), + )) } decode.run(body, decoder) } @@ -199,26 +227,26 @@ fn update(req: Request, ctx: Context, id: Int) -> Response { None, decode.optional(decode.optional(decode.string)), ) - use args_json <- decode.optional_field( - "args_json", + use args <- decode.optional_field( + "args", None, - decode.optional(decode.string), + decode.optional(decode.list(decode.string)), ) use url <- decode.optional_field( "url", None, decode.optional(decode.optional(decode.string)), ) - use env_json <- decode.optional_field( - "env_json", + use env <- decode.optional_field( + "env", None, - decode.optional(decode.string), + decode.optional(decode.dict(decode.string, decode.string)), ) - decode.success(#(name, server_type, command, args_json, url, env_json)) + decode.success(#(name, server_type, command, args, url, env)) } case decode.run(body, decoder) { Error(_) -> wisp.bad_request("Invalid request body") - Ok(#(name, server_type, command, args_json, url, env_json)) -> + Ok(#(name, server_type, command, args, url, env)) -> case mcp_servers.update( ctx.db, @@ -226,9 +254,9 @@ fn update(req: Request, ctx: Context, id: Int) -> Response { name, server_type, command, - args_json, + option.map(args, encode_string_list), url, - env_json, + option.map(env, encode_string_dict), ) { Ok(s) -> diff --git a/src/server/src/fbi/json/mcp_server.gleam b/src/server/src/fbi/json/mcp_server.gleam index 458f320..87776ec 100644 --- a/src/server/src/fbi/json/mcp_server.gleam +++ b/src/server/src/fbi/json/mcp_server.gleam @@ -1,16 +1,36 @@ import fbi/db/mcp_servers.{type McpServer} +import gleam/dict.{type Dict} +import gleam/dynamic/decode import gleam/json +import gleam/list +import gleam/result + +fn parse_string_list(raw: String) -> List(String) { + json.parse(raw, decode.list(decode.string)) + |> result.unwrap([]) +} + +fn parse_string_dict(raw: String) -> Dict(String, String) { + json.parse(raw, decode.dict(decode.string, decode.string)) + |> result.unwrap(dict.new()) +} + +fn dict_to_json(d: Dict(String, String)) -> json.Json { + dict.to_list(d) + |> list.map(fn(pair) { #(pair.0, json.string(pair.1)) }) + |> json.object +} pub fn encode(s: McpServer) -> json.Json { json.object([ #("id", json.int(s.id)), #("project_id", json.nullable(s.project_id, json.int)), #("name", json.string(s.name)), - #("server_type", json.string(s.server_type)), + #("type", json.string(s.server_type)), #("command", json.nullable(s.command, json.string)), - #("args_json", json.string(s.args_json)), + #("args", json.array(parse_string_list(s.args_json), json.string)), #("url", json.nullable(s.url, json.string)), - #("env_json", json.string(s.env_json)), + #("env", dict_to_json(parse_string_dict(s.env_json))), #("created_at", json.int(s.created_at)), ]) }