Spring AI MCP: Server and Client Example

Build a Spring AI MCP server with @McpTool, @McpResource and @McpPrompt, test it with MCP Inspector, and call its tools from a Spring AI MCP client.

Left box Host: orderdesk-mcp-client on port 8081 with the chat model qwen2.5:3b in Ollama, a ChatClient with the tool-call loop and the MCP client (ToolCallbackProvider, connection orderdesk). Under the text "JSON-RPC over Streamable HTTP", an arrow labelled tools/call goes to the right box Server: OrderDesk on port 8080, endpoint /mcp, and an arrow labelled result comes back. The server box holds tools get_order_status and list_orders, the resource template policy://{topic} and the prompt support_reply. Below the server: the orders table in HSQLDB and the policies *.md files. Below the host: the store manager asks for the status of ORD-1042

Model Context Protocol (MCP) lets an AI application use tools and data provided by another application. Instead of copying order lookup code into every assistant, we can expose it once through an MCP server and let compatible clients call it.

In this article, we add an MCP server to OrderDesk, our Spring Boot support application. Then we build a separate Spring Boot client that uses a local chat model to answer questions with OrderDesk’s order data. Along the way, we explain tool discovery, remote tool execution, resources, prompts, and the security checks needed before deployment.

1. Why Use MCP?

In the tool calling article, OrderDesk lets its chat model request an order lookup. Spring AI executes the Java method and returns the result to the model, which writes the customer’s answer. This works well when the assistant and the tool belong to the same application.

Suppose a store manager’s assistant needs the same lookup. Copying the Java code would duplicate business logic and require another application to access the database. It also would not help a desktop assistant or IDE integration written in another language.

MCP provides a standard connection between these applications. OrderDesk keeps ownership of its data and business logic, while authorized MCP clients discover and call the capabilities it exposes. The consuming application does not need to know how OrderDesk stores its orders.

1.1. Host, Client, and Server

The MCP architecture page of the specification names three roles:

  1. The host is the AI app the user talks to. It calls a model.
  2. The client is a connector inside the host. Each client keeps one connection to one server. So a host that uses three servers has three clients.
  3. The server is the app that owns the data and the actions. Here, it is OrderDesk.
Left box Host, the AI app the user talks to, which calls a model. Inside it, a box MCP client, one connection to one server. An arrow labelled JSON-RPC messages goes from the MCP client to the right box MCP server, the app that owns the data, offers tools, resources and prompts, and needs no model. A dashed arrow comes back from the server to the client. Below the boxes: one host can have many clients, one for each server it uses.
The host is the AI app that calls a model; the MCP client inside it sends JSON-RPC messages to the MCP server, which owns the data and needs no model.

The important distinction is that the host uses the model; the MCP server provides capabilities. OrderDesk does not need a model to serve an order lookup. It only needs to handle requests such as “list the available tools” and “execute this tool with these arguments.”

1.2. Tools, Resources, and Prompts

A server can offer three kinds of things. The server features page of the specification describes each one:

  1. Tools are actions the model can call, such as “get the status of order ORD-1042”.
  2. Resources are data that the host can read by a URI, such as the returns policy at policy://returns.
  3. Prompts are ready-made prompt templates with arguments, such as “reply to a customer about one order”. The host shows them to the user, and the user picks one.

We start with tools, because our client app needs only tools. Section 5 adds a resource and a prompt.

1.3. How Clients and Servers Communicate

MCP uses JSON-RPC 2.0 to represent messages. A request identifies the operation in its method, supplies arguments in params, and includes an id that links it to the response. For example, this tools/call request asks OrderDesk to look up an order:

