Skip to main content

Module 2: Environment Setup

Duration: ~30 minutes | Level: Intermediate | Prerequisites: Java 21 installed, Maven or Gradle, an API key from Anthropic or OpenAI


What You'll Have After This Module

  • A Spring Boot 4.1.x project with Spring AI and MCP dependencies correctly configured
  • A working API key connected via environment variable
  • A smoke test confirming your AI provider connection works
  • Solid understanding of the dependency structure and why each piece exists

Project Setup: Get the Code

All code in this course lives in the companion repository at projects/mcp-spring-ai-course/. It is a single multi-module Maven build: a parent POM (com.themcpguy:mcp-spring-ai-course:1.0.0-SNAPSHOT, packaging pom) plus four child modules, one per code-bearing module of the course:

  • first-connection (Modules 2 and 3)
  • chatclient-demo (Module 4)
  • multi-server (Module 5)
  • order-mcp-server (Module 6)

The parent POM centralises everything the children share: it imports the spring-boot-dependencies and spring-ai-bom BOMs, sets Java 21, and pins the build plugins. The exact versions in the reference parent are:

  • Java: 21
  • Spring Boot: 4.1.0 (via the spring-boot-dependencies BOM)
  • Spring AI: 2.0.0 (via the spring-ai-bom)

Build everything from the repo root, then run an individual module:

# From the repository root (projects/mcp-spring-ai-course/)
mvn install # builds the parent and all four child modules

# Run a single module (e.g. the first-connection app from this course module)
mvn -pl first-connection spring-boot:run
Reference modules vs the standalone POMs in these lessons

The reference children are intentionally lean: they inherit versions, plugins, and the BOM imports from the parent, so their own pom.xml files list no versions on the Spring Boot or Spring AI dependencies. The lessons, by contrast, present standalone-equivalent POMs (with a spring-boot-starter-parent, an explicit spring-ai-bom import, and pinned versions) so each module reads as a complete, self-contained project. The two are equivalent in what they pull in, but a child pom.xml copied straight out of the repo will not build on its own: it needs the parent. To run reference code, build from the repo root as shown above; to follow along in your own fresh project, use the standalone POM below.


Understanding the Dependency Stack

Before touching any files, let's understand what we're pulling in and why. This saves you from cargo-culting a pom.xml and being confused when things don't work.

Spring Boot Parent

Spring Boot's parent POM manages hundreds of transitive dependency versions. When you add a Spring Boot dependency, you usually don't specify a version. The parent handles it. This is Spring's "opinionated" approach: tested version combinations, so you don't discover incompatibilities at 2am.

Spring AI BOM

Spring AI follows the same pattern. The Spring AI BOM (Bill of Materials) pins the versions of all Spring AI modules (spring-ai-model, spring-ai-starter-mcp-client, spring-ai-starter-model-anthropic, and so on) to a single tested set.

Without the BOM, you'd need to manually align versions across multiple Spring AI artifacts. With it, you import one BOM entry and everything just works.

The MCP Client Starter

spring-ai-starter-mcp-client pulls in everything needed to connect to MCP servers from a Spring Boot application:

  • The MCP client library (manages JSON-RPC connections over stdio or Streamable HTTP)
  • Spring Boot autoconfiguration that reads your application.properties and creates McpClient beans
  • SyncMcpToolCallbackProvider, the bridge that turns MCP tools into Spring AI ToolCallback objects

A Model Provider Starter

Spring AI separates the model protocol from the model provider. You add one starter for MCP (the tool protocol) and one starter for your AI provider (Anthropic, OpenAI, etc). They work together through the ChatModel abstraction.


Prerequisites

Java 21. Spring AI 2.0.x (on Spring Boot 4) requires Java 17+, but we recommend 21. It's a well-supported LTS (Java 25, released September 2025, is the current LTS) and Spring Boot 4.1.x is optimised for it. Verify with:

java -version
# Should show: openjdk 21.0.x or similar

Maven 3.9+ (or Gradle 8.x). All examples use Maven.

mvn -version
# Should show: Apache Maven 3.9.x

An API Key. For Anthropic:

  • Create an account at console.anthropic.com
  • Navigate to Settings, API Keys, Create Key
  • Copy it. You won't see it again

For OpenAI:

Store it as an environment variable. Do not put API keys in application.properties directly. That file ends up in version control.

# Add to ~/.bashrc, ~/.zshrc, or equivalent
export ANTHROPIC_API_KEY="sk-ant-api03-..."

# Or for OpenAI
export OPENAI_API_KEY="sk-proj-..."

Node.js (optional but useful). Several MCP servers (including the official filesystem reference implementation) are distributed as npm packages. Install Node.js 20+ from nodejs.org if you want to use them for testing.


Create the Project

Use Spring Initializr or create the Maven project manually. The Initializr is quickest for the base, then you'll add the Spring AI starters to pom.xml (Initializr's catalog of Spring AI starters has changed over time, so manual edits keep the dependency set explicit).

Start with this base pom.xml. It is the standalone-equivalent of the repo's first-connection module (it inlines the parent and BOM the reference inherits, so it builds on its own):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.0</version>
<relativePath/>
</parent>

<groupId>com.themcpguy</groupId>
<artifactId>first-connection</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
<java.version>21</java.version>
<spring-ai.version>2.0.0</spring-ai.version>
</properties>

<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>

<dependencies>

