Skip to content

Add Primitive repository APIs (ResourceRepository, ToolsRepository, ... ) #578

Description

@sdelamo

Currently, McpStatelessAsyncServer and McpAsyncServer save resources, tools, prompts, and completionswithCopyOnWriteArrayList`.

https://github.com/modelcontextprotocol/java-sdk/blob/main/mcp-core/src/main/java/io/modelcontextprotocol/server/McpStatelessAsyncServer.java#L60-L68

I want to provide an API (for example, ResourcesRepository) that, by default, uses the current in-memory strategy but can be replaced, for example, by a dynamic implementation that returns a different list of resources depending on the currently authenticated user.

Image

Activity

  1. chemicL commented on Oct 2, 2025

    @chemicL
    Member

    I believe the repository-oriented design should be able to address the needs expressed in #593 (comment). If we can unify these ideas in this issue we could close #593 and make progress designing a comprehensive solution with this issue. @tzolov, @sdelamo WDYT?

  2. hbsjz-swl commented on Oct 9, 2025

    @hbsjz-swl
    Contributor

    @sdelamo @chemicL
    hi, I think these problems are essentially the same type of problems that the org.springframework.ai.mcp.McpToolUtils.prefixedToolName(String prefix, String title, String toolName) method in the SpringAI project solves. They are all about how to manage tools by grouping them so that the MCP client can more intelligently heuristically call tools among a large number of tools, first calling the tool group and then calling the specific tool.
    https://github.com/spring-projects/spring-ai/blob/main/mcp/common/src/main/java/org/springframework/ai/mcp/McpToolUtils.java

  3. ultramancode commented on Jan 13, 2026

    @ultramancode

    Hi! @sdelamo @chemicL Could you take a look at my pr (#747)? I think it’s closely related to this issue, and I’d really appreciate any feedback.

  4. added
    enhancementNew feature or request
    P3Nice to haves, rare edge cases
    on Feb 18, 2026
  5. chemicL commented on Feb 20, 2026

    @chemicL
    Member

    More requirements are described in #746, which I closed as a duplicate but should keep in mind when implementing this.

  6. bcersows commented on Feb 23, 2026

    @bcersows

    As all the related tickets seem to have been closed, I want to throw in one requirement that we at our company need; we've implemented a dynamic tool approach depending on some header field in the tools/list request. So a repository-based approach would be great, but also on a per-request basis.

  7. ultramancode commented on Feb 24, 2026

    @ultramancode

    @chemicL
    Hi! After #746 was closed as a duplicate of this issue, I’d like to confirm the direction.
    I put together PR #747 as an early attempt at the ToolsRepository portion (repository abstraction, per-request filtering, and cursor pagination).

    Does this approach align with the direction you’re aiming for here? If not, I’m happy to adjust it accordingly.
    If it does, I’d be glad to help with the remaining work via follow-up PRs.

  8. tim-schneider-elo commented on Mar 23, 2026

    @tim-schneider-elo

    Any updates on when this feature will be addressed and will be available?

  9. 6 remaining items

  10. tim-schneider-elo commented on Jun 18, 2026

    @tim-schneider-elo

    Now, that 2.0.0 is released, can this be prioritized? @chemicL

  11. Kehrlann commented on Jun 19, 2026

    @Kehrlann
    Contributor

    We want to introduce this feature in a non-breaking, 2.1 minor. Users may opt-in to the new feature, possibly replacing the old way of doing things with repositories (rather than having both at runtime); but they may stay on the current implementation as well.

    With that said,

    State of the project and upcoming changes

    Here's our thinking right now:

    We have 6 variants for the servers, [Sync | Async] * [SSE | Streamable | Stateless]. We can dial it down to 4 by deciding not to implement SSE. In our case, "Stateless" means "Streamable but only the request-response part, no sessions or streaming". They were all implemented to match the API of the STDIO client.

    If we were to implement a full API, we'd need 4 interfaces for repositories for Tools, 4 for Resources, 4 for Prompts and possibly Completions.

    On top of that, there is a new spec coming (tentatively) end of July, introducing a new definition of statelessness SEP-2575: Make MCP Stateless. It relies on SEP-2322: Multi Round-Trip Requests, which changes return types from e.g. CallToolResult to CallToolResult | InputRequiredResult. We haven't started on the implementation yet but this likely means we'll have to introduce a new abstraction, separate from the existing stuff, leading to more definitions for Tools and, possibly ratcheting up the number of repositories even further.

    In 3.0, we would like to move away from the Sync / Async dichotomy, and introduce a new, virtual-thread friendly, unified API (see #778).

    So all these new repositories we discussed above, we'll have to maintain, even though we will be moving away from them in the mid term.

    Implementation strategy

    We do want this feature. We're planning on experimenting with it, releasing some milestones so users can start playing with it and we get feedback.

    Here's what we are thinking thus far:

    1. Implement only the sync variant (no Mono<...>), as it matches our long-term roadmap
      1. This may be an issue, as the current architecture for sync is an async core, which must NOT perform blocking work. Typically achieved by defensively wrapping blocking calls with .subscribeOn(Schedulers.boundedElastic())), but this means introducing some undesirable overhead.
      2. Consider making the sync implementations pluggable into async servers using an adapter
    2. Start with the stateless variant, i.e. a repository for (CallToolRequest, McpTransportContext) -> CallToolResult
      1. This means we would not support sampling, logging (going away in the next spec), as well as elicitation and progress.
      2. These can be plugged into streamable, but you'd lose the benefit
  12. ultramancode commented on Jun 20, 2026

    @ultramancode

    Thanks for laying this out, @Kehrlann. This direction makes sense to me.

    My understanding is that the first implementation should move toward a sync/stateless repository contract, without Mono<...> in the public repository API. It should receive the MCP request together with request context, likely through McpTransportContext, and avoid exposing a server exchange object in the repository contract.

    That would keep the repository path focused on request-context based dynamic discovery and execution, while users who need exchange-dependent flows such as sampling, elicitation, logging, or progress can continue using the existing exchange-based handler API.

    If this direction sounds right, I’d like to rework the repository proposal across tools, resources, prompts, and completions using a consistent sync/stateless repository shape.

    I’d also be happy to help with follow-up work around exchange-dependent or multi-round-trip behavior once the Java SDK model for that settles.

    Would you prefer me to rework #953 directly, or open a fresh smaller PR for that narrower shape?

  13. ultramancode commented on Jun 23, 2026

    @ultramancode

    I opened #1034 as a smaller proposal to make the sync/stateless repository direction more concrete and easier to review.

    When you have a chance, I’d appreciate a check on whether this matches the intended direction.

  14. SharathPrakash commented on Jul 21, 2026

    @SharathPrakash

    @Kehrlann Any release date is planned for this feature?

  15. Kehrlann commented on Aug 26, 2026

    @Kehrlann
    Contributor

    Sorry for the long response time - summer time in Europe is not very active.

    We have decided to NOT implement full repositories in the 2025-11-25 iteration of the spec (current 2.x line of the SDK), and instead focus on implementing from first principles in 3.x (2026-07-28 sec).

    A full implementation in the current state (2.x, 2025-11-25) spec would be complex, as it'd need to target [sync | async] x [stateful | stateless] = 4 variants. We'd need to address the bridges between sync and async, make it coexist with current server's tool management (array list of tools, .addTool method). We have originally floated the idea of doing only stateless / sync ; but that would leave the 3 other variants stranded.
    3.x will bring breaking changes and new APIs, and we will introduce repositories as a first class primitive in that version.

    In the meantime, to address the most pressing need, we'll introduce tools/list filtering in 2.1.0 (see #1108)

  16. ultramancode commented on Aug 26, 2026

    @ultramancode

    I’ve closed #953 and #1034 in line with the updated direction here.
    If there’s a useful piece I can contribute to when the 3.x repository work starts, I’d be glad to help.

  17. ujibang commented on Sep 8, 2026

    @ujibang

    A concrete case for the per-client half of this, from a server built on the SDK.

    RESTHeart derives MCP resources from MongoDB collections and GraphQL applications. Two things we wanted to do turned out not to be expressible against a server-wide registry:

    A schema that differs per caller. We added a @visible(roles: [...]) directive to our GraphQL apps: a field the caller's roles do not name is removed from the schema they see, so introspection does not reveal it exists. We wanted the resource description in the catalog to carry the same role-filtered schema. It cannot: resources is one registry for the whole server, so the description we register is the one every client gets. Publishing the unfiltered schema there would have revealed exactly what the directive removes, so we left the schema out of the catalog entirely and point clients at GraphQL introspection instead — which does answer per caller.

    A catalog that reflects what the caller may read. Reads are authorized per principal (a resource read is checked as the equivalent REST GET), so a client can see catalog entries it will be refused. That is a deliberate trade on our side, not a complaint — but with a repository API it would be a choice rather than a constraint.

    Both would be covered by the interface proposed here, provided the lookup receives the exchange (i.e. the authenticated identity), not just a key.

    One adjacent note, since it comes up in the same place: #746 was closed as a duplicate of this issue, but it also asked for cursor-based pagination, which this issue does not cover — the server-side list handlers ignore cursor and never set nextCursor. Filed separately as #1128, so it does not get lost again.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions