Daniel Sun created GROOVY-12487:
-----------------------------------

             Summary: Add an optional MCP server
                 Key: GROOVY-12487
                 URL: https://issues.apache.org/jira/browse/GROOVY-12487
             Project: Groovy
          Issue Type: New Feature
            Reporter: Daniel Sun


An agent needs to ask the Groovy compiler about source without running that 
source. {{groovy-mcp}} is an optional subproject that speaks the Model Context 
Protocol (MCP) over stdio. It calls the existing {{GroovyLanguageEngine}} in 
{{groovy-lsp}}. It is not a second compiler, and it is not part of 
{{groovy-all}}.


h3. Launch

{noformat}
groovymcp [--stdio] [--root DIR] [--classpath PATH] [--version] [--help]
{noformat}

* {{--stdio}} is accepted and changes nothing. Stdio is the only transport.
* {{--root}} confines every {{path}} argument to that directory. The directory 
is not scanned. Without it, {{path}} is rejected and in-memory {{text}} still 
works.
* {{--classpath}} is the compile classpath. It is fixed at launch. A tool call 
cannot change it, and the server reports the entry count, not the paths.
* {{--help}} wins over later arguments and exits 0. {{--version}} prints 
{{groovy-mcp}} and the implementation version.

Stdout is the protocol stream. Server logs go to stderr. {{main}} redirects 
incidental standard output to stderr only while the process is serving, and 
restores it afterwards.

h3. Protocol

One JSON-RPC 2.0 message is one UTF-8 line. A JSON array (a batch) is rejected. 
CRLF is one line; the CR is not part of the payload and does not count toward 
the message cap.

Supported protocol versions are {{2025-11-25}}, {{2025-06-18}}, {{2025-03-26}}, 
and {{2024-11-05}}. A supported version is echoed. Any other non-empty version 
is answered as {{2025-11-25}}.

A session must {{initialize}}, then send {{notifications/initialized}}, before 
tools, resources, or prompts answer. {{ping}} is allowed earlier. A second 
{{initialize}} is an error. Requests before the session is ready return 
{{-32002}}.

* A tool mistake, including an unknown tool, is a successful response with 
{{isError}} set. The unknown-tool text is the constant {{unknown tool}}. The 
name from the request is not repeated.
* An unknown resource, an unknown prompt, or an argument value that is not an 
object returns {{-32602}}.
* An unexpected failure is {{-32603}} with the message {{Internal error}}. The 
log records the exception class and a short message, not the source text.
* {{notifications/cancelled}} is ignored. The session handles one request at a 
time, and a compile is not interrupted.

Tool input and output schemas use JSON Schema 2020-12. Each tool has a title 
and an {{outputSchema}}. A successful {{structuredContent}} matches that 
schema. A failed call's {{structuredContent}} is an {{error}} string.

h3. What the server exposes

Thirteen read-only tools wrap the engine: diagnostics, describe, definition, 
implementations, references, symbols, complete, signatures, tokens, format, 
organize-imports, rename, and close. Format, organize-imports, and rename 
return edits. They do not write files. An empty rename means the compiler did 
not prove the binding.

Positions are 0-based UTF-16 and are passed through unchanged.

Source is passed in one of three ways:

* {{text}} keeps the source in a {{groovy-buffer}} URI. The default is 
{{groovy-buffer:snippet}}. {{text}} may also carry a {{uri}} of that scheme. It 
is not attached to a file URI.
* {{path}} reads one file under {{--root}}.
* {{uri}} addresses a buffer that is already open. A file URI must stay inside 
the root, including the path after symbolic links are resolved.

{{text}} together with {{path}} is rejected, and so is {{path}} together with 
{{uri}}.

Three JSON resources list the lexer keywords, the default imports, and the 
server limits. The keyword list and the default imports come from the compiler. 
Two prompts, {{explain-groovy}} and {{review-groovy}}, tell the model to use 
those tools and not to run the source. The server does not treat the prompt 
text as instructions to itself.

h3. Limits and safety

Defaults are 2 MiB for one message, 512 Ki characters of source, and 512 Ki 
characters of result JSON. Oversized tool JSON is replaced with an error 
result. Client strings that must be quoted are shortened, and control 
characters are replaced.

There is no evaluate tool. Grab and ASTTest stay disabled. The process does not 
walk a workspace, invoke Gradle or Maven, or download dependencies. Sampling, 
elicitation, tasks, and HTTP transports are not implemented. The module does 
not depend on an MCP library or an LLM client library. JSON is parsed and 
written with {{groovy.json}}.

h3. Build

The module is optional. Binary-compatibility checks are off until a release has 
published a baseline jar, as with {{groovy-lsp}}. The jar's main class is 
{{org.apache.groovy.mcp.GroovyMcpLauncher}}. The binary distribution ships the 
jar, {{groovymcp}}, and {{groovymcp.bat}}. Both scripts are force-added because 
{{bin/}} is ignored, the same way {{groovylsp}} is tracked. The user guide is 
included from the binary documentation.

The public types are {{GroovyMcpServer}}, {{GroovyMcpLauncher}}, and 
{{McpServerConfig}}. Types under {{org.apache.groovy.mcp.internal}} are not a 
supported API.






--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to