jmcp: dispatcher tool-name enum + fix compile-check bracket reader
ober
c47c290eeac03c0014dd5637592ede50c5dd639b
--- a/mcp/server.ss +++ b/mcp/server.ss @@ -164,19 +164,29 @@ " (write result)\n" " (newline)))\n")) +;; Mirror the real .ss -> .sls front-end (jerboa translator's translate-file): +;; apply the default string transforms, read forms with the native Chez reader +;; (where [..] are parens, matching the language), run the default s-expr +;; transforms per form, then compile. The (jerboa reader)'s jerboa-read must NOT +;; be used here: it reads [x y] as (list x y) (Clojure-compat), which mangles +;; bracket bindings like (let loop ([x y]) ...) into false syntax errors. (def (build-syntax-script code imports) (def escaped (scheme-escape code)) (string-append (join-lines (import-lines imports)) - "\n\n" + "\n(import (jerboa translator))\n\n" "(guard (e [else\n" " (display \"" error-marker "\\n\")\n" " (display-condition e (current-output-port))])\n" - " (let ([port (open-string-input-port \"" escaped "\")])\n" + " (let* ([cooked (translate-method-dispatch\n" + " (translate-hash-bang\n" + " (translate-keywords \"" escaped "\")))]\n" + " [xlate (apply make-translator (default-transforms))]\n" + " [port (open-string-input-port cooked)])\n" " (let loop ()\n" - " (let ([expr (jerboa-read port)])\n" + " (let ([expr (read port)])\n" " (unless (eof-object? expr)\n" - " (compile expr)\n" + " (compile (xlate expr))\n" " (loop)))))\n" " (display \"" valid-marker "\\n\"))\n")) @@ -340,22 +350,29 @@ (string=? (hash-ref t "name") "jerboa"))) *tools*)) +(def (tool-short-name t) + (if (string-prefix? "jerboa_" (hash-ref t "name")) + (substring (hash-ref t "name") 7 (string-length (hash-ref t "name"))) + (hash-ref t "name"))) + +(def (routable-tools) + (filter (lambda (t) + (and (not (hash-ref t 'critical)) + (not (string=? (hash-ref t "name") "jerboa")))) + *tools*)) + +;; Names placed on the dispatcher's `tool` enum: a cheap always-visible +;; table of contents (names only, ~hundreds of tokens) so a model can find a +;; deferred tool without first calling tool="list". Schemas stay lazy. +(def (routable-tool-names) + (unique (append '("list" "describe") + (map tool-short-name (routable-tools))))) + (def (manifest-line t) - (string-append "- " - (if (string-prefix? "jerboa_" (hash-ref t "name")) - (substring (hash-ref t "name") 7 (string-length (hash-ref t "name"))) - (hash-ref t "name")) - ": " - (hash-ref t "description"))) + (string-append "- " (tool-short-name t) ": " (hash-ref t "description"))) (def (dispatcher-manifest) - (string-join - (map manifest-line - (filter (lambda (t) - (and (not (hash-ref t 'critical)) - (not (string=? (hash-ref t "name") "jerboa")))) - *tools*)) - "\n")) + (string-join (map manifest-line (routable-tools)) "\n")) (def (format-schema s) (def props (hash-ref s "properties")) @@ -4168,8 +4185,14 @@ '("file_path")) tool-bisect-crash #f '()) (register-tool "jerboa" "Jerboa Tool Dispatcher" - "Invoke any non-critical jerboa_* tool by name." - (schema (list (list "tool" (property "string" "Tool name")) + (string-append + "Invoke any non-critical jerboa_* tool by name. The `tool` enum lists " + "every routable tool. Use tool=\"describe\" with args={\"name\":\"<tool>\"} " + "to see a tool's arguments, then call it with args={...}. " + "tool=\"list\" returns names plus descriptions.") + (schema (list (list "tool" (jhash "type" "string" + "description" "Tool to invoke, or \"list\"/\"describe\"" + "enum" (routable-tool-names))) (list "args" (property "object" "Tool arguments"))) '("tool")) dispatcher #f '()))