// What a server can be extended with: components another object supplies, the
// way those components are shown, and the code every request passes through
// before it is served.
///|
/// Take components from `provider` as well as from the server's own registry.
/// The provider is asked again on every listing, so what it supplies may change
/// between two of them. A name this server already answers is not taken from a
/// provider.
pub fn Server::add_provider(
self : Server,
provider : &@providers.Provider,
) -> Unit {
self.providers.push(provider)
}
///|
/// Apply `transform` to the components the server offers, in the order the
/// transforms were added: each one sees what the one before it produced.
pub fn Server::add_transform(
self : Server,
transform : &@transforms.Transform,
) -> Unit {
self.transforms.push(transform)
}
///|
/// Put `layer` in front of every request: the first one added is the outermost,
/// so it sees a request before the ones after it and its answer last.
///
/// A layer that raises `@exceptions.Mcp` refuses the request with that code; a
/// layer that answers without calling on to the rest answers for the server.
pub fn Server::use_layer(self : Server, layer : @layers.Layer) -> Unit {
self.layers.push(layer)
}
///|
/// The tools a client is offered: the registered ones, then whatever the
/// providers supply, as the transforms leave them.
pub fn Server::offered_tools(self : Server) -> Array[@tools.Tool] {
self.view().0
}
///|
/// The resources a client is offered, after the transforms.
pub fn Server::offered_resources(self : Server) -> Array[@resources.Resource] {
self.view().1
}
///|
/// The resource templates a client is offered, after the transforms.
pub fn Server::offered_templates(
self : Server,
) -> Array[@resources.ResourceTemplate] {
self.view().2
}
///|
/// The prompts a client is offered, after the transforms.
pub fn Server::offered_prompts(self : Server) -> Array[@prompts.Prompt] {
self.view().3
}
///|
/// Everything the server offers, gathered from its registry and its providers
/// and then put through every transform. A name offered twice is taken from
/// whichever came first.
fn Server::view(
self : Server,
) -> (
Array[@tools.Tool],
Array[@resources.Resource],
Array[@resources.ResourceTemplate],
Array[@prompts.Prompt],
) {
let tools : Array[@tools.Tool] = []
let resources : Array[@resources.Resource] = []
let templates : Array[@resources.ResourceTemplate] = []
let prompts : Array[@prompts.Prompt] = []
let tool_names : Map[String, Unit] = Map([])
let resource_uris : Map[String, Unit] = Map([])
let template_patterns : Map[String, Unit] = Map([])
let prompt_names : Map[String, Unit] = Map([])
for tool in self.tools.values() {
tool_names[tool.component.name] = ()
tools.push(tool)
}
for resource in self.resources.values() {
resource_uris[resource.uri] = ()
resources.push(resource)
}
for template in self.templates {
template_patterns[template.uri_template] = ()
templates.push(template)
}
for prompt in self.prompts.values() {
prompt_names[prompt.component.name] = ()
prompts.push(prompt)
}
for provider in self.providers {
for tool in provider.tools() {
if !tool_names.contains(tool.component.name) {
tool_names[tool.component.name] = ()
tools.push(tool)
}
}
for resource in provider.resources() {
if !resource_uris.contains(resource.uri) {
resource_uris[resource.uri] = ()
resources.push(resource)
}
}
for template in provider.templates() {
if !template_patterns.contains(template.uri_template) {
template_patterns[template.uri_template] = ()
templates.push(template)
}
}
for prompt in provider.prompts() {
if !prompt_names.contains(prompt.component.name) {
prompt_names[prompt.component.name] = ()
prompts.push(prompt)
}
}
}
@transforms.apply(self.transforms, tools, resources, templates, prompts)
}
///|
/// The schema a tool's arguments are checked against. A tool that came from a
/// provider is compiled the first time it is called; one whose schema does not
/// compile is served without the check rather than failing the request.
fn Server::check_of(self : Server, tool : @tools.Tool) -> @moonschema.Schema? {
let name = tool.component.name
match self.checks.get(name) {
Some(check) => Some(check)
None => {
let check = @moonschema.Schema::new(tool.parameters) catch {
_ => return None
}
self.checks[name] = check
Some(check)
}
}
}