Springing into AI - Part 12: MCP Server - Resources Playground
Project
For a sample playground, we being the owners of Hobbits Inc will be providing resource based information from our Gen AI application about hobbits. In this dummy playground, we will be showcasing demonstration of static and dynamic resources that we in ideal world would be exposing to the MCP Client or LLM. The static resources will list the resources we expose from our file path. The dynamic resource will provide hobbit requested information, be it their profile description or their mugshot. MCP Inspector will be used as a testing tool for validating our sanity of the project. Some of the other capabilities would also include auto completing. An architecture overview is shown below:
Setup
Our project setup encompasses of the following setup:
- Java: 17
- Spring AI: 1.1.2
- Spring Boot: 4.0.3
- Testing tool: MCP Inspector
- Source code: MCP Server Resources can be viewed here
- Project Demo: Youtube (MCP Server - Resources) here
Demo Screenshots
List, Read Resource(s)
The figure below reflects the result of "resources/list" followed by "resources/read" mcp server commands for static resources.
List, Read Resource Template(s)
The figure below reflects the result of "resources/template/list" followed by "completion/complete" (for auto completion) and "resources/read" mcp server commands for dynamic resources.
NOTE: If you trying out the mugshots as well, it will be displayed in Base64 encoded format. To view the image you would have to copy the content and then decode the image. This you can then view using an online tool such as this
Code Walkthrough
Configuration
1 2 3 4 5 6 7 8 9 10 11 | spring: ai: mcp: server: stdio: false enabled: true name: vikram-mcp-server version: 1.0.0 protocol: STREAMABLE streamable-http: mcp-endpoint: /mcp |
- Line 5: This indicates that we wont be using "stdio" for transport as we have external MCP server
- Line 6: Enables the MCP server
- Line 7: Name of our MCP server
- Line 8: Version of our MCP server
- Line 9: Of the three protocols, we are using "STREAMBLE" transport protocol
- Line 10-11: The endpoint that we will use to connect to the MCP Server
Static Resources Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | @McpResource( name = "Company Resources", description = "Contains all of the companies static files", uri = "company://resources" ) public McpSchema.ReadResourceResult loadAllCompanyResources(final McpMeta mcpMeta) throws IOException { List<McpSchema.ResourceContents> resourceContentList = new ArrayList<>(); // business logic to get files from path if (mimeType.equals(MediaType.APPLICATION_PDF_VALUE)) { McpSchema.BlobResourceContents resource; try { resource = new McpSchema.BlobResourceContents( "file://" + currentFile.getName(), mimeType, Base64.getEncoder().encodeToString(Files.readAllBytes(currentFile.toPath()))); } catch (IOException e) { throw new RuntimeException(e); } resourceContentList.add(resource); } else { McpSchema.TextResourceContents resource; try { resource = new McpSchema.TextResourceContents( "file://" + currentFile.getName(), mimeType, Files.readString(currentFile.toPath())); } catch (IOException e) { throw new RuntimeException(e); } resourceContentList.add(resource); } } |
From the code above:
- Line 1: Annotation used to declare a MCP resource to the server
- Line 2-3: Name and description that would be used by the client to identify the resource
- Line 4: URI indicator for the resource
- Line 6: McpMeta class holding the metadata that maybe required to be operated upon if desired
- Line 9: ResourceContents interface that has two forms namely Blob and Text Resource Content
- Line 16: For binary resources we create a BlobResourceContent that is Base64 encoded
- Line 28: For normal resources we create a TextResourceContent
Resource Template Code
This contains dynamic functionality with auto-complete for a particular information. Should the name exist from what we can offer, we then show the various hobbit names. Once the end user choses which of the hobbit profiles they want to view, we simply load the particular resource be it their profile or mugshot depending upon the selected resource.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | private static List<String> MIDDLE_EARTH_USERS = List.of("frodo", "samwise", "merry", "pippin"); @Value("classpath:/profiles") private Resource userProfileResource; @McpComplete( uri = "hobbit-profile://profile/{user}" ) public List<String> searchForHobbitsWithProfiles( final McpSchema.CompleteRequest.CompleteArgument argument) { if ("user".equals(argument.name())) { return MIDDLE_EARTH_USERS.stream() .filter(name -> name.toLowerCase().startsWith(argument.value().toLowerCase())) .toList(); } return List.of(); } |
In above:
- Line 1 - 4: Creates a dummy list of our four hobbits that we would be exposing for resources
- Line 6 - 7: Spring resource loading from our file path
- Line 9: Annotation used to indicate this is an auto complete feature for user to search from
- Line 10: URI resource identifier with user as the resource template parameter
- Line 13: CompleteArgument class from Spring AI that is used to obtain the user inputs
- Remainder: business logic to search and filter hobbits from list of available hobbits
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | @McpResource( name = "MiddleEarth Hobbit Profiles", uri = "hobbit-profile://profile/{user}", description = "Provides profiles for main hobbits from lord of the rings" ) public McpSchema.ReadResourceResult hobbitProfiles( final McpSchema.ReadResourceRequest request, final String user) throws IOException { File currentDirectory = new File(userProfileResource.getURI()); File[] files = currentDirectory.listFiles(); for (File file : files) { if (file.getName().equals(user + ".txt")) { try { byte[] fileContent = Files.readAllBytes(Path.of(file.getPath())); return new McpSchema.ReadResourceResult( List.of(new McpSchema.TextResourceContents(request.uri(), "text/plain", new String(fileContent, StandardCharsets.UTF_8)))); } catch (IOException e) { throw new RuntimeException(e); } } } throw new FileNotFoundException("No profile found for user : %s".formatted(user)); } |
In above, we receive the selected hobbit, the end user wants to view the profile of. From a business logic point of view, we simply going through the files in the particular path, and then load the file for that selected hobbit and return the ResourceContent be it Text or Blob. For simplicity all profiles are text based while mugshots are binary images. NOTE the above is the logic for loading profiles. The same principles apply for mugshot loading and its auto-complete. From walkthrough point of view:
- Line 1: Spring AI annotation used to mark this as an exposed MCP resource
- Line 2 - 4: URI, name and description to indicate to client about the resource information
- Line 7: The selected ResourceRequest containing information about the selected resource such as URI
- Line 8: The selected hobbit name from the auto-complete that end user wants to view profile of
- Line 18: ReadResourceResult containing the type of resource content, be it Text or Blob
- Remainder: business logic pertaining to loading information from the pre-configured path, and the end user selected hobbit. In this particular case their hobbit profile.
.jpg)


Comments
Post a Comment