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

Completion.pudu

Pudu52 lines2.3 KB

GitHub ↗
1/** @App.Completion.Handler — argument completion for templates and prompts */2module PuduLangMcp.App.Completion34import Std.Json as Json5import Std.List as List6import Std.Option as Option7import PuduLangMcp.App.Context as Context8import PuduLangMcp.App.Resources as Resources9import PuduLangMcp.Domain.Docs.Chapter as Chapter10import PuduLangMcp.Domain.Rpc.Reply as Reply11import PuduLangMcp.Errors.RpcError as RpcError12import PuduLangMcp.Services.Reference as Reference13import PuduLangMcp.Utils.JsonAccess as Access1415/// The most values one completion answers, as the specification allows.16const MAX_VALUES: Int = 1001718/// Answers the values that could complete one argument.19export fn complete(context: &Context.Context, params: &Json.Json) -> Result[Json.Json, RpcError.ProtocolError] {20  let reference = Option.unwrapOr(Access.member(params, "ref"), Json.Null)21  let kind = match Access.text(&reference, "type") {22    case Some(given) => given23    case None => { return Err(RpcError.InvalidParams("ref.type must be a string")) }24  }25  let argument = Option.unwrapOr(Access.member(params, "argument"), Json.Null)26  let name = match Access.text(&argument, "name") {27    case Some(given) => given28    case None => { return Err(RpcError.InvalidParams("argument.name must be a string")) }29  }30  let value = match Access.text(&argument, "value") {31    case Some(given) => given.toLower()32    case None => { return Err(RpcError.InvalidParams("argument.value must be a string")) }33  }34  let template = Resources.REFERENCE_PREFIX + "\{module\}"35  let candidates = if kind == "ref/resource" && Access.text(&reference, "uri") == Some(template) && name == "module" {36    context.moduleFiles.map(|held: Reference.ModuleFile| held.moduleName)37  } else if kind == "ref/prompt" && Access.text(&reference, "name") == Some("pudu_explain") && name == "topic" {38    Chapter.inGroup(&context.chapters, "docs").map(|held: Chapter.Chapter| held.slug)39  } else {40    []41  }42  let matching = candidates.filter(|candidate: Str| candidate.toLower().startsWith(value))43  let shown = List.take(&matching, MAX_VALUES)44  Ok(Reply.complete(&[45        ("completion", Json.object(&[46              ("values", Access.texts(&shown)),47              ("total", Json.Number(matching.length())),48              ("hasMore", Json.Boolean(matching.length() > shown.length()))49            ]))50      ], &Reply.NoCache))51}52