///|
/// Common state shared by all moonmcp components.
pub(all) struct ComponentBase {
/// Type prefix used in lookup keys, e.g. `"tool"` or `"resource"`.
key_prefix : String
name : String
version : String?
title : String?
description : String?
icons : Array[@types.Icon]?
tags : @set.Set[String]
meta : Map[String, Json]?
task_config : @tasks.TaskConfig
}
///|
pub fn ComponentBase::new(
key_prefix : String,
name : String,
version? : String,
title? : String,
description? : String,
icons? : Array[@types.Icon],
tags~ : @set.Set[String] = @set.Set::from_array([]),
meta? : Map[String, Json],
task_config~ : @tasks.TaskConfig = @tasks.TaskConfig::new(
mode=@tasks.Forbidden,
),
) -> ComponentBase {
{
key_prefix,
name,
version,
title,
description,
icons,
tags,
meta,
task_config,
}
}
///|
/// Construct the lookup key for a component type, e.g. `"tool:name"`.
pub fn ComponentBase::make_key(prefix : String, identifier : String) -> String {
if prefix.is_empty() {
identifier
} else {
"\{prefix}:\{identifier}"
}
}
///|
/// The globally unique lookup key for a component.
///
/// Format: `"{key_prefix}:{identifier}@{version}"`; the `@` delimiter is
/// always present so keys parse unambiguously even when the identifier
/// itself contains `@`.
pub fn ComponentBase::key(self : ComponentBase, identifier : String) -> String {
let base_key = ComponentBase::make_key(self.key_prefix, identifier)
"\{base_key}@\{self.version.unwrap_or("")}"
}
///|
/// The public meta information about the component.
///
/// Always includes a `fastmcp` key containing the sorted `tags` list and the
/// `version` (when set); internal keys (prefixed with `_`) are stripped from
/// the fastmcp namespace.
pub fn ComponentBase::get_meta(self : ComponentBase) -> Map[String, Json] {
let meta = self.meta.map_or({}, fn(m) { m.copy() })
// The core `Compare` for `String` orders by length first; tags follow
// fastmcp's lexicographic `sorted()` instead.
let sorted_tags = self.tags.to_array()
sorted_tags.sort_by(String::lexical_compare)
let fastmcp_meta : Map[String, Json] = { "tags": Json(sorted_tags) }
if self.version is Some(version) {
fastmcp_meta["version"] = Json(version)
}
if meta.get("fastmcp") is Some(Object(upstream)) {
for key, value in upstream {
if !key.has_prefix("_") {
fastmcp_meta[key] = value
}
}
}
meta["fastmcp"] = Json(fastmcp_meta)
meta
}
///|
/// A moonmcp component: tool, prompt, resource or resource template.
pub(open) trait Component {
fn base(Self) -> ComponentBase
/// The raw identifier: the name for tools and prompts, the URI for
/// resources and resource templates.
fn identifier(Self) -> String = _
fn key(Self) -> String = _
fn get_meta(Self) -> Map[String, Json] = _
}
///|
impl Component with fn identifier(self) {
self.base().name
}
///|
impl Component with fn key(self) {
self.base().key(self.identifier())
}
///|
impl Component with fn get_meta(self) {
self.base().get_meta()
}