Практычныя прытамулі: Spring AI + MCP: Адкройце свае Java-інструменты для будзь-якага кліента AI (нават...)
Практычныя прыказкі: Spring AI + MCP: Адкрыце свае Java-інструменты для будзь-якага кліента AI (включаючы контракты, перакантрольванні та месцы для додавання коду для команд, якія використоўваюць гэты патерн).
У гэтым карыце практычным нарадзенні перакладзенаецца весь пацек з сыр'ёў да рабочай системы для: Spring AI + MCP: Адкрыванне вашых Java-інструментаў для будзь-якага AI-кліента (нават Claude Desktop). Акцэнт ставяцца на практычныя крокі, чыстае перакананне ў правильнасці дзействаў і код, які можна проста падставіць у репазітарый без неабязковасці з'ясоўвання намеру.
Практычныя інструкцыі па протаколе Model Context Protocol (MCP) з Spring AI 2.0. Створыце сервер MCP, які адкрывае інструменты для платежаў через SSE, а таксама околачны AI-кліент, який іх аднаходзіць і выкананае — без жадных ваказаў пра інструменты з боку кліента.
Для практычнага кярэкстання, перад зменым коду неабходна ясная інструкцыя: паказаць, што ўваходзіцца, хто являецца адпаведным адпаведальнікам за крок і якія критэрыя завершэння. Аперацыёныя працавнікі должны магчыма ўвайсці знову у крок, выкарыстоўваючы вядомую точку контролю, без неабясненага адгадвання скрытых станоў. Конфігурацыю трэба захаваць праз аддзел ад коду прыкладнення. Файлы сяродавішча, хранільнікі секрэтных дадзеных і флагі функцыйяў должны знаходзіцца ў аднам месцы, якое працавнікі можуць пераглядаць, не чытаючы весь ланцуг задач. Калі наступны крок — гэта код або вызов інструмента, лепш выкарыстоўваць структураваныя выходны дадзеныя з перакананнем ў схэме, чым вольныя тэкстовыя апісанні.
Проблема
Для стадіі «Проблема» неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крэтыры завершэння пры перадзеі коду. Аперацыйныя працавнікі павінны магчымае перазапускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Неабяжна задокументаваць як шлях успеху, так і шлях вярнення. Перапрыбуткі, людзкіе перакрыцця і обробка некоректных паведамленняў є часткай продукту, а не чымсь, што дадаецца пазней.
Шта такое MCP?
Для стадіі «Што такое MCP» неабяжна ўзначыць вхідныя даны, адпаведальнага за крок і крэтырыя завершэння пры зміне коду. Аперацыйныя працавнікі павінны магчымае перазапускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Лепш выбіраць маленькія, тэставаныя елементы замест большых скрыптов. Калі крок не выйшоў, прычына нехарактэрыстыкі павінна вказываць на адзіну адпаведальнасць, а не на заплутаны ланцужок задач. Автентыфікуйцеся на в’язку і паўтарна автарызуйцеся на роўні дадзенняў. Толькі токэн-носіцель не є межай арендаванага ресурсу.
Што мы ствараем
Для стадіі «Што мы будуем ствараць» неабяжнае падзець: апрацаваць параметры вхідных дадзеных, абяронца крока і критэрыі завершэння пры перадзеўці коду. Аперацыйныя працавнікі павінны магчымае перазапускаць крок з вядомай точкі контролю, не падозрываючы прыхованы стан. Спрыяйце гэтай стадіі як дагавору межа вхіднымі дадзенымі і перакананымі выходнымі рэзультатамі. Даўце назвы артыфактам, апрацаваць перакананні на успех і не прымайце часткова завершаныя рэзультаты без падтверджэння. Автентыфікуйцеся ў воратах і паўтарна автарызавайцеся на роўні дадзеных. Толькі токэн-носіцель не є межай арендаванага ресурсу.
Прыямыя прычынкі
У стадії праграматыкі неабяжна з’явіць вхідныя даны, адміністратара крока і критэрыя завершэння пры перадзмене коду. Аператары должны магчымаць перзапуск крока з вядомай точкі контролю, не спрабоўваючы здогадвацца пра схованы стан. Запісвайце час выконання і вартасць токена або запытку праза функцыйнальныя рэзултаты. Відразлівае паказанне вартасцей запобегае неспадзячым рахункам, калі процес пераходзіць з дэмовай среды ў спадзеленую. Автентыфікуйцеся ў шлюзе і паўтарна автарызавайцеся на роўні дадзеных. Сам токен-носіцель не є межай арендаванага ресурсу. У стадії праграматыкі неабяжна з’явіць вхідныя даны, адміністратара крока і критэрыя завершэння пры перадзмене коду. Аператары должны магчымаць перзапуск крока з вядомай точкі контролю, не спрабоўваючы здагадвацца пра схованы стан. Дакументавайце як шлях успеху, так і шлях вярнення. Перапрыбуткі, людзкіе контролі і обработка некоректных паведамленняў є часткай продукту, а не чымсь, што дадаецца пазней.
Настройка проекту
Калі працюеце над стадзіяй установкі проекту, спачатку запісайце контракт: неабяжлівыя даны, сігнал успеху і тое, што выходзіць у разе частковага неяксамоства. Такі список пераканальвае ў тым, каб пазнейшыя змены коду былі чыстымі. Валіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок неяксамоства, гэтае неяксамоства павінна адначасова паказваць адной адпаведнасці, а не заплутаны ланцюг задач. Запісвайце назву інструмента, хэш аргументаў, час затрымкі і рынак кожнага вызову. Дэбаггаванне без такога следу марнуе гадзіны.
Parent POM
Калі працюеце над стадзіяй Parent POM, спачатку запісайце контракт: неабяжлівыя вхідныя даны, сігнал успеху і тое, што выходзіць у случае частковага нявыпання. Такі список пераконтроўвае, каб пазнейшыя змены коду былі чыстымі. Спрацоўвайце з гэтай стадзіяй як з контрактом межа вхіднымі данымі і перакананымі выходнымі рэзультатамі. Дайце назвы артыфактам, задаце правілы пераканання успеху і не падпрымайце тых, хто выканання завершае часткова без паведамлення. Запісвайце назву інструмента, хэш аргументаў, час адклікання і рэзультат кожнага вызову. Без такога лёгкага следу дэбагаванне можа зайняць гады.
<groupId>com.anupam</groupId>
<artifactId>spring-ai-mcp</artifactId>
<packaging>pom</packaging>
<properties>
<java.version>21</java.version>
<spring-ai.version>2.0.0</spring-ai.version>
</properties>
<modules>
<module>mcp-server</module>
<module>mcp-client</module>
</modules>
<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>
Частка 1: Стварэнне сервера MCP
Калі працуеце над часткаю 1 «Стварэнне сцэны», спачатку запісайце угоду: неабяжлівыя даннэ, сігнал успеху і тое, што выходзіць пад частковым нявыпаннем. Такі список контроля дапамагае заліцварыць будучыя змены ў кодзе. Запісвайце часы выканання і кост токена або запиту праза функцыональныя рэзультаты. Відразлівасць коста з самага пачатку запобегае неспакоўным рахункам, калі працэс пераходзіць з дэмаверсіі ў спяльныя среды. Запішыце назву інструмента, хэш аргументаў, час затрымкі і рэзультат кожнага вызову. Без такога лёгкага следу дэбагаванне агента, які цягнёцца без канца, марнуе гадзіны.
Залежнасці сервера
Калі працюеце над стадзіяй «Залежнасці сервера», спачатку запішыце умовы вярбавання: неабяжлівыя даннэ, сігнал успеху і тое, што выходзіць пад частыя неудачы. Такі список дапамагае заліцварваць будучыя змены коду. Храніце настройкі парадульна ад коду прыемліка. Файлы сераўнавання, хранальнікі секрэтных данных і флагі функцыйяў должны знаходзіцца ў аднам месцы, куды аператары можаць адбавіць аудыт без неабяжлівага чытання всей структуры. Запішыце назву інструмента, хэш аргументаў, час адклікання і рэзультат кожнага вызову. Без такога лёгкага следу час на дэбагаванне праходзіць без прагу.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
</dependency>
</dependencies>
Настройкі сервера
Калі працюеце над стадзіяй канфыгурацыі сервера, спачатку запісайце умовы кантракту: неабяжлівыя даны, сігнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі список контроля дапамагае заліцьварыць пазнейшыя змены коду. Документавайце як шлях успеху, так і шлях вярнення. Перапрыбуткі, людзкіе перакрыцця і обробка некоректных паведамленняў ёсць часткаю продукту, а не пазнейшыя доработкі. Запісвайце назву інструмента, хэш аргументаў, час затрымкі і рынак кожнага вызову. Без такога лёгкага следу дэбагаванне агента губіць гады.
spring:
ai:
mcp:
server:
name: payment-tools-server
version: 1.0.0
description: "MCP server exposing payment lookup and exchange rate tools"
server:
port: 8081
Адзначайце інструменты за дапамою @Tool
Калі працуеце на стадыі «З’явленне інструментаў з інструментам», спачатку запішыце умовы викорыстоўвання: неабходныя даны, сігнал успеху і тое, што выходзіць на частым нявыпанні. Такі список контроля дапамагае залічыцца з пазнейшымі змянамі ў кодзе. Валіце маленькія, тэставаныя елементы замест большых скрыптаў. Калі якісь крок не выйшоў, прычына нявыпанні павінна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцюг задач. Запішыце назву інструмента, хэш параметраў, час адклікання і рынак кожнага вызову. Без такога следу дэбаггін займае гадзіны.
@Component
public class PaymentMcpTools {
@Tool(description = "Get the current status and details of a payment by its transaction ID")
public PaymentInfo getPaymentStatus(
@ToolParam(description = "Transaction ID, e.g. TXN-9042") String transactionId) {
PaymentInfo info = payments.get(transactionId);
if (info == null) {
throw new RuntimeException("Payment not found: " + transactionId);
}
return info;
}
@Tool(description = "Get the current exchange rate between two currencies. " +
"Supported: USD, EUR, GBP, JPY, INR, CAD, AUD")
public ExchangeRate getExchangeRate(
@ToolParam(description = "Source currency code, e.g. USD") String from,
@ToolParam(description = "Target currency code, e.g. EUR") String to) {
// ... conversion logic
return new ExchangeRate(from, to, rate, LocalDateTime.now());
}
@Tool(description = "Calculate the total amount in a target currency for a given payment")
public String convertPaymentAmount(
@ToolParam(description = "Transaction ID") String transactionId,
@ToolParam(description = "Target currency code") String targetCurrency) {
PaymentInfo payment = getPaymentStatus(transactionId);
ExchangeRate rate = getExchangeRate(payment.currency(), targetCurrency);
BigDecimal converted = payment.amount().multiply(rate.rate());
return String.format("%s %s = %s %s", payment.amount(), payment.currency(),
converted, targetCurrency);
}
}
Прыступіце да запуску сервера
Калі працюеце над стадзіяй «Запуск сервера», спачатку запісайце контракт: неабяжлівыя даны, сигнал успеху і тое, што выходзіць у случае частковага нявыпання. Такі список пераконвае ў тым, што пазнейшыя змены коду будуць чыстымі. Спрэтавайцеся да гэтай стадзіяй як да контракта межа данымі і перакананымі выходамі. Дайце назву артыфактам, задаце правілы пераканання успеху і не падзэльвайцеся на частковыя завершэння без паведамлення. Запісвайце назву інструмента, хэш аргументаў, час затрымкі і рынак кожнага вызову. Без такога лёгкага следу дэбагаванне можа зайняць гады.
cd mcp-server
./mvnw spring-boot:run
Частка 2: Стварэнне кліента MCP
Калі працуеце над часткай 2 «Стварэнне сцэны», спачатку запісайце угоду: неабяжлівыя даннэ, сігнал успеху і тое, што выходзіць пад частковым нявыпаннем. Такі список контроля дапамагае заліцварваць будучыя змены коду. Запісуйце час выканання і кост токена або запыту праз тэкст функцыйнальных рэзултатаў. Відразы коста з самага пачатку запобегае неспакойным рахункам, калі працэўнае сераўеры пераходзяць з дамовай версіі ў спакульнаныя среды. Запішыце назву інструмента, хэш аргументаў, час затрымкі і рэзултат кожнага вызову. Без такога лёгкага адліку дэбагаванне агента, які цягнёцца без канца, можа зайняць гады.
Залежнасці кліента
Калі працюеце над стадзіяй «Залежнасці кліента», спачатку запішыце кантракт: неабяжлівыя даны, сигнал успеху і тое, што выходзіць у разе частковага нявыпання. Такі список перакладзець пазнейшыя змены коду ў правільным напрамку. Зберагаеце настройкі паза кодам прыемліка. Файлы сераўнавання, хранальнікі секрэтных данных і флагі функцыйяў должны знаходзіцца ў аднам месцы, куды аператары можаць адбавіць аудыт без неабяжлівага чытання всей структуры. Зяўляйце логі з назвай інструмента, хэшам аргументаў, часам затрымкі і рэзультатам кожнага вызову. Без такога следу дэбаггінгавы агент траціць гадзіны на безрэзультатную працу.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-client</artifactId>
</dependency>
</dependencies>
Настройкі кліента
Калі працюеце над стадзіяй канфыгурацыі кліента, спачатку запісайце угоду: неабяжлівыя даны, сігнал успеху і тое, што выканаецца у разе частковага нявыполнення. Такі список пераканальвае ў тым, каб пазнейшыя змены коду былі чыстымі. Документавайце як шлях успеху, так і шлях вярнення. Перапрыбуткі, людзкіе контралі і обработка некоректных паведамленняў ёсць часткай продукту, а не пазнейшым дапрацоўкам. Зявляйце логі з назвай інструмента, хэшам аргументаў, часам затрымкі і рэзультатам кожнага вызову. Без такога следу дэбаггінгавы агент траціць гадзіны на безрезультатныя циклы.
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
chat:
options:
model: gpt-4o
temperature: 0.3
mcp:
client:
sse:
connections:
payment-tools:
url: http://localhost:8081
server:
port: 8080
Прыўяжыце інструменты MCP да ChatClient
Калі працуеце з інструментамі Wire MCP, спачатку запісайце умовы ўгоды: неабяжныя даны, сігнал успеху і тое, што выходзіць у разе частковага невыпання. Такі список контролю дапамагае залічыць змяны ў кодзе пасля таго.
@Configuration
public class AiConfig {
@Bean
public ChatClient chatClient(ChatModel chatModel, SyncMcpToolCallbackProvider mcpTools) {
return ChatClient.builder(chatModel)
.defaultSystem("""
You are a helpful payments assistant. You have access to tools
provided by an MCP server. Use them to look up payment status,
get exchange rates, and convert payment amounts.
""")
.defaultTools(mcpTools)
.build();
}
}
Кантролер (непатрабуюць ведомаў пра інструменты)
@RestController
@RequestMapping("/api/v1/assistant")
public class AssistantController {
private final ChatClient chatClient;
public AssistantController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@PostMapping("/chat")
public ResponseEntity<ChatResponse> chat(@Valid @RequestBody ChatRequest request) {
String answer = chatClient.prompt()
.user(request.message())
.call()
.content();
return ResponseEntity.ok(ChatResponse.of(answer));
}
}
Тэставанне
1. Запускайце оба прыкладніка
# Terminal 1 — MCP Server
cd mcp-server
./mvnw spring-boot:run
# Terminal 2 - MCP Client
cd mcp-client
export OPENAI_API_KEY=sk-your-key-here
./mvnw spring-boot:run
2. Здзейсніце запыт через кліента
curl -X POST http://localhost:8080/api/v1/assistant/chat \
-H "Content-Type: application/json" \
-d '{"message": "What is the status of payment TXN-9042?"}'
{
"answer": "Payment TXN-9042 has been completed. It was $250.00 USD sent from Alice Johnson to Bob Smith on August 20, 2026.",
"timestamp": "2026-08-22T11:00:00"
}
3. Ожывайце інструмент, які выкарыстоўвае іншыя інструменты
curl -X POST http://localhost:8080/api/v1/assistant/chat \
-H "Content-Type: application/json" \
-d '{"message": "Convert TXN-9043 amount to EUR"}'
{
"answer": "Payment TXN-9043 is $1,200.50 USD. At the current rate of 0.92, that equals approximately €1,104.46 EUR.",
"timestamp": "2026-08-22T11:00:05"
}
MCP проты локальных інструментаў: калі які выкарыстоўваць
Спалучэнне локальных і MCP інструментаў
chatClient.prompt()
.user(question)
.tools(localTools, mcpTools) // Both in one call
.call()
.content();
Варыянты транспарту
Прыкладstdio (з’яўленне звязку з серверам MCP на базе npm):
spring:
ai:
mcp:
client:
stdio:
connections:
filesystem:
command: npx
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
Аспекты безпекі
// Only allow specific tools from the MCP server
@Bean
McpToolFilter mcpToolFilter() {
return (serverName, toolName) ->
Set.of("getPaymentStatus", "getExchangeRate").contains(toolName);
}
Частыя проблемы
Полны працюючы прыклад
git clone https://github.com/AnupamSinha/spring-boot-examples/tree/main/06-ai-mcp
cd spring-ai-mcp
# Terminal 1 - Start server
cd mcp-server && ../mvnw spring-boot:run
# Terminal 2 - Start client
cd mcp-client && export OPENAI_API_KEY=sk-... && ../mvnw spring-boot:run