Q:

Can an annotation type define static methods?

I have developed a framework and corresponding API which includes a runtime-visible annotation.  The API also supplies some helper methods intended for client use on objects whose classes have that annotation.  Understandably, the helpers are tightly-coupled with the annotation, but it is important that their internals be encapsulated from the client.  The helper methods are currently provided via static inner class within the annotation type...
@Target(TYPE)
@Retention(RUNTIME)
public @interface MyAnnotation {
   // ... annotation elements, e.g. `int xyz();` ...

   public static final class Introspection {
       public static Foo helper(Object mightHaveMyAnnotation) {
           /* ... uses MyAnnotation.xyz() if annotation is present ... */
      }
   }
}

... but the helpers could just as easily exist in some other top-level utility class.  Either way provides the necessary amount of encapsulation from client code, but both incur extra costs to maintain a completely separate type, prevent them from instantiation since all useful methods are static, etc.
When Java 8 introduced static methods on Java interface types (see JLS 9.4), the feature was touted as providing the ability to...

... organize helper methods in your libraries; you can keep static methods specific to an interface in the same interface rather than in a separate class.
— from Java Tutorials Interface Default Methods

This has been used within the JDK libraries to provide implementations such as List.of(...), Set.of(...), etc., whereas previously such methods were relegated to a separate utility class such as java.util.Collections.  By locating the utility methods within their related interfaces, it improves their discoverability and removes arguably unnecessary helper class types from the API domain.
Since I the current JVM bytecode representation for annotation types is very closely related to normal interfaces, I wondered if the annotations would also support static methods.  When I moved the helpers into the annotation type, such as:
@Target(TYPE)
@Retention(RUNTIME)
public @interface MyAnnotation {
   // ... annotation elements ...

   public static Foo helper(Object mightHaveMyAnnotation) { /* ... */ }
}

... I was slightly surprised that javac complained with the following compile-time errors:
OpenJDK Runtime Environment 18.3 (build 10+46)

modifier static not allowed here
elements in annotation type declarations cannot declare formal parameters
interface abstract methods cannot have body

Clearly, the Java language does not currently allow this.  It may be that there are good design reasons to disallow it or, as previously presumed for static interface methods, "there [was] no compelling reason to do so; consistency isn't sufficiently compelling to change the status quo".
It is specifically not the goal of this question to ask "why doesn't it work?" or "should the language support it?", as to avoid opinion-based answers.
The JVM is a powerful technology and in many ways more flexible than what's allowed by the Java Language.  At the same time, the Java Language continues to evolve, and today's answer may be obsolete tomorrow.  With the understanding that such power must be used with great care...
Is it technically possible to encapsulate static behavior directly within an annotation type, and how?

A:

It is technically feasible to accomplish this within the JVM and interoperate with standard Java code, but it comes with important caveats:

Java-compatible source code, per the JLS, cannot define static methods in annotation types.
Java source code appears able to use such methods if they exist, including at compile-time and at runtime via reflection.
The subject annotation may need to be placed in a separate compilation unit so that its binary class is available to IDEs and javac when processing code.
This has been verified on OpenJDK 10 HotSpot, but there is the potential that the observed behavior may depend on internal details subject to change in later releases.
Carefully consider the impacts to long-term maintenance and compatibility before deciding to adopt this approach.

A proof-of-concept was successful, using a mechanism that directly manipulates JVM bytecode.
The mechanism is simple enough.  Use an alternate language, or a bytecode manipulation tool (i.e. ASM), which will emit a JVM *.class file that both (1) matches the function and appearance of a legal Java (language) annotation, and (2) also contains the desired method implementation with the static access modifier set.  This class file can be compiled separately and packaged into a JAR or placed onto the classpath directly, at which point it becomes usable by your other normal Java code.
The following steps will create the working bytecode corresponding to the following not-quite-legal Java annotation type, which defines a trivial strlen static function for simplicity in the POC:
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    String value();

    // not legal in Java, through at least JDK 10:
    public static int strlen(java.lang.String str) {
        return str.length(); // boring!
    }
}

First, set up the annotation class with a "normal" value() parameter as a String without a default value:
import static org.objectweb.asm.Opcodes.*;
import java.util.*;
import org.objectweb.asm.*;
import org.objectweb.asm.tree.*;

/* ... */

final String fqcn = "com.example.MyAnnotation";
final String methodName = "strlen";
final String methodDesc = "(Ljava/lang/String;)I"; // int function(String)

ClassNode cn = new ClassNode(ASM6);
cn.version = V1_8; // Java 8
cn.access = ACC_SYNTHETIC | ACC_PUBLIC | ACC_INTERFACE | ACC_ABSTRACT | ACC_ANNOTATION;
cn.name = fqcn.replace(".", "/");
cn.superName = "java/lang/Object";
cn.interfaces = Arrays.asList("java/lang/annotation/Annotation");

// String value();
cn.methods.add(
    new MethodNode(
        ASM6, ACC_PUBLIC | ACC_ABSTRACT, "value", "()Ljava.lang.String;", null, null));

Optionally annotate the annotation with @Retention(RUNTIME), if appropriate:
AnnotationNode runtimeRetention = new AnnotationNode(ASM6, "Ljava/lang/annotation/Retention;");
runtimeRetention.values = Arrays.asList(
    "value", // parameter name; related value follows immediately next:
    new String[] { "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME" } // enum type & value
);
cn.visibleAnnotations = Arrays.asList(runtimeRetention);

Next, add the desired static method:
MethodNode method = new MethodNode(ASM6, 0, methodName, methodDesc, null, null);
method.access = ACC_PUBLIC | ACC_STATIC;
method.annotationDefault = Integer.MIN_VALUE; // see notes
AbstractInsnNode invokeStringLength =
    new MethodInsnNode(INVOKEVIRTUAL, "java/lang/String", "length", "()I", false);
method.instructions.add(new IntInsnNode(ALOAD, 0)); // push String method arg
method.instructions.add(invokeStringLength);        // invoke .length()
method.instructions.add(new InsnNode(IRETURN));     // return an int value
method.maxLocals = 1;
method.maxStack = 1;
cn.methods.add(method);

Finally, output the JVM bytecode for this annotation to a *.class file on the classpath, or load it directly in memory using a custom ClassLoader (not shown):
ClassWriter cw = new ClassWriter(0);
cn.accept(cw);
byte[] bytecode = cw.toByteArray();

Notes:

This requires generating bytecode version 52 (Java 8) or later, and will only run under JVMs that support that version.
Annotations have java.lang.Object as their super type, and they implement the java.lang.annotation.Annotation interface.
The two null parameters to the MethodNode constructors are for generics and declared exceptions, neither are used in this example.
OpenJDK 10's HotSpot required setting MethodNode.annotationDefault to a non-null value (of the appropriate type) on the static method, even though setting/overriding strlen would never be an option when applying the annotation to another element.  This is a gray area for this approach being "legal". The HS bytecode verifier seemed to ignore the ACC_STATIC flag and assumes all defined methods are normal annotation elements.

