armeria icon indicating copy to clipboard operation
armeria copied to clipboard

Support for MCP (Model Context Protocol)

Open Dogacel opened this issue 9 months ago • 11 comments

From, https://modelcontextprotocol.io/introduction

MCP is an open protocol that standardizes how applications provide context to LLMs. Think of MCP like a USB-C port for AI applications. Just as USB-C provides a standardized way to connect your devices to various peripherals and accessories, MCP provides a standardized way to connect AI models to different data sources and tools.

This protocol is relatively new and there is a lot of hype around it. I think there is value in creating an experimental module to add MCP support in Armeria. I think it can boost the project's popularity as well.

For example, Http4k added it recently: https://www.http4k.org/news/http4k_mcp_has_landed/ And Spring boot: https://docs.spring.io/spring-ai-mcp/reference/overview.html

As current implementation of MCP uses SSE, it should be relatively easy to abstract out the MCP protocol as a separate HttpService.

Dogacel avatar Mar 28 '25 18:03 Dogacel

If you find value in this project, I am willing to pair-up to help. I have recently went ahead and helped a relatively new CLI tools for interacting MCPs to add HTTP support, so I am kinda familiar with the protocol now.

Dogacel avatar Mar 28 '25 18:03 Dogacel

I am also interested in MCP. It would be awesome if we could support MCP in Armeria.

ikhoon avatar Mar 31 '25 13:03 ikhoon

I am also interested in MCP. It would be awesome if we could support MCP in Armeria.

Here are some suggestions.


public class MyMcpService implements McpService {
  @Override
  public Set<McpTool> tools() { ... }

  @Override
  public Set<McpResource> resources() { ... }

  @Override
  public Set<McpPrompt> prompts() { ... }
}

public class TellAgeTool implements McpTool {
  @Override public String name = "tell-age";
  @Override public String description = "Tells the age of a person";

  @Override
  public McpParams params() {
     return McpParams.of(
        McpParams.string("name"),
        McpParams.int("age").withDescription("Age of the person"),
     );
  }

  @Override
  public ToolResponse handle(McpParams params) {
    String name = params.string("name");
    Int age = params.int("age").orDefault(0);
    
    return ToolResponse.ofText("Hello " + name ", you are " + age + " years old.");
  }
}

public class PersonResource implements McpResource {
  @Override public String name = "person";
  @Override public String uri = "uri://person";

  @Override
  public ResourceResponse handle(params: McpParams) {
    URI uri = params.uri("uri");
     
    return ResourceResponse.ofImage(Images.get(uri).toBase64());
  } 
}

public class SamplePrompt implements McpPrompt {
   @Override public String name = "person";

   @Override
   public ResourceResponse handle(params: McpParams) {
     URI uri = params.uri("uri");
       
     return ResourceResponse.ofImage(Images.get(uri).toBase64());
   } 
}

sb.serviceUnder("/mcp", MyMcpService())

For registering the entities, we can also use annotations,


@McpService
public class MyMcpService {

  // Auto-register schema from parameters
  @Tool(name = "tellAge", description = "...")
  public tellAge(
     String name,
     Int age,
  ) ToolResponse {
     return ToolResponse.ofText("Hello " + name ", you are " + age + " years old.);
  }
  
  @Resource(uri = "file://person", name = "person", description = "...")
  public person(ResourceParams params) ResourceResponse {
    return ResourceResponse.ofImage(Images.get(params.uri).toBase64());
  }


  @Prompt()
  public prompt(
    String code
  ) PromptResponse {
     return PromptResponse.ofResource("uri://...");
  }
}

sb.mcpService("/mcp", MyMcpService())
// Or
sb.annotatedService("/mcp", MyMcpService())

Dogacel avatar Mar 31 '25 14:03 Dogacel

I would like this, even though it is an interesting challenge for trace propagation. Suggest we support stdio mode as well, even though that isn't typical for armeria. Rationale is well engineered i/o is a win even if using stdio.

codefromthecrypt avatar Apr 09 '25 02:04 codefromthecrypt

Furthermore it can be extended to Google's recently released protocol A2A

https://github.com/google/A2A

Dogacel avatar Apr 12 '25 18:04 Dogacel

Should we implement JSON RPC first to implement MCP?

trustin avatar Apr 16 '25 05:04 trustin

sg. Also if you want a reference, this uses stdio in spring-ai. I think the integrations could be far cleaner in armeria https://github.com/elastic/observability-examples/pull/61

codefromthecrypt avatar Apr 16 '25 05:04 codefromthecrypt

also here's a nice read of the latest non-stdio protocol "streamable" https://www.claudemcp.com/blog/mcp-streamable-http

codefromthecrypt avatar Apr 16 '25 05:04 codefromthecrypt

Should we implement JSON RPC first to implement MCP?

Yes, I think it should be the first step. Should we create a separate issue for that @trustin ?

Dogacel avatar Apr 16 '25 15:04 Dogacel

Hi @Dogacel , I'm interested in the JSON RPC implementation mentioned in that issue, if you don't mind, would you mind if I work on it?

patcher454 avatar Apr 24 '25 05:04 patcher454

fyi I proposed a clarification on MCP spec about how to encode trace identifiers in MCP's JSON-RPC requests (which I think would end up implemented here decoupled from MCP) https://github.com/modelcontextprotocol/modelcontextprotocol/pull/414 cc also also other armeria tracing friends @anuraaga and @kojilin

codefromthecrypt avatar Apr 27 '25 22:04 codefromthecrypt