<!-- Spring Boot base -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>

<!-- MCP client: connects to MCP servers, registers their tools -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>

<!-- AI model provider: choose one -->

<!-- Option A: Anthropic (Claude) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-anthropic</artifactId>
</dependency>

<!-- Option B: OpenAI (GPT-4o). Uncomment to use instead of Anthropic -->
<!--
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
-->

<!-- Testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

<!-- Spring AI milestone repository: optional for GA builds; only needed to resolve pre-GA milestones/RCs -->

<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>

</project>

Why the Spring Milestones Repository?

Spring AI 1.x and 2.0.x GA releases are available on Maven Central. Pre-GA milestones (for example, a future 2.1.0-Mx line) live in the Spring Milestones repository. If you're using only GA releases on Maven Central, this repository is optional, but including it future-proofs your build when you want to evaluate a milestone.


Configure Your Application

Create src/main/resources/application.properties:

# ── AI Provider ─────────────────────────────────────────────────────────────

# Anthropic Claude
spring.ai.anthropic.api-key=${ANTHROPIC_API_KEY}
spring.ai.anthropic.chat.model=claude-sonnet-4-6

# OpenAI GPT-4o (uncomment if using OpenAI)
# spring.ai.openai.api-key=${OPENAI_API_KEY}
# spring.ai.openai.chat.model=gpt-4o

# ── MCP Client ──────────────────────────────────────────────────────────────

# Automatically register MCP tools with ChatClient
spring.ai.mcp.client.toolcallback.enabled=true

# Log MCP connection activity at startup
logging.level.org.springframework.ai.mcp=DEBUG

Notice that the API key uses ${ANTHROPIC_API_KEY}. Spring Boot resolves environment variables using this ${VAR_NAME} syntax in properties files. The actual key never appears in the file.

Older Spring AI examples use spring.ai.anthropic.chat.options.* keys; those still work in 2.0.0 but are deprecated for removal, so this course uses the new flattened keys like spring.ai.anthropic.chat.model.

No MCP server connections are configured yet. We'll add those in Module 3. For now, we're just verifying the AI provider connection.


Write the Smoke Test

Create a minimal application class that calls the LLM once and exits:

package com.themcpguy.springai;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class FirstConnectionApplication {

public static void main(String[] args) {
SpringApplication.run(FirstConnectionApplication.class, args);
}

@Bean
CommandLineRunner smokeTest(ChatClient.Builder builder) {
return args -> {
ChatClient chatClient = builder.build();

String response = chatClient.prompt()
.user("Reply with exactly: 'Spring AI is working.'")
.call()
.content();

System.out.println("Response: " + response);
};
}
}

ChatClient.Builder is auto-configured by the AI provider starter. You inject the builder, call .build(), and you have a ChatClient ready to use.

The CommandLineRunner pattern runs code on startup and then exits, perfect for smoke tests and command-line tools.

Run it:

mvn spring-boot:run

You should see something like:

Response: Spring AI is working.

If you see that, your stack is correctly configured end to end: Spring Boot, then Spring AI, then Anthropic API, then Claude, then response back.


The Auto-Configuration Chain

Let's be explicit about what happened automatically when you ran the application:

  1. spring-ai-starter-model-anthropic registered an AnthropicChatModel bean that points at Anthropic's API using your key.

  2. Spring AI core registered a ChatClient.Builder bean backed by that ChatModel.

  3. Your CommandLineRunner injected the builder, built a ChatClient, and called the API.

  4. spring-ai-starter-mcp-client is present but did nothing yet. There are no MCP servers configured, so it has nothing to connect to.

The Spring AI autoconfiguration classes are in spring-ai-autoconfigure. You can explore them with mvn dependency:tree to see the full dependency graph.


Troubleshooting Common Issues

401 Unauthorized from the AI provider

Your API key is wrong, expired, or missing. Verify:

echo $ANTHROPIC_API_KEY   # Should print your key, not empty

# Test the key directly
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{"model":"claude-haiku-4-5","max_tokens":10,"messages":[{"role":"user","content":"hi"}]}'

Could not resolve artifact spring-ai-starter-mcp-client

With the GA 2.0.0 pin used in this lesson, this artifact comes from Maven Central, so the milestones repository is not the cause. Check, in order: the <spring-ai.version> property matches a released version (2.0.0), the spring-ai-bom import is present in <dependencyManagement>, and the artifact id is spelled exactly spring-ai-starter-mcp-client. Then force a metadata refresh:

mvn dependency:resolve -U   # Force update of snapshots/releases

If you are deliberately using a pre-GA milestone or RC version, also verify the Spring Milestones repository entry in the <repositories> block.

No qualifying bean of type 'ChatClient.Builder'

You're missing the AI provider starter (or it's commented out). The provider starter is what registers the ChatModel bean, which is what ChatClient.Builder wraps. You must have at least one provider starter.

Application starts but no output appears

The CommandLineRunner may have thrown an exception that Spring swallowed. Add this to your properties:

logging.level.root=INFO
spring.main.banner-mode=off

And check the full log output for exception stack traces.


Check the Latest Versions

Spring AI releases frequently. Before starting a new project, check the current stable version:

To update, change <spring-ai.version> in your pom.xml and run mvn verify. The BOM ensures all Spring AI artifacts update together.


Next: Module 3: Connecting to Your First MCP Server. Add an MCP server to your configuration and inspect the tools it exposes.