Pudu programming language
Menu
Package

@chrismichaelps / pudu-lang-mcp

Model Context Protocol server for Pudu language documentation and compiler tools

0.1.1Apache-2.01

InstallClose

ToolSpec.pudu

Pudu83 lines3.3 KB

GitHub ↗
1/** @Domain.Catalog.ToolSpec.Aggregate — tool definitions held as data */2module PuduLangMcp.Domain.Catalog.ToolSpec34import Std.Json as Json5import PuduLangMcp.Utils.JsonAccess as Access67/** @Domain.Catalog.ToolSpec.KindJSON kind of one argument */8export type Kind = TextKind | IntegerKind | BooleanKind910/** @Domain.Catalog.ToolSpec.Property — one declared tool argument */11export type Property = { name: Str, kind: Kind, description: Str, required: Bool, minimum: Option[Int], maximum: Option[Int] }1213/** @Domain.Catalog.ToolSpec.Annotations — behaviour hints every tool declares */14export type Annotations = { readOnly: Bool, destructive: Bool, idempotent: Bool, openWorld: Bool }1516/** @Domain.Catalog.ToolSpec.ToolSpec — one tool described once */17export type ToolSpec = {18  name: Str,19  title: Str,20  description: Str,21  properties: Array[Property],22  exactlyOne: Array[Str],23  annotations: Annotations24}2526/// A string argument.27export fn text(name: Str, description: Str, required: Bool) -> Property {28  Property{name: name, kind: TextKind, description: description, required: required, minimum: None, maximum: None}29}3031/// An integer argument bounded to `minimum..maximum`.32export fn integer(name: Str, description: Str, required: Bool, minimum: Int, maximum: Int) -> Property {33  Property{name: name, kind: IntegerKind, description: description, required: required, minimum: Some(minimum), maximum: Some(maximum)}34}3536/// The JSON Schema type name of an argument kind.37export fn kindName(kind: &Kind) -> Str {38  match kind {39    case TextKind => "string"40    case IntegerKind => "integer"41    case BooleanKind => "boolean"42  }43}4445/// A tool as `tools/list` describes it.46export fn toJson(spec: &ToolSpec) -> Json.Json {47  Json.object(&[48      ("name", Json.Text(spec.name)),49      ("title", Json.Text(spec.title)),50      ("description", Json.Text(spec.description)),51      ("inputSchema", schema(spec)),52      ("annotations", Json.object(&[53            ("title", Json.Text(spec.title)),54            ("readOnlyHint", Json.Boolean(spec.annotations.readOnly)),55            ("destructiveHint", Json.Boolean(spec.annotations.destructive)),56            ("idempotentHint", Json.Boolean(spec.annotations.idempotent)),57            ("openWorldHint", Json.Boolean(spec.annotations.openWorld))58          ]))59    ])60}6162/// The input schema of a tool.63fn schema(spec: &ToolSpec) -> Json.Json {64  var fields = [("type", Json.Text("object")), ("additionalProperties", Json.Boolean(false))]65  if !spec.properties.isEmpty() {66    fields = fields.push(("properties", Json.object(&spec.properties.map(|held: Property| (held.name, propertyJson(&held))))))67  }68  let required = spec.properties.filter(|held: Property| held.required).map(|held: Property| held.name)69  if !required.isEmpty() { fields = fields.push(("required", Access.texts(&required))) }70  if !spec.exactlyOne.isEmpty() {71    fields = fields.push(("oneOf", Json.list(&spec.exactlyOne.map(|name: Str| Json.object(&[("required", Access.texts(&[name]))])))))72  }73  Json.object(&fields)74}7576/// The schema of one argument.77fn propertyJson(held: &Property) -> Json.Json {78  var fields = [("type", Json.Text(kindName(&held.kind))), ("description", Json.Text(held.description))]79  if let Some(low) = held.minimum { fields = fields.push(("minimum", Json.Number(low))) }80  if let Some(high) = held.maximum { fields = fields.push(("maximum", Json.Number(high))) }81  Json.object(&fields)82}83