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

Resources.pudu

Pudu95 lines4.3 KB

GitHub ↗
1/** @App.Resources.Handler — documentation and reference as resources */2module PuduLangMcp.App.Resources34import Std.Json as Json5import PuduLangMcp.App.Context as Context6import PuduLangMcp.App.Tools.Reference as Reference7import PuduLangMcp.Constants.Protocol as Protocol8import PuduLangMcp.Constants.Server as Server9import PuduLangMcp.Domain.Docs.Chapter as Chapter10import PuduLangMcp.Domain.Protocol.Pagination as Pagination11import PuduLangMcp.Domain.Rpc.Reply as Reply12import PuduLangMcp.Errors.RpcError as RpcError13import PuduLangMcp.Errors.ToolError as ToolError14import PuduLangMcp.Utils.JsonAccess as Access15import PuduLangMcp.Utils.Uri as Uri1617/// The table of contents of the documentation.18export const INDEX_URI: Str = "pudu://docs/index"1920/// What every module-reference URI begins with.21export const REFERENCE_PREFIX: Str = "pudu://reference/"2223/// The scheme of every resource this server offers.24const SCHEME: Str = "pudu://"2526/// The media type of documentation.27const MARKDOWN: Str = "text/markdown"2829/// The media type of a module reference.30const PLAIN_TEXT: Str = "text/plain"3132/// Answers one page of the documentation resources.33export fn list(context: &Context.Context, params: &Json.Json) -> Result[Json.Json, RpcError.ProtocolError] {34  let cursor = Pagination.cursorOf(params) ?35  let all = [resource(INDEX_URI, "index", "Pudu documentation contents")].concat(context.chapters.map(|held: Chapter.Chapter| resource(SCHEME + held.group + "/" + held.slug, held.group + "/" + held.slug, held.title)))36  let paged = Pagination.page(&all, &cursor, Server.PAGE_SIZE) ?37  var fields = [("resources", Json.list(&paged[0]))]38  if let Some(next) = paged[1] { fields = fields.push(("nextCursor", Json.Text(next))) }39  Ok(Reply.complete(&fields, &Reply.Cached(Server.LIST_TTL_MS, Protocol.CACHE_PUBLIC)))40}4142/// Answers the module-reference template.43export fn templates(context: &Context.Context, params: &Json.Json) -> Result[Json.Json, RpcError.ProtocolError] {44  let cursor = Pagination.cursorOf(params) ?45  let template = Json.object(&[46      ("uriTemplate", Json.Text(REFERENCE_PREFIX + "\{module\}")),47      ("name", Json.Text("reference")),48      ("title", Json.Text("Module reference")),49      ("description", Json.Text("Every public declaration of one standard-library or installed-package module.")),50      ("mimeType", Json.Text(PLAIN_TEXT))51    ])52  let paged = Pagination.page(&[template], &cursor, Server.PAGE_SIZE) ?53  Ok(Reply.complete(&[("resourceTemplates", Json.list(&paged[0]))], &Reply.Cached(Server.LIST_TTL_MS, Protocol.CACHE_PUBLIC)))54}5556/// Answers the contents of one resource.57export fn read(context: &Context.Context, params: &Json.Json) -> Result[Json.Json, RpcError.ProtocolError] {58  let uri = match Access.text(params, "uri") {59    case Some(given) => given60    case None => { return Err(RpcError.InvalidParams("uri must be a string")) }61  }62  if uri == INDEX_URI {63    return Ok(contents(uri, MARKDOWN, Chapter.tableOfContents(&context.chapters), Server.DOCS_TTL_MS, Protocol.CACHE_PUBLIC))64  }65  if let Some(moduleName) = Uri.resourcePath(uri, REFERENCE_PREFIX) {66    return match Reference.moduleText(context, moduleName) {67      case Ok(text) => Ok(contents(uri, PLAIN_TEXT, text, Server.STDLIB_TTL_MS, Protocol.CACHE_PRIVATE))68      case Err(ToolError.NotFound(_)) => Err(RpcError.ResourceNotFound(uri))69      case Err(failure) => Err(RpcError.Internal(ToolError.explain(&failure)))70    }71  }72  let path = match Uri.resourcePath(uri, SCHEME) {73    case Some(rest) => rest74    case None => { return Err(RpcError.ResourceNotFound(uri)) }75  }76  let slash = path.indexOf("/")77  if slash == -1 { return Err(RpcError.ResourceNotFound(uri)) }78  match Chapter.find(&context.chapters, path.take(slash), path.drop(slash + 1)) {79    case Some(held) => Ok(contents(uri, MARKDOWN, held.markdown, Server.DOCS_TTL_MS, Protocol.CACHE_PUBLIC))80    case None => Err(RpcError.ResourceNotFound(uri))81  }82}8384/// One listed resource.85fn resource(uri: Str, name: Str, title: Str) -> Json.Json {86  Json.object(&[("uri", Json.Text(uri)), ("name", Json.Text(name)), ("title", Json.Text(title)), ("mimeType", Json.Text(MARKDOWN))])87}8889/// A read result holding one text content.90fn contents(uri: Str, mimeType: Str, text: Str, ttl: Int, visibility: Str) -> Json.Json {91  Reply.complete(&[92      ("contents", Json.list(&[Json.object(&[("uri", Json.Text(uri)), ("mimeType", Json.Text(mimeType)), ("text", Json.Text(text))])]))93    ], &Reply.Cached(ttl, visibility))94}95