33 * Apache License Version 2.0 https://jooby.io/LICENSE.txt
44 * Copyright 2014 Edgar Espina
55 */
6- package io .jooby .internal .apt ;
6+ package io .jooby .internal .apt . mcp ;
77
88import static io .jooby .internal .apt .AnnotationSupport .VALUE ;
99import static io .jooby .internal .apt .CodeBlock .*;
2323import javax .lang .model .element .ExecutableElement ;
2424import javax .lang .model .element .TypeElement ;
2525
26+ import io .jooby .internal .apt .AnnotationSupport ;
27+ import io .jooby .internal .apt .MvcContext ;
28+ import io .jooby .internal .apt .WebRouter ;
2629import io .jooby .javadoc .JavaDocParser ;
2730import io .jooby .javadoc .MethodDoc ;
2831
@@ -347,38 +350,31 @@ private void appendCompletions(
347350 ? "io.modelcontextprotocol.spec.McpSchema.ResourceReference"
348351 : "io.modelcontextprotocol.spec.McpSchema.PromptReference" ;
349352
350- String lambda ;
353+ String invokerCall ;
351354 if (groups .containsKey (ref )) {
352355 var targetMethod = findTargetMethodName (ref );
353356 var handlerName = targetMethod + "CompletionHandler" ;
354- var operationArg = generateOperationArg (kt , "completions/" + ref , targetMethod );
355-
356- String invokeArgs =
357- isStateless ? "null, ctx, req" : "exchange, exchange.transportContext(), req" ;
358- String lambdaArgs = isStateless ? "ctx, req" : "exchange, req" ;
359-
360- lambda =
361- kt
362- ? "{ "
363- + lambdaArgs
364- + " -> invoker.invoke("
365- + operationArg
366- + ") { this."
367- + handlerName
368- + "("
369- + invokeArgs
370- + ") } }"
371- : "("
372- + lambdaArgs
373- + ") -> invoker.invoke("
374- + operationArg
375- + ", () -> this."
376- + handlerName
377- + "("
378- + invokeArgs
379- + "))" ;
357+ var targetClass = getTargetType ().toString ();
358+
359+ String adapterMethod = isStateless ? "asStatelessCompletionHandler" : "asCompletionHandler" ;
360+ String handlerRef = "this::" + handlerName ;
361+ String operationId = "completions/" + ref ; // <--- ADD THIS
362+
363+ // Update the string builder to include operationId:
364+ invokerCall =
365+ "invoker."
366+ + adapterMethod
367+ + "("
368+ + string (operationId )
369+ + ", "
370+ + string (targetClass )
371+ + ", "
372+ + string (targetMethod )
373+ + ", "
374+ + handlerRef
375+ + ")" ;
380376 } else {
381- lambda =
377+ invokerCall =
382378 kt
383379 ? "{ _, _ -> io.jooby.mcp.McpResult(this.json).toCompleteResult(emptyList<Any>()) }"
384380 : "(exchange, req) -> new"
@@ -398,7 +394,7 @@ private void appendCompletions(
398394 "(" ,
399395 string (ref ),
400396 "), " ,
401- lambda ,
397+ invokerCall ,
402398 "))" ));
403399 } else {
404400 buffer .append (
@@ -411,7 +407,7 @@ private void appendCompletions(
411407 "(" ,
412408 string (ref ),
413409 "), " ,
414- lambda ,
410+ invokerCall ,
415411 "))" ,
416412 semicolon (kt )));
417413 }
@@ -482,32 +478,30 @@ private void appendInstall(
482478 var mcpType = getMcpRouteType (route );
483479 if (mcpType .isEmpty ()) continue ;
484480
485- var operationArg = generateOperationArg (kt , mcpType + "/" + mcpName , methodName );
486-
487- String invokeArgs =
488- isStateless ? "null, ctx, req" : "exchange, exchange.transportContext(), req" ;
489- String lambdaArgs = isStateless ? "ctx, req" : "exchange, req" ;
490-
491- var lambda =
492- kt
493- ? "{ "
494- + lambdaArgs
495- + " -> invoker.invoke("
496- + operationArg
497- + ") { this."
498- + methodName
499- + "("
500- + invokeArgs
501- + ") } }"
502- : "("
503- + lambdaArgs
504- + ") -> invoker.invoke("
505- + operationArg
506- + ", () -> this."
507- + methodName
508- + "("
509- + invokeArgs
510- + "))" ;
481+ String adapterMethod = "" ;
482+ if (route .isMcpTool ())
483+ adapterMethod = isStateless ? "asStatelessToolHandler" : "asToolHandler" ;
484+ else if (route .isMcpPrompt ())
485+ adapterMethod = isStateless ? "asStatelessPromptHandler" : "asPromptHandler" ;
486+ else if (route .isMcpResource () || route .isMcpResourceTemplate ())
487+ adapterMethod = isStateless ? "asStatelessResourceHandler" : "asResourceHandler" ;
488+
489+ String handlerRef = "this::" + methodName ;
490+ String targetClass = getTargetType ().toString ();
491+ String operationId = mcpType + "/" + mcpName ;
492+ String invokerCall =
493+ of (
494+ "invoker." ,
495+ adapterMethod ,
496+ "(" ,
497+ string (operationId ),
498+ "," ,
499+ string (targetClass ),
500+ ", " ,
501+ string (methodName ),
502+ ", " ,
503+ handlerRef ,
504+ ")" );
511505
512506 String prefix = kt ? "" : "new " ;
513507 String serverMethod = "io.modelcontextprotocol.server." + featuresClass + "." ;
@@ -522,7 +516,7 @@ private void appendInstall(
522516 "SyncToolSpecification(" ,
523517 methodName ,
524518 "ToolSpec(schemaGenerator), " ,
525- lambda ,
519+ invokerCall ,
526520 "))" ,
527521 semicolon (kt )));
528522 } else if (route .isMcpPrompt ()) {
@@ -535,7 +529,7 @@ private void appendInstall(
535529 "SyncPromptSpecification(" ,
536530 methodName ,
537531 "PromptSpec(), " ,
538- lambda ,
532+ invokerCall ,
539533 "))" ,
540534 semicolon (kt )));
541535 } else if (route .isMcpResource () || route .isMcpResourceTemplate ()) {
@@ -556,7 +550,7 @@ private void appendInstall(
556550 methodName ,
557551 defMethod ,
558552 ", " ,
559- lambda ,
553+ invokerCall ,
560554 "))" ,
561555 semicolon (kt )));
562556 }
@@ -581,7 +575,7 @@ private void appendCompletionHandlers(
581575 + " io.modelcontextprotocol.spec.McpSchema.CompleteRequest):"
582576 + " io.modelcontextprotocol.spec.McpSchema.CompleteResult {" ));
583577 buffer .append (
584- statement (indent (6 ), "val ctx = transportContext.get(\" CTX\" ) as io.jooby.Context" ));
578+ statement (indent (6 ), "val ctx = transportContext.get(\" CTX\" ) as? io.jooby.Context" ));
585579 buffer .append (statement (indent (6 ), "val c = this.factory.apply(ctx)" ));
586580 buffer .append (statement (indent (6 ), "val targetArg = req.argument()?.name() ?: \" \" " ));
587581 buffer .append (statement (indent (6 ), "val typedValue = req.argument()?.value() ?: \" \" " ));
@@ -686,18 +680,6 @@ else if (type.equals("io.modelcontextprotocol.common.McpTransportContext"))
686680 }
687681 }
688682
689- private String generateOperationArg (boolean kt , String operationId , String targetMethod ) {
690- String prefix = kt ? "" : "new " ;
691- return prefix
692- + "io.jooby.mcp.McpOperation("
693- + string (operationId )
694- + ", "
695- + string (getTargetType ().toString ())
696- + ", "
697- + string (targetMethod )
698- + ")" ;
699- }
700-
701683 public Optional <MethodDoc > getMethodDoc (String methodName , List <String > types ) {
702684 return javadoc .parse (getTargetType ().toString ()).flatMap (it -> it .getMethod (methodName , types ));
703685 }
0 commit comments