// MCP servers a client may hand to a session. The protocol carries their configuration; connecting
// to them is the agent's business, so these are records and nothing more.
///|
/// One header sent to an MCP server.
#derive(Eq)
pub(all) struct HttpHeader {
name : String
value : String
meta : Json?
}
///|
pub extend HttpHeader with Eq::{equal, not_equal}
///|
pub extend HttpHeader with @json.ToJson::{to_json}
///|
pub impl ToJson for HttpHeader with fn to_json(self) {
json_of([
("name", Some(Json::string(self.name))),
("value", Some(Json::string(self.value))),
("_meta", self.meta),
])
}
///|
pub extend HttpHeader with @json.FromJson::{from_json}
///|
pub impl @json.FromJson for HttpHeader with fn from_json(json, path) {
let fields = expect_object(json, path)
{
name: required_string(fields, "name", path),
value: required_string(fields, "value", path),
meta: meta_of(fields, path),
}
}
///|
/// One environment variable for a spawned MCP server.
#derive(Eq)
pub(all) struct EnvVariable {
name : String
value : String
meta : Json?
}
///|
pub extend EnvVariable with Eq::{equal, not_equal}
///|
pub extend EnvVariable with @json.ToJson::{to_json}
///|
pub impl ToJson for EnvVariable with fn to_json(self) {
json_of([
("name", Some(Json::string(self.name))),
("value", Some(Json::string(self.value))),
("_meta", self.meta),
])
}
///|
pub extend EnvVariable with @json.FromJson::{from_json}
///|
pub impl @json.FromJson for EnvVariable with fn from_json(json, path) {
let fields = expect_object(json, path)
{
name: required_string(fields, "name", path),
value: required_string(fields, "value", path),
meta: meta_of(fields, path),
}
}
///|
/// An MCP server reached over HTTP.
#derive(Eq)
pub(all) struct McpHttp {
name : String
url : String
headers : Array[HttpHeader]
meta : Json?
}
///|
pub extend McpHttp with Eq::{equal, not_equal}
///|
pub extend McpHttp with @json.ToJson::{to_json}
///|
pub impl ToJson for McpHttp with fn to_json(self) {
json_of([
("name", Some(Json::string(self.name))),
("url", Some(Json::string(self.url))),
("headers", Some(Json::array(self.headers.map(write)))),
("_meta", self.meta),
])
}
///|
pub extend McpHttp with @json.FromJson::{from_json}
///|
pub impl @json.FromJson for McpHttp with fn from_json(json, path) {
let fields = expect_object(json, path)
{
name: required_string(fields, "name", path),
url: required_string(fields, "url", path),
headers: required_objects(fields, "headers", path),
meta: meta_of(fields, path),
}
}
///|
/// An MCP server reached over the older HTTP plus SSE transport.
#derive(Eq)
pub(all) struct McpSse {
name : String
url : String
headers : Array[HttpHeader]
meta : Json?
}
///|
pub extend McpSse with Eq::{equal, not_equal}
///|
pub extend McpSse with @json.ToJson::{to_json}
///|
pub impl ToJson for McpSse with fn to_json(self) {
json_of([
("name", Some(Json::string(self.name))),
("url", Some(Json::string(self.url))),
("headers", Some(Json::array(self.headers.map(write)))),
("_meta", self.meta),
])
}
///|
pub extend McpSse with @json.FromJson::{from_json}
///|
pub impl @json.FromJson for McpSse with fn from_json(json, path) {
let fields = expect_object(json, path)
{
name: required_string(fields, "name", path),
url: required_string(fields, "url", path),
headers: required_objects(fields, "headers", path),
meta: meta_of(fields, path),
}
}
///|
/// An MCP server the client is to launch as a child process.
#derive(Eq)
pub(all) struct McpStdio {
name : String
command : String
args : Array[String]
env : Array[EnvVariable]
meta : Json?
}
///|
pub extend McpStdio with Eq::{equal, not_equal}
///|
pub extend McpStdio with @json.ToJson::{to_json}
///|
pub impl ToJson for McpStdio with fn to_json(self) {
json_of([
("name", Some(Json::string(self.name))),
("command", Some(Json::string(self.command))),
("args", Some(Json::array(self.args.map(Json::string)))),
("env", Some(Json::array(self.env.map(write)))),
("_meta", self.meta),
])
}
///|
pub extend McpStdio with @json.FromJson::{from_json}
///|
pub impl @json.FromJson for McpStdio with fn from_json(json, path) {
let fields = expect_object(json, path)
{
name: required_string(fields, "name", path),
command: required_string(fields, "command", path),
args: required_list(fields, "args", path, (value, at) => text_of(value, at)),
env: required_objects(fields, "env", path),
meta: meta_of(fields, path),
}
}
///|
/// An MCP server a session may use. The wire tells the two HTTP shapes apart by `type`; the stdio
/// shape carries no tag at all.
#derive(Eq)
pub(all) enum McpServer {
Http(McpHttp)
Sse(McpSse)
Stdio(McpStdio)
}
///|
pub extend McpServer with Eq::{equal, not_equal}
///|
pub extend McpServer with @json.ToJson::{to_json}
///|
pub impl ToJson for McpServer with fn to_json(self) {
match self {
Http(server) => json_tagged(write(server), "type", "http")
Sse(server) => json_tagged(write(server), "type", "sse")
Stdio(server) => write(server)
}
}
///|
pub extend McpServer with @json.FromJson::{from_json}
///|
pub impl @json.FromJson for McpServer with fn from_json(json, path) {
let fields = expect_object(json, path)
match string_member(fields, "type", path) {
Some("http") => Http(read(json, path))
Some("sse") => Sse(read(json, path))
Some(other) =>
raise @json.JsonDecodeError(
(path.add_key("type"), "unknown MCP transport \{other}"),
)
None => Stdio(read(json, path))
}
}