{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_order_status","arguments":{"orderId":"ORD-1042"}}}

The server answers with the tool’s result as text, and the host gives this text to its model. We’ll see a full result in Section 3.3. Other methods, such as tools/list, have the same shape.

A transport carries these messages between applications. This example uses Streamable HTTP: the client sends requests to /mcp, and the server can respond with JSON or a stream of server-sent events. STDIO is another transport, commonly used when a desktop host launches a local server process. The base protocol specification explains the transport details.

You don’t write any of this JSON yourself. Spring AI writes and reads it on both sides.

2. Overview of the Example Application

OrderDesk is the support assistant of a demo online store, and every article in this series grows this same app. It uses Spring Boot 4.1, Spring AI 2.0.1, and Java 21, and it runs a local model, qwen2.5:3b, in Ollama. If you need to install Ollama and create the project, follow Getting Started with Spring AI. OrderDesk runs on the default port 8080.

In this article, we work with two apps:

  1. OrderDesk becomes the MCP server. Its server code is in the com.howtodoinjava.orderdesk.mcp package.
  2. The client app (orderdesk-mcp-client) is a new Spring Boot app on port 8081. It is the MCP host: the store manager asks it questions, and its model calls OrderDesk’s tools.
Left box Host: orderdesk-mcp-client on port 8081. Inside it, from top to bottom: Chat model qwen2.5:3b in Ollama, ChatClient with the tool-call loop, and MCP client with the ToolCallbackProvider and the connection orderdesk. Arrows go down from the chat model to ChatClient and from ChatClient to the MCP client. Under the text "JSON-RPC over Streamable HTTP", an arrow labelled tools/call goes from the MCP client to the right box Server: OrderDesk on port 8080, endpoint /mcp, and an arrow labelled result comes back. The server box holds tools get_order_status and list_orders, the resource template policy://{topic} and the prompt support_reply. Below the server: the orders table in HSQLDB and the policies *.md files. Below the host: the store manager asks for the status of ORD-1042
The two apps in this article: the client app on port 8081 holds the chat model, the ChatClient and the MCP client, and the MCP client sends tools/call over Streamable HTTP to the /mcp endpoint of OrderDesk on port 8080.

For an order question, the model requests a tool, ChatClient delegates execution to the MCP client, and the client sends tools/call to OrderDesk. The order result returns to the model, which uses it to compose the answer. The database remains behind the OrderDesk server.

The MCP server reuses OrderDesk code from earlier articles. To follow along, clone the orderdesk folder of the repo, or copy these files into your project:

  1. OrderTools.java reads the orders table with JdbcClient. Its public methods getOrderStatus(String) and listOrders(String) return the public record OrderInfo(orderId, customerName, city, status, totalInr, placedOn, deliveredOn). For an unknown id, getOrderStatus() throws an IllegalArgumentException with the message “No order with id …”. The tool calling article explains this class.
  2. schema.sql and data.sql create the orders table with 12 sample orders in HSQLDB, a small Java database. OrderDesk’s pom.xml and application.properties already have the HSQLDB setup.
  3. The policies folder has four Markdown files with the store’s rules: returns.md, shipping.md, warranty.md and payments.md. Section 5 reads them.

3. Building the MCP Server

The server needs two additions: a starter that exposes the MCP endpoint and a Spring bean whose annotated methods provide the tools. Existing order lookup logic stays in OrderTools.

3.1. Add the Server Starter

OrderDesk is a Spring MVC app, so we add the WebMVC server starter. We don’t write a version, because the Spring AI BOM (bill of materials) from Getting Started sets it:

<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>

Then we describe the server in application.properties:

spring.ai.mcp.server.name=orderdesk
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.protocol=streamable
spring.ai.mcp.server.instructions=Look up OrderDesk orders and read the store's policies.

When a client connects, the server shares its name and version with it. The protocol=streamable property selects the Streamable HTTP transport, while the instructions tell the client’s model what the server is intended to do. Spring AI documents all four properties on its Streamable-HTTP server page. Unless you configure a different path, the MCP endpoint defaults to /mcp.

3.2. Turn Methods into MCP Tools

Spring AI scans Spring beans for MCP annotations, as the server annotations page describes. Each method with @McpTool becomes one MCP tool. We put the class in a sub-package of com.howtodoinjava.orderdesk, so that Spring Boot finds it.

Here’s the part of OrderDeskMcpServer with the two tools. The full class on GitHub also has a resource and a prompt, shown in Section 5:

package com.howtodoinjava.orderdesk.mcp;

import java.util.List;

import org.springframework.ai.mcp.annotation.McpTool;
import org.springframework.ai.mcp.annotation.McpToolParam;
import org.springframework.stereotype.Component;

import com.howtodoinjava.orderdesk.tools.OrderTools;
import com.howtodoinjava.orderdesk.tools.OrderTools.OrderInfo;

// Exposes OrderDesk's order lookups, policies and a reply prompt over MCP
@Component
public class OrderDeskMcpServer {

    private final OrderTools orderTools;

    OrderDeskMcpServer(OrderTools orderTools) {
        this.orderTools = orderTools;
    }

    @McpTool(name = "get_order_status",
        description = "Get the status, city, total and dates of one order by its id",
        annotations = @McpTool.McpAnnotations(readOnlyHint = true, destructiveHint = false,
            idempotentHint = true, openWorldHint = false))
    public OrderInfo getOrderStatus(
            @McpToolParam(description = "Order id, for example ORD-1042") String orderId) {
        return orderTools.getOrderStatus(orderId);
    }

    @McpTool(name = "list_orders",
        description = "List all orders of one customer by the customer's full name",
        annotations = @McpTool.McpAnnotations(readOnlyHint = true, destructiveHint = false,
            idempotentHint = true, openWorldHint = false))
    public List<OrderInfo> listOrders(
            @McpToolParam(description = "Customer's full name, for example Priya Sharma")
            String customerName) {
        return orderTools.listOrders(customerName);
    }
}

This class acts as an MCP interface to the existing application. It delegates order lookups to OrderTools, so local tool calling and remote MCP access share the same business logic.

The MCP tool name is get_order_status because @McpTool sets name. In the tool calling article, the methods of OrderTools carry @Tool without a name, so there the model saw the method name getOrderStatus.

Spring AI derives the tool’s input schema from its method parameters and @McpToolParam annotations. The model uses the tool name, description, and schema to decide when to call it and which arguments to provide. Describe the expected order ID or customer name clearly.

McpAnnotations provide hints about tool behavior. Here, the hints describe tools that read the application’s own data without changing it. Hosts may use these hints when presenting tools or requesting user approval. They do not enforce access control; the server must still authenticate callers, authorize data access, and validate arguments.

3.3. Test the Server with MCP Inspector

MCP Inspector lets us check tool discovery and execution before involving a chat model. Start OrderDesk with the full server class from GitHub. The following sample startup log includes the two tools, resource template, and prompt defined in this article:

McpServerAutoConfiguration : Registered tools: 2
McpServerAutoConfiguration : Registered resource templates: 1
McpServerAutoConfiguration : Registered prompts: 1

Use MCP Inspector to send requests directly to the server. Install a Node.js version supported by Inspector, then list the tools with the following command:

npx -y @modelcontextprotocol/inspector --cli http://localhost:8080/mcp --transport http --method tools/list
{
  "tools": [
    {
      "name": "get_order_status",
      "title": "get_order_status",
      "description": "Get the status, city, total and dates of one order by its id",
      "inputSchema": {
        "type": "object",
        "properties": {
          "orderId": {
            "type": "string",
            "description": "Order id, for example ORD-1042"
          }
        },
        "required": [
          "orderId"
        ],
        "$schema": "https://json-schema.org/draft/2020-12/schema"
      },
      "annotations": {
        "title": "",
        "readOnlyHint": true,
        "destructiveHint": false,
        "idempotentHint": true,
        "openWorldHint": false
      }
    }
  ]
}

The output above shows only get_order_status; list_orders is omitted for brevity. The tool definition contains the name, description, and input schema that a host can make available to its model.

Next, we call the tool for an existing order and for a missing one:

npx -y @modelcontextprotocol/inspector --cli http://localhost:8080/mcp --transport http --method tools/call --tool-name get_order_status --tool-arg orderId=ORD-1042
npx -y @modelcontextprotocol/inspector --cli http://localhost:8080/mcp --transport http --method tools/call --tool-name get_order_status --tool-arg orderId=ORD-9999
{
  "content": [
    {
      "type": "text",
      "text": "{\"orderId\":\"ORD-1042\",\"customerName\":\"Meera Iyer\",\"city\":\"Chennai\",\"status\":\"PLACED\",\"totalInr\":12999.0,\"placedOn\":\"2026-10-16\",\"deliveredOn\":null}"
    }
  ],
  "isError": false
}
{
  "content": [
    {
      "type": "text",
      "text": "No order with id ORD-9999\nNo order with id ORD-9999"
    }
  ],
  "isError": true
}

For the existing order, the tool returns the OrderInfo record as JSON text. For the missing order, OrderTools throws an IllegalArgumentException, which appears in a tool result with isError: true. This reports a tool execution failure without stopping the server. The host can use the error result to explain that the order was not found.

Run the Inspector without –cli to make the same calls in a browser.

4. Building the MCP Client

The client application runs separately on port 8081. It uses Ollama to interpret questions and write answers, while OrderDesk provides the order data through MCP. The client needs no direct database connection.

4.1. Connect to the Server

Create a new project as in Getting Started, with the Spring Boot parent, the Spring AI BOM, and a main class. Here the main class is McpClientApplication in com.howtodoinjava.orderdesk.mcpclient, and the full pom.xml is on GitHub. The client app needs three starters:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-starter-model-ollama</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

The connection to OrderDesk is a few lines in application.properties:

spring.application.name=orderdesk-mcp-client
server.port=8081

spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.options.model=qwen2.5:3b
spring.ai.ollama.chat.options.temperature=0.2

# The OrderDesk MCP server (start OrderDesk first)
spring.ai.mcp.client.name=orderdesk-mcp-client
spring.ai.mcp.client.type=sync
spring.ai.mcp.client.streamable-http.connections.orderdesk.url=http://localhost:8080

A low temperature makes the model’s answers less random. The spring.ai.mcp.client.name property sets the name that the client app sends to the server when it connects. The type=sync property creates blocking clients, which fit a Spring MVC app. The name orderdesk in the last property is our own name for this connection. By default, the client adds the endpoint /mcp to the URL. The MCP client starter page lists all these properties.

Additional named connections let the client use more servers. In this example, the callback provider exposes the discovered tools to ChatClient, so adding servers can increase the number of tool definitions sent to the model. Register only the tools the assistant needs. The client starter documentation describes McpToolFilter for filtering individual tools.

With the default initialization behavior used here, the client connects to OrderDesk and discovers its tools during startup. Start OrderDesk first so this connection can succeed. Setting spring.ai.mcp.client.initialized=false skips automatic initialization; it does not by itself make unavailable tools usable or provide reconnection handling.

4.2. Give the Remote Tools to a ChatClient

On the client side, a remote MCP tool looks like a local tool. The starter creates a ToolCallbackProvider bean that holds the tools of every connected server, and we give it to the ChatClient:

package com.howtodoinjava.orderdesk.mcpclient;

import java.util.Arrays;
import java.util.List;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class AssistantController {

    private final ChatClient chatClient;
    private final ToolCallbackProvider mcpTools;

    // ToolCallbackProvider holds the tools of every connected MCP server
    AssistantController(ChatClient.Builder builder, ToolCallbackProvider mcpTools) {
        this.mcpTools = mcpTools;
        this.chatClient = builder
            .defaultSystem("You help a store manager. Use the tools to look up orders.")
            .defaultToolCallbacks(mcpTools)
            .build();
    }

    @GetMapping("/tools")
    List<String> tools() {
        return Arrays.stream(mcpTools.getToolCallbacks())
            .map(t -> t.getToolDefinition().name())
            .toList();
    }

    @GetMapping("/ask")
    String ask(@RequestParam String question) {
        return chatClient.prompt()
            .user(question)
            .call()
            .content();
    }
}

A tool callback adapts a remote tool to Spring AI’s tool execution interface. When the model requests it, the callback sends tools/call to OrderDesk and returns the server’s result. The /tools endpoint lists the available callback names so we can check discovery separately from a model-generated answer.

This diagram follows one question through both apps:

Sequence diagram with five lifelines: Manager (curl), Client app (ChatClient), Chat model (qwen2.5:3b), MCP client (in the client app) and OrderDesk (MCP server). Messages in order: GET /ask status of ORD-1042; question plus 2 tool definitions to the model; the model answers call get_order_status(ORD-1042); the client app runs the tool callback in the MCP client; the MCP client sends JSON-RPC tools/call to OrderDesk; OrderDesk returns the order as JSON text; the client app sends the question plus the tool result to the model; the model answers in plain words; the answer goes back to the manager
One question through both apps: the client app sends the question and the tool definitions to the model, the model asks for get_order_status, the MCP client sends tools/call to OrderDesk, and the result goes back to the model, which writes the answer.

The illustrated lookup uses two model calls: one to request the tool and another to turn its result into an answer. Spring AI manages this exchange. Other questions may need no tool call or several tool calls, so two model calls is the flow shown here, rather than a fixed rule.

4.3. Ask a Question

Start OrderDesk first, then the client app. Now we call the client app:

curl localhost:8081/tools
curl -G localhost:8081/ask --data-urlencode "question=What is the status of order ORD-1042?"
curl -G localhost:8081/ask --data-urlencode "question=Which orders does Rahul Verma have, and which are not delivered yet?"
["get_order_status","list_orders"]

The order ORD-1042 has been placed. The status is PLACED. It is from the city Chennai and the total amount is 12999.0 INR. The delivery date is not specified yet.

Rahul Verma has the following orders:

1. **Order ID: ORD-1032**
   - Status: DELIVERED
   - Total: ₹349.00
   - Placed On: 2026-10-02
   - Delivered On: 2026-10-04

2. **Order ID: ORD-1039**
   - Status: SHIPPED
   - Total: ₹3299.00
   - Placed On: 2026-10-12
   - Delivered On: Not yet delivered

None of the orders are not delivered yet as all are either DELIVERED or SHIPPED.

In the recorded example, the first answer took 26.5 seconds and the second took 41.8 seconds on a laptop CPU. These timings include model inference and are specific to that setup.

The second answer also shows why successful tool execution does not guarantee a correct response. ORD-1039 has status SHIPPED, which means it has not yet been delivered, but the model interprets it incorrectly. MCP transports the result; the model still has to understand it. A more capable model may help. For a critical status decision, a dedicated tool or deterministic application logic can return the conclusion directly.

5. Resources and Prompts

Tools execute operations, but MCP can also expose readable content and reusable prompt templates. The client built above registers tool callbacks only; resources and prompts require the host to request and use those capabilities explicitly. The full OrderDeskMcpServer class on GitHub includes one of each.

Three rows with the columns Java method, MCP capability and Client methods. @McpTool with @McpToolParam on getOrderStatus becomes tool get_order_status, listed by tools/list and run by tools/call. @McpResource on policy(topic) becomes resource template policy://{topic}, listed by resources/templates/list and read by resources/read. @McpPrompt with @McpArg on supportReply becomes prompt support_reply, listed by prompts/list and filled by prompts/get
Each Spring AI MCP annotation becomes one MCP capability with two client methods: @McpTool to tools/list and tools/call, @McpResource to resources/templates/list and resources/read, @McpPrompt to prompts/list and prompts/get.

5.1. A Resource for the Store’s Policies

A resource is data that a host can read by a URI. The host decides how to use it, for example to show it to the user or to add it to the model’s prompt. Our resource returns one policy file:

    private static final Pattern TOPIC = Pattern.compile("returns|shipping|warranty|payments");

    @McpResource(uri = "policy://{topic}", name = "OrderDesk policy",
        description = "One OrderDesk policy as Markdown: returns, shipping, warranty or payments",
        mimeType = "text/markdown")
    public String policy(String topic) throws IOException {
        if (!TOPIC.matcher(topic).matches()) {
            throw new IllegalArgumentException("Unknown policy: " + topic);
        }
        return new ClassPathResource("policies/" + topic + ".md")
            .getContentAsString(StandardCharsets.UTF_8);
    }

The URI is a template, policy://{topic}. Spring AI fills the topic parameter from the URI, so policy://returns calls policy(“returns”). A client can send any topic, so we check it against a fixed list. Without that check, a request like policy://../application would try to read a file we never meant to share. This code needs five more imports: IOException, StandardCharsets, Pattern, ClassPathResource, and McpResource.

We read the resource with MCP Inspector:

npx -y @modelcontextprotocol/inspector --cli http://localhost:8080/mcp --transport http --method resources/read --uri policy://returns

It returned the returns policy as Markdown. For policy://secrets, it returned an error with “Unknown policy: secrets”, and no file was read.

5.2. A Prompt for Customer Replies

An MCP prompt is a reusable template with arguments. A host can present it to the user, request the filled template from the server, and pass the resulting messages to a model. Fetching a prompt does not itself generate an answer. This template asks for a customer reply about an order:

    @McpPrompt(name = "support_reply",
        description = "Draft a short, polite reply to a customer about one order")
    public GetPromptResult supportReply(
            @McpArg(name = "orderId", description = "Order id", required = true) String orderId,
            @McpArg(name = "complaint", description = "What the customer wrote", required = true)
            String complaint) {
        String text = """
            You are an OrderDesk support agent. A customer wrote about order %s:
            "%s"
            First call get_order_status for this order. Then write a reply of at most
            four sentences that states the order status and the next step.
            """.formatted(orderId, complaint);
        // a String return becomes an assistant message; a prompt should be a user message
        return GetPromptResult.builder(
                List.of(new PromptMessage(Role.USER, TextContent.builder(text).build())))
            .description("Reply to a customer about one order")
            .build();
    }

@McpArg describes each argument. The new imports are McpPrompt, McpArg, GetPromptResult, PromptMessage, Role and TextContent.

The method explicitly returns a GetPromptResult containing a USER message. This makes the intended role clear: the generated text is a request for the model to answer, rather than an answer already produced by the assistant.

We called prompts/get with the order ORD-1042 and the complaint “Where is my order?”. Here is the server’s JSON-RPC response:

{"jsonrpc":"2.0","id":2,"result":{"description":"Reply to a customer about one order","messages":[{"role":"user","content":{"type":"text","text":"You are an OrderDesk support agent. A customer wrote about order ORD-1042:\n\"Where is my order?\"\nFirst call get_order_status for this order. Then write a reply of at most\nfour sentences that states the order status and the next step.\n"}}]}}

The response holds one user message with the filled text.

6. Securing the MCP Server

This example leaves /mcp unauthenticated. Anyone who can reach the endpoint can invoke its order tools, so it should not expose real customer data in this form. Before deployment, protect both the connection and the operations behind it:

  1. Protect /mcp with Spring Security, like any other endpoint. The MCP specification defines OAuth 2.1 authorization for HTTP servers. The Spring AI MCP security page points to the Spring Security integration.
  2. Authorize every operation and restrict which customer records each caller can read. Read-only tools still expose sensitive data. Tools that change data also need permission checks for those changes.
  3. Validate every argument on the server, as policy() does. The client’s model can send anything.
  4. On the client, be careful with tools from servers that other people run, because their descriptions go straight into your model’s prompt. The MCP Tool Poisoning article shows how a harmful description attacks a host.

6.1. Sessions and Running More Than One Instance

A stateful MCP server tracks client sessions using the Mcp-Session-Id header. When running multiple server instances, requests must reach an instance that can handle the session; this may require session affinity or another supported session strategy. If the application does not need stateful MCP interactions, Spring AI also provides a stateless mode:

spring.ai.mcp.server.protocol=stateless

Stateless mode removes MCP session handling, but it does not remove database state, authorization requirements, or other application-level coordination. It also limits server-to-client interactions that depend on a persistent session. The simple order lookup tools do not need those interactions. See the stateless server documentation for the supported capabilities.

7. Summary

MCP gives other applications a standard way to use OrderDesk’s capabilities while keeping order access in one place. The server exposes annotated methods; the client discovers those tools and makes them available to ChatClient. Resources provide readable data, and prompts provide reusable message templates. Before sharing the server beyond this example, add authentication, per-operation authorization, and input validation.

All articles in order: Spring AI Tutorial

Drop me your questions related to Spring AI MCP in the comments.

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.