chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:31:35 +08:00
commit c275ba2868
13613 changed files with 2980806 additions and 0 deletions
+12
View File
@@ -0,0 +1,12 @@
ARG VARIANT="9.0"
FROM mcr.microsoft.com/devcontainers/dotnet:${VARIANT}
ARG NODE_VERSION="none"
RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
python3.11 \
python3-venv \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
+25
View File
@@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
+36
View File
@@ -0,0 +1,36 @@
<!--
IF SUFFICIENT INFORMATION IS NOT PROVIDED VIA THE FOLLOWING TEMPLATE THE ISSUE MIGHT BE CLOSED WITHOUT FURTHER CONSIDERATION OR INVESTIGATION
-->
> Please provide us with the following information:
> ---------------------------------------------------------------
### This issue is for a: (mark with an `x`)
```
- [ ] bug report -> please search issues before submitting
- [ ] feature request
- [ ] documentation issue or request
- [ ] regression (a behavior that used to work and stopped in a new release)
```
### Minimal steps to reproduce
>
### Any log messages given by the failure
>
### Expected/desired behavior
>
### OS and Version?
> Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?)
### azd version?
> run `azd version` and copy paste here.
### Versions
>
### Mention any other details that might be useful
> ---------------------------------------------------------------
> Thanks! We'll be in touch soon.
@@ -0,0 +1,33 @@
# Purpose
Describe the intention of the changes being proposed. What problem does it solve or functionality does it add?
## Does this introduce a breaking change?
When developers merge from main and run the server, azd up, or azd deploy, will this produce an error?
If you're not sure, try it out on an old environment.
```
[ ] Yes
[ ] No
```
## Does this require changes to learn.microsoft.com docs or modules?
which includes deployment, settings and usage instructions.
```
[ ] Yes
[ ] No
```
## Type of change
```
[ ] Bugfix
[ ] Feature
[ ] Code style update (formatting, local variables)
[ ] Refactoring (no functional changes, no api changes)
[ ] Documentation content changes
[ ] Other... Please describe:
```
+38
View File
@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+491
View File
@@ -0,0 +1,491 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
mcp.json
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
venv/
# Mono auto generated files
mono_crash.*
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# ASP.NET Scaffolding
ScaffoldingReadMe.txt
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp
# Visual Studio 6 technical files
*.ncb
*.aps
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# Visual Studio History (VSHistory) files
.vshistory/
# BeatPulse healthcheck temp database
healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace
# Local History for Visual Studio Code
.history/
# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp
# JetBrains Rider
*.sln.iml
# Compiled class files
*.class
# Log files
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs
hs_err_pid*
replay_pid*
# Maven
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
# Gradle
.gradle/
build/
!gradle/wrapper/gradle-wrapper.jar
# IntelliJ IDEA
.idea/
*.iws
*.iml
*.ipr
out/
# Eclipse
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
# NetBeans
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
# VS Code
.vscode/
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# Spring Boot
.spring-boot-devtools
# Misc
.DS_Store
Thumbs.db
# Environments
.env
.venv
# Chainlit and related files
.chainlit/
.files/
chainlit.md
+323
View File
@@ -0,0 +1,323 @@
# Introduction to Model Context Protocol (MCP): Why It Matters for Scalable AI Applications
[![Introduction to Model Context Protocol](../images/video-thumbnails/01.png)](https://youtu.be/agBbdiOPLQA)
_(Click the image above to view video of this lesson)_
Generative AI applications are a great step forward as they often let the user interact with the app using natural language prompts. However, as more time and resources are invested in such apps, you want to make sure you can easily integrate functionalities and resources in such a way that it's easy to extend, that your app can cater to more than one model being used, and handle various model intricacies. In short, building Gen AI apps is easy to begin with, but as they grow and become more complex, you need to start defining an architecture and will likely need to rely on a standard to ensure your apps are built in a consistent way. This is where MCP comes in to organize things and provide a standard.
---
## **🔍 What Is the Model Context Protocol (MCP)?**
The **Model Context Protocol (MCP)** is an **open, standardized interface** that allows Large Language Models (LLMs) to interact seamlessly with external tools, APIs, and data sources. It provides a consistent architecture to enhance AI model functionality beyond their training data, enabling smarter, scalable, and more responsive AI systems.
---
## **🎯 Why Standardization in AI Matters**
As generative AI applications become more complex, it's essential to adopt standards that ensure **scalability, extensibility, maintainability,** and **avoiding vendor lock-in**. MCP addresses these needs by:
- Unifying model-tool integrations
- Reducing brittle, one-off custom solutions
- Allowing multiple models from different vendors to coexist within one ecosystem
**Note:** While MCP bills itself as an open standard, there are no plans to standardize MCP through any existing standards bodies such as IEEE, IETF, W3C, ISO, or any other standards body.
---
## **📚 Learning Objectives**
By the end of this article, you'll be able to:
- Define **Model Context Protocol (MCP)** and its use cases
- Understand how MCP standardizes model-to-tool communication
- Identify the core components of MCP architecture
- Explore real-world applications of MCP in enterprise and development contexts
---
## **💡 Why the Model Context Protocol (MCP) Is a Game-Changer**
### **🔗 MCP Solves Fragmentation in AI Interactions**
Before MCP, integrating models with tools required:
- Custom code per tool-model pair
- Non-standard APIs for each vendor
- Frequent breaks due to updates
- Poor scalability with more tools
### **✅ Benefits of MCP Standardization**
| **Benefit** | **Description** |
|--------------------------|--------------------------------------------------------------------------------|
| Interoperability | LLMs work seamlessly with tools across different vendors |
| Consistency | Uniform behavior across platforms and tools |
| Reusability | Tools built once can be used across projects and systems |
| Accelerated Development | Reduce dev time by using standardized, plug-and-play interfaces |
---
## **🧱 High-Level MCP Architecture Overview**
MCP follows a **client-server model**, where:
- **MCP Hosts** run the AI models
- **MCP Clients** initiate requests
- **MCP Servers** serve context, tools, and capabilities
### **Key Components:**
- **Resources** Static or dynamic data for models
- **Prompts** Predefined workflows for guided generation
- **Tools** Executable functions like search, calculations
- **Sampling** Agentic behavior via recursive interactions
- **Elicitation** Server-initiated requests for user input
- **Roots** Filesystem boundaries for server access control
### **Protocol Architecture:**
MCP uses a two-layer architecture:
- **Data Layer**: JSON-RPC 2.0 based communication with lifecycle management and primitives
- **Transport Layer**: STDIO (local) and Streamable HTTP with SSE (remote) communication channels
---
## How MCP Servers Work
MCP servers operate in the following way:
- **Request Flow**:
1. A request is initiated by an end user or software acting on their behalf.
2. The **MCP Client** sends the request to an **MCP Host**, which manages the AI Model runtime.
3. The **AI Model** receives the user prompt and may request access to external tools or data via one or more tool calls.
4. The **MCP Host**, not the model directly, communicates with the appropriate **MCP Server(s)** using the standardized protocol.
- **MCP Host Functionality**:
- **Tool Registry**: Maintains a catalog of available tools and their capabilities.
- **Authentication**: Verifies permissions for tool access.
- **Request Handler**: Processes incoming tool requests from the model.
- **Response Formatter**: Structures tool outputs in a format the model can understand.
- **MCP Server Execution**:
- The **MCP Host** routes tool calls to one or more **MCP Servers**, each exposing specialized functions (e.g., search, calculations, database queries).
- The **MCP Servers** perform their respective operations and return results to the **MCP Host** in a consistent format.
- The **MCP Host** formats and relays these results to the **AI Model**.
- **Response Completion**:
- The **AI Model** incorporates the tool outputs into a final response.
- The **MCP Host** sends this response back to the **MCP Client**, which delivers it to the end user or calling software.
```mermaid
---
title: MCP Architecture and Component Interactions
description: A diagram showing the flows of the components in MCP.
---
graph TD
Client[MCP Client/Application] -->|Sends Request| H[MCP Host]
H -->|Invokes| A[AI Model]
A -->|Tool Call Request| H
H -->|MCP Protocol| T1[MCP Server Tool 01: Web Search]
H -->|MCP Protocol| T2[MCP Server Tool 02: Calculator tool]
H -->|MCP Protocol| T3[MCP Server Tool 03: Database Access tool]
H -->|MCP Protocol| T4[MCP Server Tool 04: File System tool]
H -->|Sends Response| Client
subgraph "MCP Host Components"
H
G[Tool Registry]
I[Authentication]
J[Request Handler]
K[Response Formatter]
end
H <--> G
H <--> I
H <--> J
H <--> K
style A fill:#f9d5e5,stroke:#333,stroke-width:2px
style H fill:#eeeeee,stroke:#333,stroke-width:2px
style Client fill:#d5e8f9,stroke:#333,stroke-width:2px
style G fill:#fffbe6,stroke:#333,stroke-width:1px
style I fill:#fffbe6,stroke:#333,stroke-width:1px
style J fill:#fffbe6,stroke:#333,stroke-width:1px
style K fill:#fffbe6,stroke:#333,stroke-width:1px
style T1 fill:#c2f0c2,stroke:#333,stroke-width:1px
style T2 fill:#c2f0c2,stroke:#333,stroke-width:1px
style T3 fill:#c2f0c2,stroke:#333,stroke-width:1px
style T4 fill:#c2f0c2,stroke:#333,stroke-width:1px
```
## 👨‍💻 How to Build an MCP Server (With Examples)
MCP servers allow you to extend LLM capabilities by providing data and functionality.
Ready to try it out? Here are language and/or stack specific SDKs with examples of creating simple MCP servers in different languages/stacks:
- **Python SDK**: https://github.com/modelcontextprotocol/python-sdk
- **TypeScript SDK**: https://github.com/modelcontextprotocol/typescript-sdk
- **Java SDK**: https://github.com/modelcontextprotocol/java-sdk
- **C#/.NET SDK**: https://github.com/modelcontextprotocol/csharp-sdk
## 🌍 Real-World Use Cases for MCP
MCP enables a wide range of applications by extending AI capabilities:
| **Application** | **Description** |
|------------------------------|--------------------------------------------------------------------------------|
| Enterprise Data Integration | Connect LLMs to databases, CRMs, or internal tools |
| Agentic AI Systems | Enable autonomous agents with tool access and decision-making workflows |
| Multi-modal Applications | Combine text, image, and audio tools within a single unified AI app |
| Real-time Data Integration | Bring live data into AI interactions for more accurate, current outputs |
### 🧠 MCP = Universal Standard for AI Interactions
The Model Context Protocol (MCP) acts as a universal standard for AI interactions, much like how USB-C standardized physical connections for devices. In the world of AI, MCP provides a consistent interface, allowing models (clients) to integrate seamlessly with external tools and data providers (servers). This eliminates the need for diverse, custom protocols for each API or data source.
Under MCP, an MCP-compatible tool (referred to as an MCP server) follows a unified standard. These servers can list the tools or actions they offer and execute those actions when requested by an AI agent. AI agent platforms that support MCP are capable of discovering available tools from the servers and invoking them through this standard protocol.
### 💡 Facilitates access to knowledge
Beyond offering tools, MCP also facilitates access to knowledge. It enables applications to provide context to large language models (LLMs) by linking them to various data sources. For instance, an MCP server might represent a companys document repository, allowing agents to retrieve relevant information on demand. Another server could handle specific actions like sending emails or updating records. From the agents perspective, these are simply tools it can use—some tools return data (knowledge context), while others perform actions. MCP efficiently manages both.
An agent connecting to an MCP server automatically learns the server's available capabilities and accessible data through a standard format. This standardization enables dynamic tool availability. For example, adding a new MCP server to an agents system makes its functions immediately usable without requiring further customization of the agent's instructions.
This streamlined integration aligns with the flow depicted in the following diagram, where servers provide both tools and knowledge, ensuring seamless collaboration across systems.
### 👉 Example: Scalable Agent Solution
```mermaid
---
title: Scalable Agent Solution with MCP
description: A diagram illustrating how a user interacts with an LLM that connects to multiple MCP servers, with each server providing both knowledge and tools, creating a scalable AI system architecture
---
graph TD
User -->|Prompt| LLM
LLM -->|Response| User
LLM -->|MCP| ServerA
LLM -->|MCP| ServerB
ServerA -->|Universal connector| ServerB
ServerA --> KnowledgeA
ServerA --> ToolsA
ServerB --> KnowledgeB
ServerB --> ToolsB
subgraph Server A
KnowledgeA[Knowledge]
ToolsA[Tools]
end
subgraph Server B
KnowledgeB[Knowledge]
ToolsB[Tools]
end
```
The Universal Connector enables MCP servers to communicate and share capabilities with each other, allowing ServerA to delegate tasks to ServerB or access its tools and knowledge. This federates tools and data across servers, supporting scalable and modular agent architectures. Because MCP standardizes tool exposure, agents can dynamically discover and route requests between servers without hardcoded integrations.
Tool and knowledge federation: Tools and data can be accessed across servers, enabling more scalable and modular agentic architectures.
### 🔄 Advanced MCP Scenarios with Client-Side LLM Integration
Beyond the basic MCP architecture, there are advanced scenarios where both client and server contain LLMs, enabling more sophisticated interactions. In the following diagram, **Client App** could be an IDE with a number of MCP tools available for user by the LLM:
```mermaid
---
title: Advanced MCP Scenarios with Client-Server LLM Integration
description: A sequence diagram showing the detailed interaction flow between user, client application, client LLM, multiple MCP servers, and server LLM, illustrating tool discovery, user interaction, direct tool calling, and feature negotiation phases
---
sequenceDiagram
autonumber
actor User as 👤 User
participant ClientApp as 🖥️ Client App
participant ClientLLM as 🧠 Client LLM
participant Server1 as 🔧 MCP Server 1
participant Server2 as 📚 MCP Server 2
participant ServerLLM as 🤖 Server LLM
%% Discovery Phase
rect rgb(220, 240, 255)
Note over ClientApp, Server2: TOOL DISCOVERY PHASE
ClientApp->>+Server1: Request available tools/resources
Server1-->>-ClientApp: Return tool list (JSON)
ClientApp->>+Server2: Request available tools/resources
Server2-->>-ClientApp: Return tool list (JSON)
Note right of ClientApp: Store combined tool<br/>catalog locally
end
%% User Interaction
rect rgb(255, 240, 220)
Note over User, ClientLLM: USER INTERACTION PHASE
User->>+ClientApp: Enter natural language prompt
ClientApp->>+ClientLLM: Forward prompt + tool catalog
ClientLLM->>-ClientLLM: Analyze prompt & select tools
end
%% Scenario A: Direct Tool Calling
alt Direct Tool Calling
rect rgb(220, 255, 220)
Note over ClientApp, Server1: SCENARIO A: DIRECT TOOL CALLING
ClientLLM->>+ClientApp: Request tool execution
ClientApp->>+Server1: Execute specific tool
Server1-->>-ClientApp: Return results
ClientApp->>+ClientLLM: Process results
ClientLLM-->>-ClientApp: Generate response
ClientApp-->>-User: Display final answer
end
%% Scenario B: Feature Negotiation (VS Code style)
else Feature Negotiation (VS Code style)
rect rgb(255, 220, 220)
Note over ClientApp, ServerLLM: SCENARIO B: FEATURE NEGOTIATION
ClientLLM->>+ClientApp: Identify needed capabilities
ClientApp->>+Server2: Negotiate features/capabilities
Server2->>+ServerLLM: Request additional context
ServerLLM-->>-Server2: Provide context
Server2-->>-ClientApp: Return available features
ClientApp->>+Server2: Call negotiated tools
Server2-->>-ClientApp: Return results
ClientApp->>+ClientLLM: Process results
ClientLLM-->>-ClientApp: Generate response
ClientApp-->>-User: Display final answer
end
end
```
## 🔐 Practical Benefits of MCP
Here are the practical benefits of using MCP:
- **Freshness**: Models can access up-to-date information beyond their training data
- **Capability Extension**: Models can leverage specialized tools for tasks they weren't trained for
- **Reduced Hallucinations**: External data sources provide factual grounding
- **Privacy**: Sensitive data can stay within secure environments instead of being embedded in prompts
## 📌 Key Takeaways
The following are key takeaways for using MCP:
- **MCP** standardizes how AI models interact with tools and data
- Promotes **extensibility, consistency, and interoperability**
- MCP helps **reduce development time, improve reliability, and extend model capabilities**
- The client-server architecture **enables flexible, extensible AI applications**
## 🧠 Exercise
Think about an AI application you're interested in building.
- Which **external tools or data** could enhance its capabilities?
- How might MCP make integration **simpler and more reliable?**
## Additional Resources
- [MCP GitHub Repository](https://github.com/modelcontextprotocol)
## What's next
Next: [Chapter 1: Core Concepts](../01-CoreConcepts/README.md)
+711
View File
@@ -0,0 +1,711 @@
# MCP Core Concepts: Mastering the Model Context Protocol for AI Integration
[![MCP Core Concepts](../images/video-thumbnails/02.png)](https://youtu.be/earDzWGtE84)
_(Click the image above to view video of this lesson)_
The [Model Context Protocol (MCP)](https://github.com/modelcontextprotocol) is a powerful, standardized framework that optimizes communication between Large Language Models (LLMs) and external tools, applications, and data sources.
This guide will walk you through the core concepts of MCP. You will learn about its client-server architecture, essential components, communication mechanics, and implementation best practices.
- **Explicit User Consent**: All data access and operations require explicit user approval before execution. Users must clearly understand what data will be accessed and what actions will be performed, with granular control over permissions and authorizations.
- **Data Privacy Protection**: User data is only exposed with explicit consent and must be protected by robust access controls throughout the entire interaction lifecycle. Implementations must prevent unauthorized data transmission and maintain strict privacy boundaries.
- **Tool Execution Safety**: Every tool invocation requires explicit user consent with clear understanding of the tool's functionality, parameters, and potential impact. Robust security boundaries must prevent unintended, unsafe, or malicious tool execution.
- **Transport Layer Security**: All communication channels should use appropriate encryption and authentication mechanisms. Remote connections should implement secure transport protocols and proper credential management.
#### Implementation Guidelines:
- **Permission Management**: Implement fine-grained permission systems that allow users to control which servers, tools, and resources are accessible
- **Authentication & Authorization**: Use secure authentication methods (OAuth, API keys) with proper token management and expiration
- **Input Validation**: Validate all parameters and data inputs according to defined schemas to prevent injection attacks
- **Audit Logging**: Maintain comprehensive logs of all operations for security monitoring and compliance
## Overview
This lesson explores the fundamental architecture and components that make up the Model Context Protocol (MCP) ecosystem. You'll learn about the client-server architecture, key components, and communication mechanisms that power MCP interactions.
## Key Learning Objectives
By the end of this lesson, you will:
- Understand the MCP client-server architecture.
- Identify roles and responsibilities of Hosts, Clients, and Servers.
- Analyze the core features that make MCP a flexible integration layer.
- Learn how information flows within the MCP ecosystem.
- Gain practical insights through code examples in .NET, Java, Python, and JavaScript.
## MCP Architecture: A Deeper Look
The MCP ecosystem is built on a client-server model. This modular structure allows AI applications to interact with tools, databases, APIs, and contextual resources efficiently. Let's break down this architecture into its core components.
At its core, MCP follows a client-server architecture where a host application can connect to multiple servers:
```mermaid
flowchart LR
subgraph "Your Computer"
Host["Host with MCP (Visual Studio, VS Code, IDEs, Tools)"]
S1["MCP Server A"]
S2["MCP Server B"]
S3["MCP Server C"]
Host <-->|"MCP Protocol"| S1
Host <-->|"MCP Protocol"| S2
Host <-->|"MCP Protocol"| S3
S1 <--> D1[("Local\Data Source A")]
S2 <--> D2[("Local\Data Source B")]
end
subgraph "Internet"
S3 <-->|"Web APIs"| D3[("Remote\Services")]
end
```
- **MCP Hosts**: Programs like VSCode, Claude Desktop, IDEs, or AI tools that want to access data through MCP
- **MCP Clients**: Protocol clients that maintain 1:1 connections with servers
- **MCP Servers**: Lightweight programs that each expose specific capabilities through the standardized Model Context Protocol
- **Local Data Sources**: Your computer's files, databases, and services that MCP servers can securely access
- **Remote Services**: External systems available over the internet that MCP servers can connect to through APIs.
The MCP Protocol is an evolving standard using date-based versioning (YYYY-MM-DD format). The current protocol version is **2025-11-25**. You can see the latest updates to the [protocol specification](https://modelcontextprotocol.io/specification/2025-11-25/)
> **Looking ahead:** a release candidate for the next specification version, **2026-07-28**, was announced in May 2026 and is scheduled to ship July 28, 2026. It makes the protocol stateless at the transport layer (removing the `initialize` handshake and session IDs), formalizes an Extensions framework, and deprecates Roots, Sampling, and Logging in favor of newer patterns. See [What's Changing in MCP: The 2026-07-28 Release Candidate](./mcp-2026-07-28-release-candidate.md) for a full breakdown.
### 1. Hosts
In the Model Context Protocol (MCP), **Hosts** are AI applications that serve as the primary interface through which users interact with the protocol. Hosts coordinate and manage connections to multiple MCP servers by creating dedicated MCP clients for each server connection. Examples of Hosts include:
- **AI Applications**: Claude Desktop, Visual Studio Code, Claude Code
- **Development Environments**: IDEs and code editors with MCP integration
- **Custom Applications**: Purpose-built AI agents and tools
**Hosts** are applications that coordinate AI model interactions. They:
- **Orchestrate AI Models**: Execute or interact with LLMs to generate responses and coordinate AI workflows
- **Manage Client Connections**: Create and maintain one MCP client per MCP server connection
- **Control User Interface**: Handle conversation flow, user interactions, and response presentation
- **Enforce Security**: Control permissions, security constraints, and authentication
- **Handle User Consent**: Manage user approval for data sharing and tool execution
### 2. Clients
**Clients** are essential components that maintain dedicated one-to-one connections between Hosts and MCP servers. Each MCP client is instantiated by the Host to connect to a specific MCP server, ensuring organized and secure communication channels. Multiple clients enable Hosts to connect to multiple servers simultaneously.
**Clients** are connector components within the host application. They:
- **Protocol Communication**: Send JSON-RPC 2.0 requests to servers with prompts and instructions
- **Capability Negotiation**: Negotiate supported features and protocol versions with servers during initialization
- **Tool Execution**: Manage tool execution requests from models and process responses
- **Real-time Updates**: Handle notifications and real-time updates from servers
- **Response Processing**: Process and format server responses for display to users
### 3. Servers
**Servers** are programs that provide context, tools, and capabilities to MCP clients. They can execute locally (same machine as the Host) or remotely (on external platforms), and are responsible for handling client requests and providing structured responses. Servers expose specific functionality through the standardized Model Context Protocol.
**Servers** are services that provide context and capabilities. They:
- **Feature Registration**: Register and expose available primitives (resources, prompts, tools) to clients
- **Request Processing**: Receive and execute tool calls, resource requests, and prompt requests from clients
- **Context Provision**: Provide contextual information and data to enhance model responses
- **State Management**: Maintain session state and handle stateful interactions when needed
- **Real-time Notifications**: Send notifications about capability changes and updates to connected clients
Servers can be developed by anyone to extend model capabilities with specialized functionality, and they support both local and remote deployment scenarios.
### 4. Server Primitives
Servers in the Model Context Protocol (MCP) provide three core **primitives** that define the fundamental building blocks for rich interactions between clients, hosts, and language models. These primitives specify the types of contextual information and actions available through the protocol.
MCP servers can expose any combination of the following three core primitives:
#### Resources
**Resources** are data sources that provide contextual information to AI applications. They represent static or dynamic content that can enhance model understanding and decision-making:
- **Contextual Data**: Structured information and context for AI model consumption
- **Knowledge Bases**: Document repositories, articles, manuals, and research papers
- **Local Data Sources**: Files, databases, and local system information
- **External Data**: API responses, web services, and remote system data
- **Dynamic Content**: Real-time data that updates based on external conditions
Resources are identified by URIs and support discovery through `resources/list` and retrieval through `resources/read` methods:
```text
file://documents/project-spec.md
database://production/users/schema
api://weather/current
```
#### Prompts
**Prompts** are reusable templates that help structure interactions with language models. They provide standardized interaction patterns and templated workflows:
- **Template-based Interactions**: Pre-structured messages and conversation starters
- **Workflow Templates**: Standardized sequences for common tasks and interactions
- **Few-shot Examples**: Example-based templates for model instruction
- **System Prompts**: Foundational prompts that define model behavior and context
- **Dynamic Templates**: Parameterized prompts that adapt to specific contexts
Prompts support variable substitution and can be discovered via `prompts/list` and retrieved with `prompts/get`:
```markdown
Generate a {{task_type}} for {{product}} targeting {{audience}} with the following requirements: {{requirements}}
```
#### Tools
**Tools** are executable functions that AI models can invoke to perform specific actions. They represent the "verbs" of the MCP ecosystem, enabling models to interact with external systems:
- **Executable Functions**: Discrete operations that models can invoke with specific parameters
- **External System Integration**: API calls, database queries, file operations, calculations
- **Unique Identity**: Each tool has a distinct name, description, and parameter schema
- **Structured I/O**: Tools accept validated parameters and return structured, typed responses
- **Action Capabilities**: Enable models to perform real-world actions and retrieve live data
Tools are defined with JSON Schema for parameter validation and discovered through `tools/list` and executed via `tools/call`. Tools can also include **icons** as additional metadata for better UI presentation.
**Tool Annotations**: Tools support behavioral annotations (e.g., `readOnlyHint`, `destructiveHint`) that describe whether a tool is read-only or destructive, helping clients make informed decisions about tool execution.
Example tool definition:
```typescript
server.tool(
"search_products",
{
query: z.string().describe("Search query for products"),
category: z.string().optional().describe("Product category filter"),
max_results: z.number().default(10).describe("Maximum results to return")
},
async (params) => {
// Execute search and return structured results
return await productService.search(params);
}
);
```
## Client Primitives
In the Model Context Protocol (MCP), **clients** can expose primitives that enable servers to request additional capabilities from the host application. These client-side primitives allow for richer, more interactive server implementations that can access AI model capabilities and user interactions.
### Sampling
> **Deprecation notice:** the `2026-07-28` release candidate marks Sampling as deprecated in favor of direct integration with LLM provider APIs. It continues to work in `2025-11-25` and for at least a year after any deprecation, but new designs should prefer the replacement pattern. See [What's Changing in MCP: The 2026-07-28 Release Candidate](./mcp-2026-07-28-release-candidate.md).
**Sampling** allows servers to request language model completions from the client's AI application. This primitive enables servers to access LLM capabilities without embedding their own model dependencies:
- **Model-Independent Access**: Servers can request completions without including LLM SDKs or managing model access
- **Server-Initiated AI**: Enables servers to autonomously generate content using the client's AI model
- **Recursive LLM Interactions**: Supports complex scenarios where servers need AI assistance for processing
- **Dynamic Content Generation**: Allows servers to create contextual responses using the host's model
- **Tool Calling Support**: Servers can include `tools` and `toolChoice` parameters to enable the client's model to invoke tools during sampling
Sampling is initiated through the `sampling/complete` method, where servers send completion requests to clients.
### Roots
> **Deprecation notice:** the `2026-07-28` release candidate marks Roots as deprecated in favor of tool parameters, resource URIs, or server configuration. It continues to work in `2025-11-25` and for at least a year after any deprecation. See [What's Changing in MCP: The 2026-07-28 Release Candidate](./mcp-2026-07-28-release-candidate.md).
**Roots** provide a standardized way for clients to expose filesystem boundaries to servers, helping servers understand which directories and files they have access to:
- **Filesystem Boundaries**: Define the boundaries of where servers can operate within the filesystem
- **Access Control**: Help servers understand which directories and files they have permission to access
- **Dynamic Updates**: Clients can notify servers when the list of roots changes
- **URI-Based Identification**: Roots use `file://` URIs to identify accessible directories and files
Roots are discovered through the `roots/list` method, with clients sending `notifications/roots/list_changed` when roots change.
### Elicitation
**Elicitation** enables servers to request additional information or confirmation from users through the client interface:
- **User Input Requests**: Servers can ask for additional information when needed for tool execution
- **Confirmation Dialogs**: Request user approval for sensitive or impactful operations
- **Interactive Workflows**: Enable servers to create step-by-step user interactions
- **Dynamic Parameter Collection**: Gather missing or optional parameters during tool execution
Elicitation requests are made using the `elicitation/request` method to collect user input through the client's interface.
**URL Mode Elicitation**: Servers can also request URL-based user interactions, allowing servers to direct users to external web pages for authentication, confirmation, or data entry.
### Logging
> **Deprecation notice:** the `2026-07-28` release candidate marks Logging as deprecated in favor of `stderr` for stdio transports and OpenTelemetry for structured observability. It continues to work in `2025-11-25` and for at least a year after any deprecation. See [What's Changing in MCP: The 2026-07-28 Release Candidate](./mcp-2026-07-28-release-candidate.md).
**Logging** allows servers to send structured log messages to clients for debugging, monitoring, and operational visibility:
- **Debugging Support**: Enable servers to provide detailed execution logs for troubleshooting
- **Operational Monitoring**: Send status updates and performance metrics to clients
- **Error Reporting**: Provide detailed error context and diagnostic information
- **Audit Trails**: Create comprehensive logs of server operations and decisions
Logging messages are sent to clients to provide transparency into server operations and facilitate debugging.
## Information Flow in MCP
The Model Context Protocol (MCP) defines a structured flow of information between hosts, clients, servers, and models. Understanding this flow helps clarify how user requests are processed and how external tools and data are integrated into model responses.
- **Host Initiates Connection**
The host application (such as an IDE or chat interface) establishes a connection to an MCP server, typically via STDIO, WebSocket, or another supported transport.
- **Capability Negotiation**
The client (embedded in the host) and the server exchange information about their supported features, tools, resources, and protocol versions. This ensures both sides understand what capabilities are available for the session.
- **User Request**
The user interacts with the host (e.g., enters a prompt or command). The host collects this input and passes it to the client for processing.
- **Resource or Tool Use**
- The client may request additional context or resources from the server (such as files, database entries, or knowledge base articles) to enrich the model's understanding.
- If the model determines that a tool is needed (e.g., to fetch data, perform a calculation, or call an API), the client sends a tool invocation request to the server, specifying the tool name and parameters.
- **Server Execution**
The server receives the resource or tool request, executes the necessary operations (such as running a function, querying a database, or retrieving a file), and returns the results to the client in a structured format.
- **Response Generation**
The client integrates the server's responses (resource data, tool outputs, etc.) into the ongoing model interaction. The model uses this information to generate a comprehensive and contextually relevant response.
- **Result Presentation**
The host receives the final output from the client and presents it to the user, often including both the model's generated text and any results from tool executions or resource lookups.
This flow enables MCP to support advanced, interactive, and context-aware AI applications by seamlessly connecting models with external tools and data sources.
## Protocol Architecture & Layers
MCP consists of two distinct architectural layers that work together to provide a complete communication framework:
### Data Layer
The **Data Layer** implements the core MCP protocol using **JSON-RPC 2.0** as its foundation. This layer defines the message structure, semantics, and interaction patterns:
#### Core Components:
- **JSON-RPC 2.0 Protocol**: All communication uses standardized JSON-RPC 2.0 message format for method calls, responses, and notifications
- **Lifecycle Management**: Handles connection initialization, capability negotiation, and session termination between clients and servers
- **Server Primitives**: Enables servers to provide core functionality through tools, resources, and prompts
- **Client Primitives**: Enables servers to request sampling from LLMs, elicit user input, and send log messages
- **Real-time Notifications**: Supports asynchronous notifications for dynamic updates without polling
#### Key Features:
- **Protocol Version Negotiation**: Uses date-based versioning (YYYY-MM-DD) to ensure compatibility
- **Capability Discovery**: Clients and servers exchange supported feature information during initialization
- **Stateful Sessions**: Maintains connection state across multiple interactions for context continuity
### Transport Layer
The **Transport Layer** manages communication channels, message framing, and authentication between MCP participants:
#### Supported Transport Mechanisms:
1. **STDIO Transport**:
- Uses standard input/output streams for direct process communication
- Optimal for local processes on the same machine with no network overhead
- Commonly used for local MCP server implementations
2. **Streamable HTTP Transport**:
- Uses HTTP POST for client-to-server messages
- Optional Server-Sent Events (SSE) for server-to-client streaming
- Enables remote server communication across networks
- Supports standard HTTP authentication (bearer tokens, API keys, custom headers)
- MCP recommends OAuth for secure token-based authentication
#### Transport Abstraction:
The transport layer abstracts communication details from the data layer, enabling the same JSON-RPC 2.0 message format across all transport mechanisms. This abstraction allows applications to switch between local and remote servers seamlessly.
### Security Considerations
MCP implementations must adhere to several critical security principles to ensure safe, trustworthy, and secure interactions across all protocol operations:
- **User Consent and Control**: Users must provide explicit consent before any data is accessed or operations are performed. They should have clear control over what data is shared and which actions are authorized, supported by intuitive user interfaces for reviewing and approving activities.
- **Data Privacy**: User data should only be exposed with explicit consent and must be protected by appropriate access controls. MCP implementations must safeguard against unauthorized data transmission and ensure that privacy is maintained throughout all interactions.
- **Tool Safety**: Before invoking any tool, explicit user consent is required. Users should have a clear understanding of each tools functionality, and robust security boundaries must be enforced to prevent unintended or unsafe tool execution.
By following these security principles, MCP ensures user trust, privacy, and safety are maintained across all protocol interactions while enabling powerful AI integrations.
## Code Examples: Key Components
Below are code examples in several popular programming languages that illustrate how to implement key MCP server components and tools.
### .NET Example: Creating a Simple MCP Server with Tools
Here is a practical .NET code example demonstrating how to implement a simple MCP server with custom tools. This example showcases how to define and register tools, handle requests, and connect the server using the Model Context Protocol.
```csharp
using System;
using System.Threading.Tasks;
using ModelContextProtocol.Server;
using ModelContextProtocol.Server.Transport;
using ModelContextProtocol.Server.Tools;
public class WeatherServer
{
public static async Task Main(string[] args)
{
// Create an MCP server
var server = new McpServer(
name: "Weather MCP Server",
version: "1.0.0"
);
// Register our custom weather tool
server.AddTool<string, WeatherData>("weatherTool",
description: "Gets current weather for a location",
execute: async (location) => {
// Call weather API (simplified)
var weatherData = await GetWeatherDataAsync(location);
return weatherData;
});
// Connect the server using stdio transport
var transport = new StdioServerTransport();
await server.ConnectAsync(transport);
Console.WriteLine("Weather MCP Server started");
// Keep the server running until process is terminated
await Task.Delay(-1);
}
private static async Task<WeatherData> GetWeatherDataAsync(string location)
{
// This would normally call a weather API
// Simplified for demonstration
await Task.Delay(100); // Simulate API call
return new WeatherData {
Temperature = 72.5,
Conditions = "Sunny",
Location = location
};
}
}
public class WeatherData
{
public double Temperature { get; set; }
public string Conditions { get; set; }
public string Location { get; set; }
}
```
### Java Example: MCP Server Components
This example demonstrates the same MCP server and tool registration as the .NET example above, but implemented in Java.
```java
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpToolDefinition;
import io.modelcontextprotocol.server.transport.StdioServerTransport;
import io.modelcontextprotocol.server.tool.ToolExecutionContext;
import io.modelcontextprotocol.server.tool.ToolResponse;
public class WeatherMcpServer {
public static void main(String[] args) throws Exception {
// Create an MCP server
McpServer server = McpServer.builder()
.name("Weather MCP Server")
.version("1.0.0")
.build();
// Register a weather tool
server.registerTool(McpToolDefinition.builder("weatherTool")
.description("Gets current weather for a location")
.parameter("location", String.class)
.execute((ToolExecutionContext ctx) -> {
String location = ctx.getParameter("location", String.class);
// Get weather data (simplified)
WeatherData data = getWeatherData(location);
// Return formatted response
return ToolResponse.content(
String.format("Temperature: %.1f°F, Conditions: %s, Location: %s",
data.getTemperature(),
data.getConditions(),
data.getLocation())
);
})
.build());
// Connect the server using stdio transport
try (StdioServerTransport transport = new StdioServerTransport()) {
server.connect(transport);
System.out.println("Weather MCP Server started");
// Keep server running until process is terminated
Thread.currentThread().join();
}
}
private static WeatherData getWeatherData(String location) {
// Implementation would call a weather API
// Simplified for example purposes
return new WeatherData(72.5, "Sunny", location);
}
}
class WeatherData {
private double temperature;
private String conditions;
private String location;
public WeatherData(double temperature, String conditions, String location) {
this.temperature = temperature;
this.conditions = conditions;
this.location = location;
}
public double getTemperature() {
return temperature;
}
public String getConditions() {
return conditions;
}
public String getLocation() {
return location;
}
}
```
### Python Example: Building an MCP Server
This example uses fastmcp, so please ensure you install it first:
```python
pip install fastmcp
```
Code Sample:
```python
#!/usr/bin/env python3
import asyncio
from fastmcp import FastMCP
from fastmcp.transports.stdio import serve_stdio
# Create a FastMCP server
mcp = FastMCP(
name="Weather MCP Server",
version="1.0.0"
)
@mcp.tool()
def get_weather(location: str) -> dict:
"""Gets current weather for a location."""
return {
"temperature": 72.5,
"conditions": "Sunny",
"location": location
}
# Alternative approach using a class
class WeatherTools:
@mcp.tool()
def forecast(self, location: str, days: int = 1) -> dict:
"""Gets weather forecast for a location for the specified number of days."""
return {
"location": location,
"forecast": [
{"day": i+1, "temperature": 70 + i, "conditions": "Partly Cloudy"}
for i in range(days)
]
}
# Register class tools
weather_tools = WeatherTools()
# Start the server
if __name__ == "__main__":
asyncio.run(serve_stdio(mcp))
```
### JavaScript Example: Creating an MCP Server
This example shows MCP server creation in JavaScript and how to register two weather-related tools.
```javascript
// Using the official Model Context Protocol SDK
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod"; // For parameter validation
// Create an MCP server
const server = new McpServer({
name: "Weather MCP Server",
version: "1.0.0"
});
// Define a weather tool
server.tool(
"weatherTool",
{
location: z.string().describe("The location to get weather for")
},
async ({ location }) => {
// This would normally call a weather API
// Simplified for demonstration
const weatherData = await getWeatherData(location);
return {
content: [
{
type: "text",
text: `Temperature: ${weatherData.temperature}°F, Conditions: ${weatherData.conditions}, Location: ${weatherData.location}`
}
]
};
}
);
// Define a forecast tool
server.tool(
"forecastTool",
{
location: z.string(),
days: z.number().default(3).describe("Number of days for forecast")
},
async ({ location, days }) => {
// This would normally call a weather API
// Simplified for demonstration
const forecast = await getForecastData(location, days);
return {
content: [
{
type: "text",
text: `${days}-day forecast for ${location}: ${JSON.stringify(forecast)}`
}
]
};
}
);
// Helper functions
async function getWeatherData(location) {
// Simulate API call
return {
temperature: 72.5,
conditions: "Sunny",
location: location
};
}
async function getForecastData(location, days) {
// Simulate API call
return Array.from({ length: days }, (_, i) => ({
day: i + 1,
temperature: 70 + Math.floor(Math.random() * 10),
conditions: i % 2 === 0 ? "Sunny" : "Partly Cloudy"
}));
}
// Connect the server using stdio transport
const transport = new StdioServerTransport();
server.connect(transport).catch(console.error);
console.log("Weather MCP Server started");
```
This JavaScript example demonstrates how to create an MCP server using the Model Context Protocol SDK. It shows how to register two tools named `weatherTool` and `forecastTool` and make them available to MCP clients through the `StdioServerTransport`.
## Security and Authorization
MCP includes several built-in concepts and mechanisms for managing security and authorization throughout the protocol:
1. **Tool Permission Control**:
Clients can specify which tools a model is allowed to use during a session. This ensures that only explicitly authorized tools are accessible, reducing the risk of unintended or unsafe operations. Permissions can be configured dynamically based on user preferences, organizational policies, or the context of the interaction.
2. **Authentication**:
Servers can require authentication before granting access to tools, resources, or sensitive operations. This may involve API keys, OAuth tokens, or other authentication schemes. Proper authentication ensures that only trusted clients and users can invoke server-side capabilities.
3. **Validation**:
Parameter validation is enforced for all tool invocations. Each tool defines the expected types, formats, and constraints for its parameters, and the server validates incoming requests accordingly. This prevents malformed or malicious input from reaching tool implementations and helps maintain the integrity of operations.
4. **Rate Limiting**:
To prevent abuse and ensure fair usage of server resources, MCP servers can implement rate limiting for tool calls and resource access. Rate limits can be applied per user, per session, or globally, and help protect against denial-of-service attacks or excessive resource consumption.
By combining these mechanisms, MCP provides a secure foundation for integrating language models with external tools and data sources, while giving users and developers fine-grained control over access and usage.
## Protocol Messages & Communication Flow
MCP communication uses structured **JSON-RPC 2.0** messages to facilitate clear and reliable interactions between hosts, clients, and servers. The protocol defines specific message patterns for different types of operations:
### Core Message Types:
#### **Initialization Messages**
- **`initialize` Request**: Establishes connection and negotiates protocol version and capabilities
- **`initialize` Response**: Confirms supported features and server information
- **`notifications/initialized`**: Signals that initialization is complete and the session is ready
#### **Discovery Messages**
- **`tools/list` Request**: Discovers available tools from the server
- **`resources/list` Request**: Lists available resources (data sources)
- **`prompts/list` Request**: Retrieves available prompt templates
#### **Execution Messages**
- **`tools/call` Request**: Executes a specific tool with provided parameters
- **`resources/read` Request**: Retrieves content from a specific resource
- **`prompts/get` Request**: Fetches a prompt template with optional parameters
#### **Client-side Messages**
- **`sampling/complete` Request**: Server requests LLM completion from the client
- **`elicitation/request`**: Server requests user input through the client interface
- **Logging Messages**: Server sends structured log messages to the client
#### **Notification Messages**
- **`notifications/tools/list_changed`**: Server notifies client of tool changes
- **`notifications/resources/list_changed`**: Server notifies client of resource changes
- **`notifications/prompts/list_changed`**: Server notifies client of prompt changes
### Message Structure:
All MCP messages follow JSON-RPC 2.0 format with:
- **Request Messages**: Include `id`, `method`, and optional `params`
- **Response Messages**: Include `id` and either `result` or `error`
- **Notification Messages**: Include `method` and optional `params` (no `id` or response expected)
This structured communication ensures reliable, traceable, and extensible interactions supporting advanced scenarios like real-time updates, tool chaining, and robust error handling.
### Tasks (Experimental)
> **Looking ahead:** the `2026-07-28` release candidate graduates Tasks out of the experimental core specification into a dedicated Tasks extension with a redesigned lifecycle (`tasks/get`, `tasks/update`, `tasks/cancel`; `tasks/list` is removed). If you build against the experimental API described below, plan to migrate. See [What's Changing in MCP: The 2026-07-28 Release Candidate](./mcp-2026-07-28-release-candidate.md).
**Tasks** are an experimental feature that provides durable execution wrappers enabling deferred result retrieval and status tracking for MCP requests:
- **Long-Running Operations**: Track expensive computations, workflow automation, and batch processing
- **Deferred Results**: Poll for task status and retrieve results when operations complete
- **Status Tracking**: Monitor task progress through defined lifecycle states
- **Multi-Step Operations**: Support complex workflows that span multiple interactions
Tasks wrap standard MCP requests to enable asynchronous execution patterns for operations that cannot complete immediately.
## Key Takeaways
- **Architecture**: MCP uses a client-server architecture where hosts manage multiple client connections to servers
- **Participants**: The ecosystem includes hosts (AI applications), clients (protocol connectors), and servers (capability providers)
- **Transport Mechanisms**: Communication supports STDIO (local) and Streamable HTTP with optional SSE (remote)
- **Core Primitives**: Servers expose tools (executable functions), resources (data sources), and prompts (templates)
- **Client Primitives**: Servers can request sampling (LLM completions with tool calling support), elicitation (user input including URL mode), roots (filesystem boundaries), and logging from clients
- **Experimental Features**: Tasks provide durable execution wrappers for long-running operations
- **Protocol Foundation**: Built on JSON-RPC 2.0 with date-based versioning (current: 2025-11-25)
- **Real-time Capabilities**: Supports notifications for dynamic updates and real-time synchronization
- **Security First**: Explicit user consent, data privacy protection, and secure transport are core requirements
## Exercise
Design a simple MCP tool that would be useful in your domain. Define:
1. What the tool would be named
2. What parameters it would accept
3. What output it would return
4. How a model might use this tool to solve user problems
---
## What's next
Next: [Chapter 2: Security](../02-Security/README.md)
Curious what's coming after `2025-11-25`? Read [What's Changing in MCP: The 2026-07-28 Release Candidate](./mcp-2026-07-28-release-candidate.md).
@@ -0,0 +1,202 @@
# What's Changing in MCP: The 2026-07-28 Release Candidate
> **Status:** Release Candidate. The `2026-07-28` specification is not final at the time of writing. It was announced May 21, 2026, and is scheduled to ship July 28, 2026. Everything in this lesson describes the release candidate; check the [draft specification](https://modelcontextprotocol.io/specification/draft) and its [changelog](https://modelcontextprotocol.io/specification/draft/changelog) for the latest status before you build against it. The rest of this curriculum is written against the current stable release, **MCP Specification 2025-11-25**, and will be updated once `2026-07-28` ships.
## Overview
`2026-07-28` is the largest revision of MCP since it launched. Six Specification Enhancement Proposals (SEPs) remove protocol-level sessions and make MCP stateless at the transport layer, extensions become a first-class, versioned mechanism, and several features you've learned earlier in this curriculum (Roots, Sampling, Logging) are marked deprecated under a new lifecycle policy. This lesson summarizes what's changing, why it matters, and what it means for the code you've already written against `2025-11-25`.
Source: [The 2026-07-28 MCP Specification Release Candidate](https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/) (Model Context Protocol Blog, David Soria Parra and Den Delimarsky).
## Learning Objectives
By the end of this lesson, you will be able to:
- Explain why MCP is moving to a stateless protocol core and what problem it solves for horizontally scaled deployments.
- Describe how the `initialize`/`initialized` handshake and `Mcp-Session-Id` header are replaced.
- Identify the new `Mcp-Method` and `Mcp-Name` headers and the `ttlMs`/`cacheScope` caching metadata.
- Recognize the Extensions framework and the two extensions shipping with this release: MCP Apps and Tasks.
- List the six authorization SEPs that harden OAuth 2.0 / OIDC alignment.
- Identify which core features (Roots, Sampling, Logging) are now deprecated, and what that means in practice.
- Explain the Full JSON Schema 2020-12 change for tool `inputSchema`/`outputSchema`.
## A Stateless Protocol
The headline change: MCP becomes stateless at the protocol layer.
### Before (2025-11-25): sessions pin you to one server instance
Calling a tool over Streamable HTTP starts with an `initialize` handshake. The server responds with an `Mcp-Session-Id` header that every subsequent request must carry:
```http
POST /mcp HTTP/1.1
Mcp-Session-Id: 1868a90c-3a3f-4f5b
Content-Type: application/json
```
Because the session is bound to whichever server instance issued it, horizontally scaled deployments need **sticky routing** at the load balancer and a **shared session store** across instances.
### After (2026-07-28): every request is self-contained
```http
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json
```
Any server instance can handle this request. Key changes:
- **The `initialize`/`initialized` handshake is removed** ([SEP-2575](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2575)). Protocol version, client info, and client capabilities move into `_meta` on every request. A new `server/discover` method lets a client fetch server capabilities up front when it needs them.
- **The `Mcp-Session-Id` header and protocol-level session are removed** ([SEP-2567](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2567)). Sticky routing and shared session stores are no longer required at the protocol layer.
### Stateless protocol, stateful applications
Removing the protocol-level session doesn't mean your server can't be stateful. The recommended pattern is the same one HTTP APIs have always used: mint an explicit handle (a `basket_id`, a `browser_id`) from one tool call, and have the model pass that handle back as an ordinary argument on later calls.
```mermaid
sequenceDiagram
participant Model
participant Server
Model->>Server: tools/call create_basket
Server->>Model: result { basket_id: "b_123" }
Model->>Server: tools/call add_item(basket_id: "b_123", item: "otter plushie")
Server->>Model: result { ok: true }
```
This makes state visible and reasonable to the model instead of hiding it in transport metadata, and it lets any server instance handle any call.
### Server-to-client requests, restructured
A stateless protocol still needs a way for a server to ask the client for something mid-call (for example, an elicitation prompt):
- **Server-initiated requests may only be issued while the server is actively processing a client request** ([SEP-2260](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2260)) — previously a recommendation, now required. A user is never prompted out of nowhere.
- **Multi Round-Trip Requests** ([SEP-2322](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)) replace holding an SSE stream open. Instead, the server returns an `InputRequiredResult`:
```json
{
"resultType": "inputRequired",
"inputRequests": {
"confirm": {
"type": "elicitation",
"message": "Delete 3 files?",
"schema": { "type": "boolean" }
}
},
"requestState": "eyJzdGVwIjoxLCJmaWxlcyI6WyJhIiwiYiIsImMiXX0="
}
```
The client collects the answers and re-issues the original call with `inputResponses` plus the echoed `requestState`. Any server instance can pick up the retry because everything needed is in the payload.
### Routable, cacheable, traceable
Three smaller changes make stateless traffic easier to operate:
- **`Mcp-Method` and `Mcp-Name` headers are required on Streamable HTTP** ([SEP-2243](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2243)), so load balancers, gateways, and rate limiters can route on the operation without inspecting the JSON body. Servers reject requests where headers and body disagree.
- **`tools/list` and resource read results carry `ttlMs` and `cacheScope`** ([SEP-2549](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2549)), modeled on HTTP `Cache-Control`. Clients know how long a list result is fresh and whether it's safe to share across users, without needing a long-lived SSE stream to learn about changes.
- **W3C Trace Context propagation in `_meta` is documented** ([SEP-414](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/414)), fixing the `traceparent`, `tracestate`, and `baggage` key names so a distributed trace can follow a call across the client SDK, the MCP server, and downstream systems in an [OpenTelemetry](https://opentelemetry.io/)-compatible backend.
## Extensions Become First-Class
Extensions existed informally in `2025-11-25`. [SEP-2133](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2133) formalizes them:
- Extensions are identified by reverse-DNS IDs.
- They're negotiated through an `extensions` map on client and server capabilities.
- They live in their own `ext-*` repositories with delegated maintainers and version independently of the core specification.
- A new Extensions Track in the SEP process gives them a path from experimental to official.
This release ships two official extensions.
### MCP Apps: server-rendered user interfaces
[MCP Apps](https://blog.modelcontextprotocol.io/posts/2026-01-26-mcp-apps/) ([SEP-1865](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1865)) lets servers ship interactive HTML interfaces that hosts render in a sandboxed iframe. Tools declare their UI templates ahead of time so hosts can prefetch, cache, and security-review them before anything runs. You already covered the fundamentals of this in [Lesson 15: MCP Apps](../03-GettingStarted/15-mcp-apps/README.md) — under the Extensions framework, MCP Apps is now formally an extension rather than an experimental core feature.
### Tasks graduates to an extension
Tasks shipped as an experimental core feature in `2025-11-25`. Production use surfaced enough redesign that the right home for it is an extension: the [Tasks extension](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2663) reshapes the lifecycle around the stateless model — a server can answer `tools/call` with a task handle, and the client drives it forward with `tasks/get`, `tasks/update`, and `tasks/cancel`. Task creation is server-directed: the client advertises the extension, and the server decides when a call should run as a task. `tasks/list` is removed entirely because it can't be scoped safely without sessions.
> **Migration note:** if you implemented the experimental `2025-11-25` Tasks API, you'll need to migrate to the new extension lifecycle — it is not backward compatible.
## Authorization Hardening
Six SEPs harden the [authorization specification](https://modelcontextprotocol.io/specification/draft/basic/authorization) to align more closely with real-world OAuth 2.0 / OpenID Connect deployments:
| SEP | Change |
|---|---|
| [SEP-2468](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2468) | Clients must validate the `iss` parameter on authorization responses per [RFC 9207](https://www.rfc-editor.org/rfc/rfc9207), mitigating mix-up attacks common in MCP's single-client, many-server pattern. A future version will require rejecting responses missing `iss`. |
| [SEP-837](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/837) | Clients declare their OpenID Connect `application_type` during Dynamic Client Registration, avoiding authorization servers defaulting a desktop/CLI client to `"web"` and rejecting its localhost redirect URI. |
| [SEP-2352](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2352) | Clients bind registered credentials to the issuing authorization server's `issuer` and re-register when a resource migrates between authorization servers. |
| [SEP-2207](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2207) | Documents how to request refresh tokens from OpenID Connect-style authorization servers. |
| [SEP-2350](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2350) | Clarifies scope accumulation during step-up authorization. |
| [SEP-2351](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2351) | Clarifies the `.well-known` discovery suffix. |
If you're building an authorization server for MCP today, start supplying `iss` on authorization responses now — see [02-Security](../02-Security/README.md) for the current authorization guidance this will build on.
## Roots, Sampling, and Logging Are Deprecated
Under the new [feature lifecycle policy](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577) ([SEP-2577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577)), three core client primitives you learned about in [Core Concepts](./README.md#roots) move to **Deprecated** status:
| Feature | Recommended replacement |
|---|---|
| Roots | Tool parameters, resource URIs, or server configuration |
| Sampling | Direct integration with LLM provider APIs |
| Logging | `stderr` for stdio transports; OpenTelemetry for structured observability |
These are **annotation-only deprecations**: the methods, types, and capability flags keep working in this release and in every specification version published within a year of it. Removing any of them outright will require a separate SEP under the lifecycle policy — so nothing breaks in your existing [Sampling](../03-GettingStarted/14-sampling/README.md) samples today, but new servers should prefer the replacement patterns above.
## Full JSON Schema 2020-12 for Tools
Tool `inputSchema` and `outputSchema` are lifted to full [JSON Schema 2020-12](https://json-schema.org/draft/2020-12) ([SEP-2106](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2106)):
- Input schemas keep the `type: "object"` root constraint but now allow composition (`oneOf`, `anyOf`, `allOf`), conditionals, and references (`$ref`, `$defs`).
- Output schemas are unrestricted, and `structuredContent` can now be any JSON value rather than only an object.
- Implementations must not auto-dereference external `$ref` URIs and should bound schema depth and validation time (a denial-of-service consideration to account for if you validate schemas server-side).
Separately, the error code for a missing resource changes from the MCP-custom `-32002` to the JSON-RPC standard `-32602` (Invalid Params) ([SEP-2164](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2164)). If your client matches on the literal `-32002` value, you'll need to update it.
## How the Protocol Evolves From Here
This release contains breaking changes, which the MCP maintainers don't intend to be the norm going forward. Three governance SEPs aim to prevent a repeat:
- The **feature lifecycle policy** gives every feature an Active → Deprecated → Removed path with at least twelve months between deprecation and the earliest possible removal.
- The **Extensions framework** lets new capabilities ship as opt-in extensions and stabilize there before (if ever) moving into the core specification.
- A Standards Track SEP can no longer reach Final status until a matching scenario lands in the [conformance suite](https://github.com/modelcontextprotocol/conformance) ([SEP-2484](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2484)) — the same suite the [SDK tier system](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/1777) scores official SDKs against.
## Release Timeline and Validation
- The release candidate was locked May 21, 2026.
- The final specification is scheduled for July 28, 2026.
- The ten-week window between the two lets SDK maintainers and client implementers validate the changes against real workloads; Tier 1 SDKs are expected to ship support within this window under the [SDK tier system](https://modelcontextprotocol.io/docs/sdk).
- Track the full set of changes in the [draft specification](https://modelcontextprotocol.io/specification/draft) and its [changelog](https://modelcontextprotocol.io/specification/draft/changelog).
## What This Means for This Curriculum
Everything you've learned so far in this course targets **2025-11-25**, which remains the current stable specification until `2026-07-28` ships. Concretely:
- **Sessions and the `initialize` handshake** (covered in [Core Concepts](./README.md) and [Lesson 6: HTTP Streaming](../03-GettingStarted/06-http-streaming/README.md)) still work as documented today, but expect them to be replaced by the stateless request model above once you upgrade to `2026-07-28`-compatible SDKs.
- **Sampling and Roots** (also covered in [Core Concepts](./README.md)) remain fully functional but are deprecated — new designs should prefer the replacement patterns listed above.
- **The experimental Tasks feature**, if you've used it, will need migrating to the Tasks extension's new lifecycle.
- **MCP Apps** ([Lesson 15](../03-GettingStarted/15-mcp-apps/README.md)) is unaffected in practice; it simply moves under the formal Extensions framework.
## Additional Resources
- [The 2026-07-28 MCP Specification Release Candidate (blog post)](https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/)
- [The Future of MCP Transports](https://blog.modelcontextprotocol.io/posts/2025-12-19-mcp-transport-future/)
- [MCP Draft Specification](https://modelcontextprotocol.io/specification/draft)
- [MCP Draft Changelog](https://modelcontextprotocol.io/specification/draft/changelog)
- [SEP Guidelines](https://modelcontextprotocol.io/community/sep-guidelines)
- [MCP SDK Tier System](https://modelcontextprotocol.io/docs/sdk)
## Next Steps
Head back to [Core Concepts](./README.md) or continue to [Security](../02-Security/README.md) to see how today's `2025-11-25` guidance maps onto what's coming.
- [MCP SDK Tier System](https://modelcontextprotocol.io/docs/sdk)
## Next Steps
Head back to [Core Concepts](./README.md) or continue to [Security](../02-Security/README.md) to see how today's `2025-11-25` guidance maps onto what's coming.
+505
View File
@@ -0,0 +1,505 @@
# MCP Security: Comprehensive Protection for AI Systems
[![MCP Security Best Practices](../images/video-thumbnails/03.png)](https://youtu.be/88No8pw706o)
_(Click the image above to view video of this lesson)_
Security is fundamental to AI system design, which is why we prioritize it as our second section. This aligns with Microsoft's **Secure by Design** principle from the [Secure Future Initiative](https://www.microsoft.com/security/blog/2025/04/17/microsofts-secure-by-design-journey-one-year-of-success/).
The Model Context Protocol (MCP) brings powerful new capabilities to AI-driven applications while introducing unique security challenges that extend beyond traditional software risks. MCP systems face both established security concerns (secure coding, least privilege, supply chain security) and new AI-specific threats including prompt injection, tool poisoning, session hijacking, confused deputy attacks, token passthrough vulnerabilities, and dynamic capability modification.
This lesson explores the most critical security risks in MCP implementations—covering authentication, authorization, excessive permissions, indirect prompt injection, session security, confused deputy problems, token management, and supply chain vulnerabilities. You'll learn actionable controls and best practices to mitigate these risks while leveraging Microsoft solutions like Prompt Shields, Azure Content Safety, and GitHub Advanced Security to strengthen your MCP deployment.
## Learning Objectives
By the end of this lesson, you will be able to:
- **Identify MCP-Specific Threats**: Recognize unique security risks in MCP systems including prompt injection, tool poisoning, excessive permissions, session hijacking, confused deputy problems, token passthrough vulnerabilities, and supply chain risks
- **Apply Security Controls**: Implement effective mitigations including robust authentication, least privilege access, secure token management, session security controls, and supply chain verification
- **Leverage Microsoft Security Solutions**: Understand and deploy Microsoft Prompt Shields, Azure Content Safety, and GitHub Advanced Security for MCP workload protection
- **Validate Tool Security**: Recognize the importance of tool metadata validation, monitoring for dynamic changes, and defending against indirect prompt injection attacks
- **Integrate Best Practices**: Combine established security fundamentals (secure coding, server hardening, zero trust) with MCP-specific controls for comprehensive protection
# MCP Security Architecture & Controls
Modern MCP implementations require layered security approaches that address both traditional software security and AI-specific threats. The rapidly evolving MCP specification continues to mature its security controls, enabling better integration with enterprise security architectures and established best practices.
Research from the [Microsoft Digital Defense Report](https://aka.ms/mddr) demonstrates that **98% of reported breaches would be prevented by robust security hygiene**. The most effective protection strategy combines foundational security practices with MCP-specific controls—proven baseline security measures remain the most impactful in reducing overall security risk.
## Current Security Landscape
> **Note:** This information reflects MCP security standards as of **February 5, 2026**, aligned with **MCP Specification 2025-11-25**. The MCP protocol continues evolving rapidly, and future implementations may introduce new authentication patterns and enhanced controls. Always refer to the current [MCP Specification](https://spec.modelcontextprotocol.io/), [MCP GitHub repository](https://github.com/modelcontextprotocol), and [security best practices documentation](https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices) for the latest guidance.
> **Looking ahead:** the `2026-07-28` release candidate hardens authorization further — clients must validate the `iss` parameter on authorization responses (RFC 9207), declare an OpenID Connect `application_type` during Dynamic Client Registration, and bind registered credentials to the issuing authorization server. See [What's Changing in MCP: The 2026-07-28 Release Candidate](../01-CoreConcepts/mcp-2026-07-28-release-candidate.md) for the full list of authorization SEPs.
## 🏔️ MCP Security Summit Workshop (Sherpa)
For **hands-on security training**, we highly recommend the **MCP Security Summit Workshop** (Sherpa) - a comprehensive guided expedition to securing MCP servers in Microsoft Azure.
### Workshop Overview
The [MCP Security Summit Workshop](https://azure-samples.github.io/sherpa/) provides practical, actionable security training through a proven "vulnerable → exploit → fix → validate" methodology. You'll:
- **Learn by Breaking Things**: Experience vulnerabilities firsthand by exploiting intentionally insecure servers
- **Use Azure-Native Security**: Leverage Azure Entra ID, Key Vault, API Management, and AI Content Safety
- **Follow Defense-in-Depth**: Progress through camps building comprehensive security layers
- **Apply OWASP Standards**: Every technique maps to the [OWASP MCP Azure Security Guide](https://microsoft.github.io/mcp-azure-security-guide/)
- **Get Production Code**: Walk away with working, tested implementations
### The Expedition Route
| Camp | Focus | OWASP Risks Covered |
|------|-------|---------------------|
| **Base Camp** | MCP fundamentals & authentication vulnerabilities | MCP01, MCP07 |
| **Camp 1: Identity** | OAuth 2.1, Azure Managed Identity, Key Vault | MCP01, MCP02, MCP07 |
| **Camp 2: Gateway** | API Management, Private Endpoints, governance | MCP02, MCP06, MCP07, MCP09 |
| **Camp 3: I/O Security** | Prompt injection, PII protection, content safety | MCP03, MCP05, MCP06, MCP10 |
| **Camp 4: Monitoring** | Log Analytics, dashboards, threat detection | MCP04, MCP08 |
| **The Summit** | Red Team / Blue Team integration test | All |
**Get Started**: [https://azure-samples.github.io/sherpa/](https://azure-samples.github.io/sherpa/)
## OWASP MCP Top 10 Security Risks
The [OWASP MCP Azure Security Guide](https://microsoft.github.io/mcp-azure-security-guide/) details the ten most critical security risks for MCP implementations:
| Risk | Description | Azure Mitigation |
|------|-------------|------------------|
| **MCP01** | Token Mismanagement & Secret Exposure | Azure Key Vault, Managed Identity |
| **MCP02** | Privilege Escalation via Scope Creep | RBAC, Conditional Access |
| **MCP03** | Tool Poisoning | Tool validation, integrity verification |
| **MCP04** | Software Supply Chain Attacks & Dependency Tampering | GitHub Advanced Security, dependency scanning |
| **MCP05** | Command Injection & Execution | Input validation, sandboxing |
| **MCP06** | Intent Flow Subversion | Azure AI Content Safety, Prompt Shields |
| **MCP07** | Insufficient Authentication & Authorization | Azure Entra ID, OAuth 2.1 with PKCE |
| **MCP08** | Lack of Audit and Telemetry | Azure Monitor, Application Insights |
| **MCP09** | Shadow MCP Servers | API Center governance, network isolation |
| **MCP10** | Context Injection & Over-Sharing | Data classification, minimal exposure |
### Evolution of MCP Authentication
The MCP specification has evolved significantly in its approach to authentication and authorization:
- **Original Approach**: Early specifications required developers to implement custom authentication servers, with MCP servers acting as OAuth 2.0 Authorization Servers managing user authentication directly
- **Current Standard (2025-11-25)**: Updated specification allows MCP servers to delegate authentication to external identity providers (such as Microsoft Entra ID), improving security posture and reducing implementation complexity
- **Transport Layer Security**: Enhanced support for secure transport mechanisms with proper authentication patterns for both local (STDIO) and remote (Streamable HTTP) connections
## Authentication & Authorization Security
### Current Security Challenges
Modern MCP implementations face several authentication and authorization challenges:
### Risks & Threat Vectors
- **Misconfigured Authorization Logic**: Flawed authorization implementation in MCP servers can expose sensitive data and incorrectly apply access controls
- **OAuth Token Compromise**: Local MCP server token theft enables attackers to impersonate servers and access downstream services
- **Token Passthrough Vulnerabilities**: Improper token handling creates security control bypasses and accountability gaps
- **Excessive Permissions**: Over-privileged MCP servers violate least privilege principles and expand attack surfaces
#### Token Passthrough: A Critical Anti-Pattern
**Token passthrough is explicitly prohibited** in the current MCP authorization specification due to severe security implications:
##### Security Control Circumvention
- MCP servers and downstream APIs implement critical security controls (rate limiting, request validation, traffic monitoring) that depend on proper token validation
- Direct client-to-API token usage bypasses these essential protections, undermining the security architecture
##### Accountability & Audit Challenges
- MCP servers cannot distinguish between clients using upstream-issued tokens, breaking audit trails
- Downstream resource server logs show misleading request origins rather than actual MCP server intermediaries
- Incident investigation and compliance auditing become significantly more difficult
##### Data Exfiltration Risks
- Unvalidated token claims enable malicious actors with stolen tokens to use MCP servers as proxies for data exfiltration
- Trust boundary violations allow unauthorized access patterns that bypass intended security controls
##### Multi-Service Attack Vectors
- Compromised tokens accepted by multiple services enable lateral movement across connected systems
- Trust assumptions between services may be violated when token origins cannot be verified
### Security Controls & Mitigations
**Critical Security Requirements:**
> **MANDATORY**: MCP servers **MUST NOT** accept any tokens that were not explicitly issued for the MCP server
#### Authentication & Authorization Controls
- **Rigorous Authorization Review**: Conduct comprehensive audits of MCP server authorization logic to ensure only intended users and clients can access sensitive resources
- **Implementation Guide**: [Azure API Management as Authentication Gateway for MCP Servers](https://techcommunity.microsoft.com/blog/integrationsonazureblog/azure-api-management-your-auth-gateway-for-mcp-servers/4402690)
- **Identity Integration**: [Using Microsoft Entra ID for MCP Server Authentication](https://den.dev/blog/mcp-server-auth-entra-id-session/)
- **Secure Token Management**: Implement [Microsoft's token validation and lifecycle best practices](https://learn.microsoft.com/en-us/entra/identity-platform/access-tokens)
- Validate token audience claims match MCP server identity
- Implement proper token rotation and expiration policies
- Prevent token replay attacks and unauthorized usage
- **Protected Token Storage**: Secure token storage with encryption both at rest and in transit
- **Best Practices**: [Secure Token Storage and Encryption Guidelines](https://youtu.be/uRdX37EcCwg?si=6fSChs1G4glwXRy2)
#### Access Control Implementation
- **Principle of Least Privilege**: Grant MCP servers only minimum permissions required for intended functionality
- Regular permission reviews and updates to prevent privilege creep
- **Microsoft Documentation**: [Secure Least-Privileged Access](https://learn.microsoft.com/entra/identity-platform/secure-least-privileged-access)
- **Role-Based Access Control (RBAC)**: Implement fine-grained role assignments
- Scope roles tightly to specific resources and actions
- Avoid broad or unnecessary permissions that expand attack surfaces
- **Continuous Permission Monitoring**: Implement ongoing access auditing and monitoring
- Monitor permission usage patterns for anomalies
- Promptly remediate excessive or unused privileges
## AI-Specific Security Threats
### Prompt Injection & Tool Manipulation Attacks
Modern MCP implementations face sophisticated AI-specific attack vectors that traditional security measures cannot fully address:
#### **Indirect Prompt Injection (Cross-Domain Prompt Injection)**
**Indirect Prompt Injection** represents one of the most critical vulnerabilities in MCP-enabled AI systems. Attackers embed malicious instructions within external content—documents, web pages, emails, or data sources—that AI systems subsequently process as legitimate commands.
**Attack Scenarios:**
- **Document-based Injection**: Malicious instructions hidden in processed documents that trigger unintended AI actions
- **Web Content Exploitation**: Compromised web pages containing embedded prompts that manipulate AI behavior when scraped
- **Email-based Attacks**: Malicious prompts in emails that cause AI assistants to leak information or perform unauthorized actions
- **Data Source Contamination**: Compromised databases or APIs serving tainted content to AI systems
**Real-World Impact**: These attacks can result in data exfiltration, privacy breaches, generation of harmful content, and manipulation of user interactions. For detailed analysis, see [Prompt Injection in MCP (Simon Willison)](https://simonwillison.net/2025/Apr/9/mcp-prompt-injection/).
![Prompt Injection Attack Diagram](../images/02-Security/prompt-injection.png)
#### **Tool Poisoning Attacks**
**Tool Poisoning** targets the metadata that defines MCP tools, exploiting how LLMs interpret tool descriptions and parameters to make execution decisions.
**Attack Mechanisms:**
- **Metadata Manipulation**: Attackers inject malicious instructions into tool descriptions, parameter definitions, or usage examples
- **Invisible Instructions**: Hidden prompts in tool metadata that are processed by AI models but invisible to human users
- **Dynamic Tool Modification ("Rug Pulls")**: Tools approved by users are later modified to perform malicious actions without user awareness
- **Parameter Injection**: Malicious content embedded in tool parameter schemas that influence model behavior
**Hosted Server Risks**: Remote MCP servers present elevated risks as tool definitions can be updated after initial user approval, creating scenarios where previously safe tools become malicious. For comprehensive analysis, see [Tool Poisoning Attacks (Invariant Labs)](https://invariantlabs.ai/blog/mcp-security-notification-tool-poisoning-attacks).
![Tool Injection Attack Diagram](../images/02-Security/tool-injection.png)
#### **Additional AI Attack Vectors**
- **Cross-Domain Prompt Injection (XPIA)**: Sophisticated attacks that leverage content from multiple domains to bypass security controls
- **Dynamic Capability Modification**: Real-time changes to tool capabilities that escape initial security assessments
- **Context Window Poisoning**: Attacks that manipulate large context windows to hide malicious instructions
- **Model Confusion Attacks**: Exploiting model limitations to create unpredictable or unsafe behaviors
### AI Security Risk Impact
**High-Impact Consequences:**
- **Data Exfiltration**: Unauthorized access and theft of sensitive enterprise or personal data
- **Privacy Breaches**: Exposure of personally identifiable information (PII) and confidential business data
- **System Manipulation**: Unintended modifications to critical systems and workflows
- **Credential Theft**: Compromise of authentication tokens and service credentials
- **Lateral Movement**: Use of compromised AI systems as pivots for broader network attacks
### Microsoft AI Security Solutions
#### **AI Prompt Shields: Advanced Protection Against Injection Attacks**
Microsoft **AI Prompt Shields** provide comprehensive defense against both direct and indirect prompt injection attacks through multiple security layers:
##### **Core Protection Mechanisms:**
1. **Advanced Detection & Filtering**
- Machine learning algorithms and NLP techniques detect malicious instructions in external content
- Real-time analysis of documents, web pages, emails, and data sources for embedded threats
- Contextual understanding of legitimate vs. malicious prompt patterns
2. **Spotlighting Techniques**
- Distinguishes between trusted system instructions and potentially compromised external inputs
- Text transformation methods that enhance model relevance while isolating malicious content
- Helps AI systems maintain proper instruction hierarchy and ignore injected commands
3. **Delimiter & Datamarking Systems**
- Explicit boundary definition between trusted system messages and external input text
- Special markers highlight boundaries between trusted and untrusted data sources
- Clear separation prevents instruction confusion and unauthorized command execution
4. **Continuous Threat Intelligence**
- Microsoft continuously monitors emerging attack patterns and updates defenses
- Proactive threat hunting for new injection techniques and attack vectors
- Regular security model updates to maintain effectiveness against evolving threats
5. **Azure Content Safety Integration**
- Part of comprehensive Azure AI Content Safety suite
- Additional detection for jailbreak attempts, harmful content, and security policy violations
- Unified security controls across AI application components
**Implementation Resources**: [Microsoft Prompt Shields Documentation](https://learn.microsoft.com/azure/ai-services/content-safety/concepts/jailbreak-detection)
![Microsoft Prompt Shields Protection](../images/02-Security/prompt-shield.png)
## Advanced MCP Security Threats
### Session Hijacking Vulnerabilities
**Session hijacking** represents a critical attack vector in stateful MCP implementations where unauthorized parties obtain and abuse legitimate session identifiers to impersonate clients and perform unauthorized actions.
#### **Attack Scenarios & Risks**
- **Session Hijack Prompt Injection**: Attackers with stolen session IDs inject malicious events into servers sharing session state, potentially triggering harmful actions or accessing sensitive data
- **Direct Impersonation**: Stolen session IDs enable direct MCP server calls that bypass authentication, treating attackers as legitimate users
- **Compromised Resumable Streams**: Attackers can terminate requests prematurely, causing legitimate clients to resume with potentially malicious content
#### **Security Controls for Session Management**
**Critical Requirements:**
- **Authorization Verification**: MCP servers implementing authorization **MUST** verify ALL inbound requests and **MUST NOT** rely on sessions for authentication
- **Secure Session Generation**: Use cryptographically secure, non-deterministic session IDs generated with secure random number generators
- **User-Specific Binding**: Bind session IDs to user-specific information using formats like `<user_id>:<session_id>` to prevent cross-user session abuse
- **Session Lifecycle Management**: Implement proper expiration, rotation, and invalidation to limit vulnerability windows
- **Transport Security**: Mandatory HTTPS for all communication to prevent session ID interception
### Confused Deputy Problem
The **confused deputy problem** occurs when MCP servers act as authentication proxies between clients and third-party services, creating opportunities for authorization bypass through static client ID exploitation.
#### **Attack Mechanics & Risks**
- **Cookie-based Consent Bypass**: Previous user authentication creates consent cookies that attackers exploit through malicious authorization requests with crafted redirect URIs
- **Authorization Code Theft**: Existing consent cookies may cause authorization servers to skip consent screens, redirecting codes to attacker-controlled endpoints
- **Unauthorized API Access**: Stolen authorization codes enable token exchange and user impersonation without explicit approval
#### **Mitigation Strategies**
**Mandatory Controls:**
- **Explicit Consent Requirements**: MCP proxy servers using static client IDs **MUST** obtain user consent for each dynamically registered client
- **OAuth 2.1 Security Implementation**: Follow current OAuth security best practices including PKCE (Proof Key for Code Exchange) for all authorization requests
- **Strict Client Validation**: Implement rigorous validation of redirect URIs and client identifiers to prevent exploitation
### Token Passthrough Vulnerabilities
**Token passthrough** represents an explicit anti-pattern where MCP servers accept client tokens without proper validation and forward them to downstream APIs, violating MCP authorization specifications.
#### **Security Implications**
- **Control Circumvention**: Direct client-to-API token usage bypasses critical rate limiting, validation, and monitoring controls
- **Audit Trail Corruption**: Upstream-issued tokens make client identification impossible, breaking incident investigation capabilities
- **Proxy-based Data Exfiltration**: Unvalidated tokens enable malicious actors to use servers as proxies for unauthorized data access
- **Trust Boundary Violations**: Downstream services' trust assumptions may be violated when token origins cannot be verified
- **Multi-service Attack Expansion**: Compromised tokens accepted across multiple services enable lateral movement
#### **Required Security Controls**
**Non-negotiable Requirements:**
- **Token Validation**: MCP servers **MUST NOT** accept tokens not explicitly issued for the MCP server
- **Audience Verification**: Always validate token audience claims match the MCP server's identity
- **Proper Token Lifecycle**: Implement short-lived access tokens with secure rotation practices
## Supply Chain Security for AI Systems
Supply chain security has evolved beyond traditional software dependencies to encompass the entire AI ecosystem. Modern MCP implementations must rigorously verify and monitor all AI-related components, as each introduces potential vulnerabilities that could compromise system integrity.
### Expanded AI Supply Chain Components
**Traditional Software Dependencies:**
- Open-source libraries and frameworks
- Container images and base systems
- Development tools and build pipelines
- Infrastructure components and services
**AI-Specific Supply Chain Elements:**
- **Foundation Models**: Pre-trained models from various providers requiring provenance verification
- **Embedding Services**: External vectorization and semantic search services
- **Context Providers**: Data sources, knowledge bases, and document repositories
- **Third-party APIs**: External AI services, ML pipelines, and data processing endpoints
- **Model Artifacts**: Weights, configurations, and fine-tuned model variants
- **Training Data Sources**: Datasets used for model training and fine-tuning
### Comprehensive Supply Chain Security Strategy
#### **Component Verification & Trust**
- **Provenance Validation**: Verify the origin, licensing, and integrity of all AI components before integration
- **Security Assessment**: Conduct vulnerability scans and security reviews for models, data sources, and AI services
- **Reputation Analysis**: Evaluate the security track record and practices of AI service providers
- **Compliance Verification**: Ensure all components meet organizational security and regulatory requirements
#### **Secure Deployment Pipelines**
- **Automated CI/CD Security**: Integrate security scanning throughout automated deployment pipelines
- **Artifact Integrity**: Implement cryptographic verification for all deployed artifacts (code, models, configurations)
- **Staged Deployment**: Use progressive deployment strategies with security validation at each stage
- **Trusted Artifact Repositories**: Deploy only from verified, secure artifact registries and repositories
#### **Continuous Monitoring & Response**
- **Dependency Scanning**: Ongoing vulnerability monitoring for all software and AI component dependencies
- **Model Monitoring**: Continuous assessment of model behavior, performance drift, and security anomalies
- **Service Health Tracking**: Monitor external AI services for availability, security incidents, and policy changes
- **Threat Intelligence Integration**: Incorporate threat feeds specific to AI and ML security risks
#### **Access Control & Least Privilege**
- **Component-level Permissions**: Restrict access to models, data, and services based on business necessity
- **Service Account Management**: Implement dedicated service accounts with minimal required permissions
- **Network Segmentation**: Isolate AI components and limit network access between services
- **API Gateway Controls**: Use centralized API gateways to control and monitor access to external AI services
#### **Incident Response & Recovery**
- **Rapid Response Procedures**: Established processes for patching or replacing compromised AI components
- **Credential Rotation**: Automated systems for rotating secrets, API keys, and service credentials
- **Rollback Capabilities**: Ability to quickly revert to previous known-good versions of AI components
- **Supply Chain Breach Recovery**: Specific procedures for responding to upstream AI service compromises
### Microsoft Security Tools & Integration
**GitHub Advanced Security** provides comprehensive supply chain protection including:
- **Secret Scanning**: Automated detection of credentials, API keys, and tokens in repositories
- **Dependency Scanning**: Vulnerability assessment for open-source dependencies and libraries
- **CodeQL Analysis**: Static code analysis for security vulnerabilities and coding issues
- **Supply Chain Insights**: Visibility into dependency health and security status
**Azure DevOps & Azure Repos Integration:**
- Seamless security scanning integration across Microsoft development platforms
- Automated security checks in Azure Pipelines for AI workloads
- Policy enforcement for secure AI component deployment
**Microsoft Internal Practices:**
Microsoft implements extensive supply chain security practices across all products. Learn about proven approaches in [The Journey to Secure the Software Supply Chain at Microsoft](https://devblogs.microsoft.com/engineering-at-microsoft/the-journey-to-secure-the-software-supply-chain-at-microsoft/).
## Foundation Security Best Practices
MCP implementations inherit and build upon your organization's existing security posture. Strengthening foundational security practices significantly enhances the overall security of AI systems and MCP deployments.
### Core Security Fundamentals
#### **Secure Development Practices**
- **OWASP Compliance**: Protect against [OWASP Top 10](https://owasp.org/www-project-top-ten/) web application vulnerabilities
- **AI-Specific Protections**: Implement controls for [OWASP Top 10 for LLMs](https://genai.owasp.org/download/43299/?tmstv=1731900559)
- **Secure Secrets Management**: Use dedicated vaults for tokens, API keys, and sensitive configuration data
- **End-to-End Encryption**: Implement secure communications across all application components and data flows
- **Input Validation**: Rigorous validation of all user inputs, API parameters, and data sources
#### **Infrastructure Hardening**
- **Multi-Factor Authentication**: Mandatory MFA for all administrative and service accounts
- **Patch Management**: Automated, timely patching for operating systems, frameworks, and dependencies
- **Identity Provider Integration**: Centralized identity management through enterprise identity providers (Microsoft Entra ID, Active Directory)
- **Network Segmentation**: Logical isolation of MCP components to limit lateral movement potential
- **Principle of Least Privilege**: Minimal required permissions for all system components and accounts
#### **Security Monitoring & Detection**
- **Comprehensive Logging**: Detailed logging of AI application activities, including MCP client-server interactions
- **SIEM Integration**: Centralized security information and event management for anomaly detection
- **Behavioral Analytics**: AI-powered monitoring to detect unusual patterns in system and user behavior
- **Threat Intelligence**: Integration of external threat feeds and indicators of compromise (IOCs)
- **Incident Response**: Well-defined procedures for security incident detection, response, and recovery
#### **Zero Trust Architecture**
- **Never Trust, Always Verify**: Continuous verification of users, devices, and network connections
- **Micro-Segmentation**: Granular network controls that isolate individual workloads and services
- **Identity-Centric Security**: Security policies based on verified identities rather than network location
- **Continuous Risk Assessment**: Dynamic security posture evaluation based on current context and behavior
- **Conditional Access**: Access controls that adapt based on risk factors, location, and device trust
### Enterprise Integration Patterns
#### **Microsoft Security Ecosystem Integration**
- **Microsoft Defender for Cloud**: Comprehensive cloud security posture management
- **Azure Sentinel**: Cloud-native SIEM and SOAR capabilities for AI workload protection
- **Microsoft Entra ID**: Enterprise identity and access management with conditional access policies
- **Azure Key Vault**: Centralized secrets management with hardware security module (HSM) backing
- **Microsoft Purview**: Data governance and compliance for AI data sources and workflows
#### **Compliance & Governance**
- **Regulatory Alignment**: Ensure MCP implementations meet industry-specific compliance requirements (GDPR, HIPAA, SOC 2)
- **Data Classification**: Proper categorization and handling of sensitive data processed by AI systems
- **Audit Trails**: Comprehensive logging for regulatory compliance and forensic investigation
- **Privacy Controls**: Implementation of privacy-by-design principles in AI system architecture
- **Change Management**: Formal processes for security reviews of AI system modifications
These foundational practices create a robust security baseline that enhances the effectiveness of MCP-specific security controls and provides comprehensive protection for AI-driven applications.
## Key Security Takeaways
- **Layered Security Approach**: Combine foundational security practices (secure coding, least privilege, supply chain verification, continuous monitoring) with AI-specific controls for comprehensive protection
- **AI-Specific Threat Landscape**: MCP systems face unique risks including prompt injection, tool poisoning, session hijacking, confused deputy problems, token passthrough vulnerabilities, and excessive permissions that require specialized mitigations
- **Authentication & Authorization Excellence**: Implement robust authentication using external identity providers (Microsoft Entra ID), enforce proper token validation, and never accept tokens not explicitly issued for your MCP server
- **AI Attack Prevention**: Deploy Microsoft Prompt Shields and Azure Content Safety to defend against indirect prompt injection and tool poisoning attacks, while validating tool metadata and monitoring for dynamic changes
- **Session & Transport Security**: Use cryptographically secure, non-deterministic session IDs bound to user identities, implement proper session lifecycle management, and never use sessions for authentication
- **OAuth Security Best Practices**: Prevent confused deputy attacks through explicit user consent for dynamically registered clients, proper OAuth 2.1 implementation with PKCE, and strict redirect URI validation
- **Token Security Principles**: Avoid token passthrough anti-patterns, validate token audience claims, implement short-lived tokens with secure rotation, and maintain clear trust boundaries
- **Comprehensive Supply Chain Security**: Treat all AI ecosystem components (models, embeddings, context providers, external APIs) with the same security rigor as traditional software dependencies
- **Continuous Evolution**: Stay current with rapidly evolving MCP specifications, contribute to security community standards, and maintain adaptive security postures as the protocol matures
- **Microsoft Security Integration**: Leverage Microsoft's comprehensive security ecosystem (Prompt Shields, Azure Content Safety, GitHub Advanced Security, Entra ID) for enhanced MCP deployment protection
## Comprehensive Resources
### **Official MCP Security Documentation**
- [MCP Specification (Current: 2025-11-25)](https://spec.modelcontextprotocol.io/specification/2025-11-25/)
- [MCP Security Best Practices](https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices)
- [MCP Authorization Specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization)
- [MCP GitHub Repository](https://github.com/modelcontextprotocol)
### **OWASP MCP Security Resources**
- [OWASP MCP Azure Security Guide](https://microsoft.github.io/mcp-azure-security-guide/) - Comprehensive OWASP MCP Top 10 with Azure implementation guidance
- [OWASP MCP Top 10](https://owasp.org/www-project-mcp-top-10/) - Official OWASP MCP security risks
- [MCP Security Summit Workshop (Sherpa)](https://azure-samples.github.io/sherpa/) - Hands-on security training for MCP on Azure
### **Security Standards & Best Practices**
- [OAuth 2.0 Security Best Practices (RFC 9700)](https://datatracker.ietf.org/doc/html/rfc9700)
- [OWASP Top 10 Web Application Security](https://owasp.org/www-project-top-ten/)
- [OWASP Top 10 for Large Language Models](https://genai.owasp.org/download/43299/?tmstv=1731900559)
- [Microsoft Digital Defense Report](https://aka.ms/mddr)
### **AI Security Research & Analysis**
- [Prompt Injection in MCP (Simon Willison)](https://simonwillison.net/2025/Apr/9/mcp-prompt-injection/)
- [Tool Poisoning Attacks (Invariant Labs)](https://invariantlabs.ai/blog/mcp-security-notification-tool-poisoning-attacks)
- [MCP Security Research Briefing (Wiz Security)](https://www.wiz.io/blog/mcp-security-research-briefing#remote-servers-22)
### **Microsoft Security Solutions**
- [Microsoft Prompt Shields Documentation](https://learn.microsoft.com/azure/ai-services/content-safety/concepts/jailbreak-detection)
- [Azure Content Safety Service](https://learn.microsoft.com/azure/ai-services/content-safety/)
- [Microsoft Entra ID Security](https://learn.microsoft.com/entra/identity-platform/secure-least-privileged-access)
- [Azure Token Management Best Practices](https://learn.microsoft.com/entra/identity-platform/access-tokens)
- [GitHub Advanced Security](https://github.com/security/advanced-security)
### **Implementation Guides & Tutorials**
- [Azure API Management as MCP Authentication Gateway](https://techcommunity.microsoft.com/blog/integrationsonazureblog/azure-api-management-your-auth-gateway-for-mcp-servers/4402690)
- [Microsoft Entra ID Authentication with MCP Servers](https://den.dev/blog/mcp-server-auth-entra-id-session/)
- [Secure Token Storage and Encryption (Video)](https://youtu.be/uRdX37EcCwg?si=6fSChs1G4glwXRy2)
### **DevOps & Supply Chain Security**
- [Azure DevOps Security](https://azure.microsoft.com/products/devops)
- [Azure Repos Security](https://azure.microsoft.com/products/devops/repos/)
- [Microsoft Supply Chain Security Journey](https://devblogs.microsoft.com/engineering-at-microsoft/the-journey-to-secure-the-software-supply-chain-at-microsoft/)
## **Additional Security Documentation**
For comprehensive security guidance, refer to these specialized documents in this section:
- **[MCP Security Best Practices 2025](./mcp-security-best-practices-2025.md)** - Complete security best practices for MCP implementations
- **[Azure Content Safety Implementation](./azure-content-safety-implementation.md)** - Practical implementation examples for Azure Content Safety integration
- **[MCP Security Controls 2025](./mcp-security-controls-2025.md)** - Latest security controls and techniques for MCP deployments
- **[MCP Best Practices Quick Reference](./mcp-best-practices.md)** - Quick reference guide for essential MCP security practices
- **[BlueHat 2026: Securing the future of AI: Securing MCP with defense in depth patterns](https://www.youtube.com/watch?v=cVWB58kEt-Y)** - Defense-in-depth patterns from the Microsoft Security Response Center (MSRC)
### **Hands-On Security Training**
- **[MCP Security Summit Workshop (Sherpa)](https://azure-samples.github.io/sherpa/)** - Comprehensive hands-on workshop for securing MCP servers in Azure with progressive camps from Base Camp to Summit
- **[OWASP MCP Azure Security Guide](https://microsoft.github.io/mcp-azure-security-guide/)** - Reference architecture and implementation guidance for all OWASP MCP Top 10 risks
---
## What's Next
Next: [Chapter 3: Getting Started](../03-GettingStarted/README.md)
@@ -0,0 +1,48 @@
# Implementing Azure Content Safety with MCP
> **OWASP MCP Risk Addressed**: [MCP06 - Intent Flow Subversion](https://microsoft.github.io/mcp-azure-security-guide/mcp/mcp06-prompt-injection/)
To strengthen MCP security against prompt injection, tool poisoning, and other AI-specific vulnerabilities, integrating Azure Content Safety is highly recommended. This implementation guide aligns with the [MCP Security Summit Workshop (Sherpa)](https://azure-samples.github.io/sherpa/) Camp 3: I/O Security.
## Integration with MCP Server
To integrate Azure Content Safety with your MCP server, add the content safety filter as middleware in your request processing pipeline:
1. Initialize the filter during server startup
2. Validate all incoming tool requests before processing
3. Check all outgoing responses before returning them to clients
4. Log and alert on safety violations
5. Implement appropriate error handling for failed content safety checks
This provides a robust defense against:
- Prompt injection attacks
- Tool poisoning attempts
- Data exfiltration via malicious inputs
- Generation of harmful content
## Best Practices for Azure Content Safety Integration
1. **Custom Blocklists**: Create custom blocklists specifically for MCP injection patterns
2. **Severity Tuning**: Adjust severity thresholds based on your specific use case and risk tolerance
3. **Comprehensive Coverage**: Apply content safety checks to all inputs and outputs
4. **Performance Optimization**: Consider implementing caching for repeated content safety checks
5. **Fallback Mechanisms**: Define clear fallback behaviors when content safety services are unavailable
6. **User Feedback**: Provide clear feedback to users when content is blocked due to safety concerns
7. **Continuous Improvement**: Regularly update blocklists and patterns based on emerging threats
## Additional Resources
### OWASP MCP Security Guidance
- [OWASP MCP Azure Security Guide](https://microsoft.github.io/mcp-azure-security-guide/) - Comprehensive OWASP MCP Top 10 with Azure implementation
- [MCP06 - Prompt Injection](https://microsoft.github.io/mcp-azure-security-guide/mcp/mcp06-prompt-injection/) - Detailed prompt injection mitigation patterns
- [MCP Security Summit Workshop](https://azure-samples.github.io/sherpa/) - Hands-on Camp 3: I/O Security covers content safety
### Azure Documentation
- [Azure Content Safety Overview](https://learn.microsoft.com/azure/ai-services/content-safety/)
- [Prompt Shields Documentation](https://learn.microsoft.com/azure/ai-services/content-safety/concepts/jailbreak-detection)
- [Azure AI Content Safety Quickstart](https://learn.microsoft.com/azure/ai-services/content-safety/quickstart-text)
## What's Next
- Return to: [Security Module Overview](./README.md)
- Continue to: [Module 3: Getting Started](../03-GettingStarted/README.md)
+43
View File
@@ -0,0 +1,43 @@
# Advanced MCP Security with Azure Content Safety
> **OWASP MCP Risk Addressed**: [MCP06 - Intent Flow Subversion](https://microsoft.github.io/mcp-azure-security-guide/mcp/mcp06-prompt-injection/)
Azure Content Safety provides several powerful tools that can enhance the security of your MCP implementations. For hands-on implementation experience, see [MCP Security Summit Workshop (Sherpa)](https://azure-samples.github.io/sherpa/) Camp 3: I/O Security.
## Prompt Shields
Microsoft's AI Prompt Shields provide robust protection against both direct and indirect prompt injection attacks through:
1. **Advanced Detection**: Uses machine learning to identify malicious instructions embedded in content.
2. **Spotlighting**: Transforms input text to help AI systems distinguish between valid instructions and external inputs.
3. **Delimiters and Datamarking**: Marks boundaries between trusted and untrusted data.
4. **Content Safety Integration**: Works with Azure AI Content Safety to detect jailbreak attempts and harmful content.
5. **Continuous Updates**: Microsoft regularly updates protection mechanisms against emerging threats.
## Implementing Azure Content Safety with MCP
This approach provides multi-layered protection:
- Scanning inputs before processing
- Validating outputs before returning
- Using blocklists for known harmful patterns
- Leveraging Azure's continuously updated content safety models
## Azure Content Safety Resources
To learn more about implementing Azure Content Safety with your MCP servers, consult these official resources:
1. [Azure AI Content Safety Documentation](https://learn.microsoft.com/azure/ai-services/content-safety/) - Official documentation for Azure Content Safety.
2. [Prompt Shield Documentation](https://learn.microsoft.com/azure/ai-services/content-safety/concepts/prompt-shield) - Learn how to prevent prompt injection attacks.
3. [Content Safety API Reference](https://learn.microsoft.com/rest/api/contentsafety/) - Detailed API reference for implementing Content Safety.
4. [Quickstart: Azure Content Safety with C#](https://learn.microsoft.com/azure/ai-services/content-safety/quickstart-csharp) - Quick implementation guide using C#.
5. [Content Safety Client Libraries](https://learn.microsoft.com/azure/ai-services/content-safety/quickstart-client-libraries-rest-api) - Client libraries for various programming languages.
6. [Detecting Jailbreak Attempts](https://learn.microsoft.com/azure/ai-services/content-safety/concepts/jailbreak-detection) - Specific guidance on detecting and preventing jailbreak attempts.
7. [Best Practices for Content Safety](https://learn.microsoft.com/azure/ai-services/content-safety/concepts/best-practices) - Best practices for implementing content safety effectively.
For a more in-depth implementation, see our [Azure Content Safety Implementation guide](./azure-content-safety-implementation.md).
## What's Next
- Read: [Azure Content Safety Implementation](./azure-content-safety-implementation.md)
- Return to: [Security Module Overview](./README.md)
- Continue to: [Module 3: Getting Started](../03-GettingStarted/README.md)
+186
View File
@@ -0,0 +1,186 @@
# MCP Security Best Practices 2025
This comprehensive guide outlines essential security best practices for implementing Model Context Protocol (MCP) systems based on the latest **MCP Specification 2025-11-25** and current industry standards. These practices address both traditional security concerns and AI-specific threats unique to MCP deployments.
## Critical Security Requirements
### Mandatory Security Controls (MUST Requirements)
1. **Token Validation**: MCP servers **MUST NOT** accept any tokens that were not explicitly issued for the MCP server itself
2. **Authorization Verification**: MCP servers implementing authorization **MUST** verify ALL inbound requests and **MUST NOT** use sessions for authentication
3. **User Consent**: MCP proxy servers using static client IDs **MUST** obtain explicit user consent for each dynamically registered client
4. **Secure Session IDs**: MCP servers **MUST** use cryptographically secure, non-deterministic session IDs generated with secure random number generators
## Core Security Practices
### 1. Input Validation & Sanitization
- **Comprehensive Input Validation**: Validate and sanitize all inputs to prevent injection attacks, confused deputy problems, and prompt injection vulnerabilities
- **Parameter Schema Enforcement**: Implement strict JSON schema validation for all tool parameters and API inputs
- **Content Filtering**: Use Microsoft Prompt Shields and Azure Content Safety to filter malicious content in prompts and responses
- **Output Sanitization**: Validate and sanitize all model outputs before presenting to users or downstream systems
### 2. Authentication & Authorization Excellence
- **External Identity Providers**: Delegate authentication to established identity providers (Microsoft Entra ID, OAuth 2.1 providers) rather than implementing custom authentication
- **Fine-grained Permissions**: Implement granular, tool-specific permissions following the principle of least privilege
- **Token Lifecycle Management**: Use short-lived access tokens with secure rotation and proper audience validation
- **Multi-Factor Authentication**: Require MFA for all administrative access and sensitive operations
### 3. Secure Communication Protocols
- **Transport Layer Security**: Use HTTPS/TLS 1.3 for all MCP communications with proper certificate validation
- **End-to-End Encryption**: Implement additional encryption layers for highly sensitive data in transit and at rest
- **Certificate Management**: Maintain proper certificate lifecycle management with automated renewal processes
- **Protocol Version Enforcement**: Use the current MCP protocol version (2025-11-25) with proper version negotiation.
### 4. Advanced Rate Limiting & Resource Protection
- **Multi-layer Rate Limiting**: Implement rate limiting at user, session, tool, and resource levels to prevent abuse
- **Adaptive Rate Limiting**: Use machine learning-based rate limiting that adapts to usage patterns and threat indicators
- **Resource Quota Management**: Set appropriate limits for computational resources, memory usage, and execution time
- **DDoS Protection**: Deploy comprehensive DDoS protection and traffic analysis systems
### 5. Comprehensive Logging & Monitoring
- **Structured Audit Logging**: Implement detailed, searchable logs for all MCP operations, tool executions, and security events
- **Real-time Security Monitoring**: Deploy SIEM systems with AI-powered anomaly detection for MCP workloads
- **Privacy-compliant Logging**: Log security events while respecting data privacy requirements and regulations
- **Incident Response Integration**: Connect logging systems to automated incident response workflows
### 6. Enhanced Secure Storage Practices
- **Hardware Security Modules**: Use HSM-backed key storage (Azure Key Vault, AWS CloudHSM) for critical cryptographic operations
- **Encryption Key Management**: Implement proper key rotation, segregation, and access controls for encryption keys
- **Secrets Management**: Store all API keys, tokens, and credentials in dedicated secret management systems
- **Data Classification**: Classify data based on sensitivity levels and apply appropriate protection measures
### 7. Advanced Token Management
- **Token Passthrough Prevention**: Explicitly prohibit token passthrough patterns that bypass security controls
- **Audience Validation**: Always verify token audience claims match the intended MCP server identity
- **Claims-based Authorization**: Implement fine-grained authorization based on token claims and user attributes
- **Token Binding**: Bind tokens to specific sessions, users, or devices where appropriate
### 8. Secure Session Management
- **Cryptographic Session IDs**: Generate session IDs using cryptographically secure random number generators (not predictable sequences)
- **User-specific Binding**: Bind session IDs to user-specific information using secure formats like `<user_id>:<session_id>`
- **Session Lifecycle Controls**: Implement proper session expiration, rotation, and invalidation mechanisms
- **Session Security Headers**: Use appropriate HTTP security headers for session protection
### 9. AI-Specific Security Controls
- **Prompt Injection Defense**: Deploy Microsoft Prompt Shields with spotlighting, delimiters, and datamarking techniques
- **Tool Poisoning Prevention**: Validate tool metadata, monitor for dynamic changes, and verify tool integrity
- **Model Output Validation**: Scan model outputs for potential data leakage, harmful content, or security policy violations
- **Context Window Protection**: Implement controls to prevent context window poisoning and manipulation attacks
### 10. Tool Execution Security
- **Execution Sandboxing**: Run tool executions in containerized, isolated environments with resource limits
- **Privilege Separation**: Execute tools with minimal required privileges and separate service accounts
- **Network Isolation**: Implement network segmentation for tool execution environments
- **Execution Monitoring**: Monitor tool execution for anomalous behavior, resource usage, and security violations
### 11. Continuous Security Validation
- **Automated Security Testing**: Integrate security testing into CI/CD pipelines with tools like GitHub Advanced Security
- **Vulnerability Management**: Regularly scan all dependencies, including AI models and external services
- **Penetration Testing**: Conduct regular security assessments specifically targeting MCP implementations
- **Security Code Reviews**: Implement mandatory security reviews for all MCP-related code changes
### 12. Supply Chain Security for AI
- **Component Verification**: Verify provenance, integrity, and security of all AI components (models, embeddings, APIs)
- **Dependency Management**: Maintain current inventories of all software and AI dependencies with vulnerability tracking
- **Trusted Repositories**: Use verified, trusted sources for all AI models, libraries, and tools
- **Supply Chain Monitoring**: Continuously monitor for compromises in AI service providers and model repositories
## Advanced Security Patterns
### Zero Trust Architecture for MCP
- **Never Trust, Always Verify**: Implement continuous verification for all MCP participants
- **Micro-segmentation**: Isolate MCP components with granular network and identity controls
- **Conditional Access**: Implement risk-based access controls that adapt to context and behavior
- **Continuous Risk Assessment**: Dynamically evaluate security posture based on current threat indicators
### Privacy-Preserving AI Implementation
- **Data Minimization**: Only expose minimum necessary data for each MCP operation
- **Differential Privacy**: Implement privacy-preserving techniques for sensitive data processing
- **Homomorphic Encryption**: Use advanced encryption techniques for secure computation on encrypted data
- **Federated Learning**: Implement distributed learning approaches that preserve data locality and privacy
### Incident Response for AI Systems
- **AI-Specific Incident Procedures**: Develop incident response procedures tailored to AI and MCP-specific threats
- **Automated Response**: Implement automated containment and remediation for common AI security incidents
- **Forensic Capabilities**: Maintain forensic readiness for AI system compromises and data breaches
- **Recovery Procedures**: Establish procedures for recovering from AI model poisoning, prompt injection attacks, and service compromises
## Implementation Resources & Standards
### 🏔️ Hands-On Security Training
- **[MCP Security Summit Workshop (Sherpa)](https://azure-samples.github.io/sherpa/)** - Comprehensive hands-on workshop for securing MCP servers in Azure
- **[OWASP MCP Azure Security Guide](https://microsoft.github.io/mcp-azure-security-guide/)** - Reference architecture and OWASP MCP Top 10 implementation guidance
### Official MCP Documentation
- [MCP Specification 2025-11-25](https://spec.modelcontextprotocol.io/specification/2025-11-25/) - Current MCP protocol specification
- [MCP Security Best Practices](https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices) - Official security guidance
- [MCP Authorization Specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization) - Authentication and authorization patterns
- [MCP Transport Security](https://modelcontextprotocol.io/specification/2025-11-25/transports/) - Transport layer security requirements
### Microsoft Security Solutions
- [Microsoft Prompt Shields](https://learn.microsoft.com/azure/ai-services/content-safety/concepts/jailbreak-detection) - Advanced prompt injection protection
- [Azure Content Safety](https://learn.microsoft.com/azure/ai-services/content-safety/) - Comprehensive AI content filtering
- [Microsoft Entra ID](https://learn.microsoft.com/entra/identity-platform/v2-oauth2-auth-code-flow) - Enterprise identity and access management
- [Azure Key Vault](https://learn.microsoft.com/azure/key-vault/general/basic-concepts) - Secure secrets and credential management
- [GitHub Advanced Security](https://github.com/security/advanced-security) - Supply chain and code security scanning
### Security Standards & Frameworks
- [OAuth 2.1 Security Best Practices](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics) - Current OAuth security guidance
- [OWASP Top 10](https://owasp.org/www-project-top-ten/) - Web application security risks
- [OWASP Top 10 for LLMs](https://genai.owasp.org/download/43299/?tmstv=1731900559) - AI-specific security risks
- [NIST AI Risk Management Framework](https://www.nist.gov/itl/ai-risk-management-framework) - Comprehensive AI risk management
- [ISO 27001:2022](https://www.iso.org/standard/27001) - Information security management systems
### Implementation Guides & Tutorials
- [Azure API Management as MCP Auth Gateway](https://techcommunity.microsoft.com/blog/integrationsonazureblog/azure-api-management-your-auth-gateway-for-mcp-servers/4402690) - Enterprise authentication patterns
- [Microsoft Entra ID with MCP Servers](https://den.dev/blog/mcp-server-auth-entra-id-session/) - Identity provider integration
- [Secure Token Storage Implementation](https://youtu.be/uRdX37EcCwg?si=6fSChs1G4glwXRy2) - Token management best practices
- [End-to-End Encryption for AI](https://learn.microsoft.com/azure/architecture/example-scenario/confidential/end-to-end-encryption) - Advanced encryption patterns
### Advanced Security Resources
- [Microsoft Security Development Lifecycle](https://www.microsoft.com/sdl) - Secure development practices
- [AI Red Team Guidance](https://learn.microsoft.com/security/ai-red-team/) - AI-specific security testing
- [Threat Modeling for AI Systems](https://learn.microsoft.com/security/adoption/approach/threats-ai) - AI threat modeling methodology
- [Privacy Engineering for AI](https://www.microsoft.com/security/blog/2021/07/13/microsofts-pet-project-privacy-enhancing-technologies-in-action/) - Privacy-preserving AI techniques
### Compliance & Governance
- [GDPR Compliance for AI](https://learn.microsoft.com/compliance/regulatory/gdpr-data-protection-impact-assessments) - Privacy compliance in AI systems
- [AI Governance Framework](https://learn.microsoft.com/azure/architecture/guide/responsible-ai/responsible-ai-overview) - Responsible AI implementation
- [SOC 2 for AI Services](https://learn.microsoft.com/compliance/regulatory/offering-soc) - Security controls for AI service providers
- [HIPAA Compliance for AI](https://learn.microsoft.com/compliance/regulatory/offering-hipaa-hitech) - Healthcare AI compliance requirements
### DevSecOps & Automation
- [DevSecOps Pipeline for AI](https://learn.microsoft.com/azure/devops/migrate/security-validation-cicd-pipeline) - Secure AI development pipelines
- [Automated Security Testing](https://learn.microsoft.com/security/engineering/devsecops) - Continuous security validation
- [Infrastructure as Code Security](https://learn.microsoft.com/security/engineering/infrastructure-security) - Secure infrastructure deployment
- [Container Security for AI](https://learn.microsoft.com/azure/container-instances/container-instances-image-security) - AI workload containerization security
### Monitoring & Incident Response
- [Azure Monitor for AI Workloads](https://learn.microsoft.com/azure/azure-monitor/overview) - Comprehensive monitoring solutions
- [AI Security Incident Response](https://learn.microsoft.com/security/compass/incident-response-playbooks) - AI-specific incident procedures
- [SIEM for AI Systems](https://learn.microsoft.com/azure/sentinel/overview) - Security information and event management
- [Threat Intelligence for AI](https://learn.microsoft.com/security/compass/security-operations-videos-and-decks#threat-intelligence) - AI threat intelligence sources
## 🔄 Continuous Improvement
### Stay Current with Evolving Standards
- **MCP Specification Updates**: Monitor official MCP specification changes and security advisories
- **Threat Intelligence**: Subscribe to AI security threat feeds and vulnerability databases
- **Community Engagement**: Participate in MCP security community discussions and working groups
- **Regular Assessment**: Conduct quarterly security posture assessments and update practices accordingly
### Contributing to MCP Security
- **Security Research**: Contribute to MCP security research and vulnerability disclosure programs
- **Best Practice Sharing**: Share security implementations and lessons learned with the community
- **Standard Development**: Participate in MCP specification development and security standard creation
- **Tool Development**: Develop and share security tools and libraries for the MCP ecosystem
---
*This document reflects MCP security best practices as of December 18, 2025, based on MCP Specification 2025-11-25. Security practices should be regularly reviewed and updated as the protocol and threat landscape evolve.*
## What's Next
- Read: [MCP Security Best Practices 2025](./mcp-security-best-practices-2025.md)
- Return to: [Security Module Overview](./README.md)
- Continue to: [Module 3: Getting Started](../03-GettingStarted/README.md)
@@ -0,0 +1,216 @@
# MCP Security Best Practices - February 2026 Update
> **Important**: This document reflects the latest [MCP Specification 2025-11-25](https://spec.modelcontextprotocol.io/specification/2025-11-25/) security requirements and official [MCP Security Best Practices](https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices). Always refer to the current specification for the most up-to-date guidance.
## 🏔️ Hands-On Security Training
For practical implementation experience, we recommend the **[MCP Security Summit Workshop (Sherpa)](https://azure-samples.github.io/sherpa/)** - a comprehensive guided expedition to securing MCP servers in Azure. The workshop covers all OWASP MCP Top 10 risks through a "vulnerable → exploit → fix → validate" methodology.
All practices in this document align with the **[OWASP MCP Azure Security Guide](https://microsoft.github.io/mcp-azure-security-guide/)** for Azure-specific implementation guidance.
## Essential Security Practices for MCP Implementations
The Model Context Protocol introduces unique security challenges that extend beyond traditional software security. These practices address both foundational security requirements and MCP-specific threats including prompt injection, tool poisoning, session hijacking, confused deputy problems, and token passthrough vulnerabilities.
### **MANDATORY Security Requirements**
**Critical Requirements from MCP Specification:**
### **MANDATORY Security Requirements**
**Critical Requirements from MCP Specification:**
> **MUST NOT**: MCP servers **MUST NOT** accept any tokens that were not explicitly issued for the MCP server
>
> **MUST**: MCP servers implementing authorization **MUST** verify ALL inbound requests
>
> **MUST NOT**: MCP servers **MUST NOT** use sessions for authentication
>
> **MUST**: MCP proxy servers using static client IDs **MUST** obtain user consent for each dynamically registered client
---
## 1. **Token Security & Authentication**
**Authentication & Authorization Controls:**
- **Rigorous Authorization Review**: Conduct comprehensive audits of MCP server authorization logic to ensure only intended users and clients can access resources
- **External Identity Provider Integration**: Use established identity providers like Microsoft Entra ID rather than implementing custom authentication
- **Token Audience Validation**: Always validate that tokens were explicitly issued for your MCP server - never accept upstream tokens
- **Proper Token Lifecycle**: Implement secure token rotation, expiration policies, and prevent token replay attacks
**Protected Token Storage:**
- Use Azure Key Vault or similar secure credential stores for all secrets
- Implement encryption for tokens both at rest and in transit
- Regular credential rotation and monitoring for unauthorized access
## 2. **Session Management & Transport Security**
**Secure Session Practices:**
- **Cryptographically Secure Session IDs**: Use secure, non-deterministic session IDs generated with secure random number generators
- **User-Specific Binding**: Bind session IDs to user identities using formats like `<user_id>:<session_id>` to prevent cross-user session abuse
- **Session Lifecycle Management**: Implement proper expiration, rotation, and invalidation to limit vulnerability windows
- **HTTPS/TLS Enforcement**: Mandatory HTTPS for all communication to prevent session ID interception
**Transport Layer Security:**
- Configure TLS 1.3 where possible with proper certificate management
- Implement certificate pinning for critical connections
- Regular certificate rotation and validity verification
## 3. **AI-Specific Threat Protection** 🤖
**Prompt Injection Defense:**
- **Microsoft Prompt Shields**: Deploy AI Prompt Shields for advanced detection and filtering of malicious instructions
- **Input Sanitization**: Validate and sanitize all inputs to prevent injection attacks and confused deputy problems
- **Content Boundaries**: Use delimiter and datamarking systems to distinguish between trusted instructions and external content
**Tool Poisoning Prevention:**
- **Tool Metadata Validation**: Implement integrity checks for tool definitions and monitor for unexpected changes
- **Dynamic Tool Monitoring**: Monitor runtime behavior and set up alerting for unexpected execution patterns
- **Approval Workflows**: Require explicit user approval for tool modifications and capability changes
## 4. **Access Control & Permissions**
**Principle of Least Privilege:**
- Grant MCP servers only minimum permissions required for intended functionality
- Implement role-based access control (RBAC) with fine-grained permissions
- Regular permission reviews and continuous monitoring for privilege escalation
**Runtime Permission Controls:**
- Apply resource limits to prevent resource exhaustion attacks
- Use container isolation for tool execution environments
- Implement just-in-time access for administrative functions
## 5. **Content Safety & Monitoring**
**Content Safety Implementation:**
- **Azure Content Safety Integration**: Use Azure Content Safety to detect harmful content, jailbreak attempts, and policy violations
- **Behavioral Analysis**: Implement runtime behavioral monitoring to detect anomalies in MCP server and tool execution
- **Comprehensive Logging**: Log all authentication attempts, tool invocations, and security events with secure, tamper-proof storage
**Continuous Monitoring:**
- Real-time alerting for suspicious patterns and unauthorized access attempts
- Integration with SIEM systems for centralized security event management
- Regular security audits and penetration testing of MCP implementations
## 6. **Supply Chain Security**
**Component Verification:**
- **Dependency Scanning**: Use automated vulnerability scanning for all software dependencies and AI components
- **Provenance Validation**: Verify the origin, licensing, and integrity of models, data sources, and external services
- **Signed Packages**: Use cryptographically signed packages and verify signatures before deployment
**Secure Development Pipeline:**
- **GitHub Advanced Security**: Implement secret scanning, dependency analysis, and CodeQL static analysis
- **CI/CD Security**: Integrate security validation throughout automated deployment pipelines
- **Artifact Integrity**: Implement cryptographic verification for deployed artifacts and configurations
## 7. **OAuth Security & Confused Deputy Prevention**
**OAuth 2.1 Implementation:**
- **PKCE Implementation**: Use Proof Key for Code Exchange (PKCE) for all authorization requests
- **Explicit Consent**: Obtain user consent for each dynamically registered client to prevent confused deputy attacks
- **Redirect URI Validation**: Implement strict validation of redirect URIs and client identifiers
**Proxy Security:**
- Prevent authorization bypass through static client ID exploitation
- Implement proper consent workflows for third-party API access
- Monitor for authorization code theft and unauthorized API access
## 8. **Incident Response & Recovery**
**Rapid Response Capabilities:**
- **Automated Response**: Implement automated systems for credential rotation and threat containment
- **Rollback Procedures**: Ability to quickly revert to known-good configurations and components
- **Forensic Capabilities**: Detailed audit trails and logging for incident investigation
**Communication & Coordination:**
- Clear escalation procedures for security incidents
- Integration with organizational incident response teams
- Regular security incident simulations and tabletop exercises
## 9. **Compliance & Governance**
**Regulatory Compliance:**
- Ensure MCP implementations meet industry-specific requirements (GDPR, HIPAA, SOC 2)
- Implement data classification and privacy controls for AI data processing
- Maintain comprehensive documentation for compliance auditing
**Change Management:**
- Formal security review processes for all MCP system modifications
- Version control and approval workflows for configuration changes
- Regular compliance assessments and gap analysis
## 10. **Advanced Security Controls**
**Zero Trust Architecture:**
- **Never Trust, Always Verify**: Continuous verification of users, devices, and connections
- **Micro-segmentation**: Granular network controls isolating individual MCP components
- **Conditional Access**: Risk-based access controls adapting to current context and behavior
**Runtime Application Protection:**
- **Runtime Application Self-Protection (RASP)**: Deploy RASP techniques for real-time threat detection
- **Application Performance Monitoring**: Monitor for performance anomalies that may indicate attacks
- **Dynamic Security Policies**: Implement security policies that adapt based on current threat landscape
## 11. **Microsoft Security Ecosystem Integration**
**Comprehensive Microsoft Security:**
- **Microsoft Defender for Cloud**: Cloud security posture management for MCP workloads
- **Azure Sentinel**: Cloud-native SIEM and SOAR capabilities for advanced threat detection
- **Microsoft Purview**: Data governance and compliance for AI workflows and data sources
**Identity & Access Management:**
- **Microsoft Entra ID**: Enterprise identity management with conditional access policies
- **Privileged Identity Management (PIM)**: Just-in-time access and approval workflows for administrative functions
- **Identity Protection**: Risk-based conditional access and automated threat response
## 12. **Continuous Security Evolution**
**Staying Current:**
- **Specification Monitoring**: Regular review of MCP specification updates and security guidance changes
- **Threat Intelligence**: Integration of AI-specific threat feeds and indicators of compromise
- **Security Community Engagement**: Active participation in MCP security community and vulnerability disclosure programs
**Adaptive Security:**
- **Machine Learning Security**: Use ML-based anomaly detection for identifying novel attack patterns
- **Predictive Security Analytics**: Implement predictive models for proactive threat identification
- **Security Automation**: Automated security policy updates based on threat intelligence and specification changes
---
## **Critical Security Resources**
### **Official MCP Documentation**
- [MCP Specification (2025-11-25)](https://spec.modelcontextprotocol.io/specification/2025-11-25/)
- [MCP Security Best Practices](https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices)
- [MCP Authorization Specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization)
### **OWASP MCP Security Resources**
- [OWASP MCP Azure Security Guide](https://microsoft.github.io/mcp-azure-security-guide/) - Comprehensive OWASP MCP Top 10 with Azure implementation
- [OWASP MCP Top 10](https://owasp.org/www-project-mcp-top-10/) - Official OWASP MCP security risks
- [MCP Security Summit Workshop (Sherpa)](https://azure-samples.github.io/sherpa/) - Hands-on security training for MCP on Azure
### **Microsoft Security Solutions**
- [Microsoft Prompt Shields](https://learn.microsoft.com/azure/ai-services/content-safety/concepts/jailbreak-detection)
- [Azure Content Safety](https://learn.microsoft.com/azure/ai-services/content-safety/)
- [Microsoft Entra ID Security](https://learn.microsoft.com/entra/identity-platform/secure-least-privileged-access)
- [GitHub Advanced Security](https://github.com/security/advanced-security)
### **Security Standards**
- [OAuth 2.0 Security Best Practices (RFC 9700)](https://datatracker.ietf.org/doc/html/rfc9700)
- [OWASP Top 10 for Large Language Models](https://genai.owasp.org/)
- [NIST AI Risk Management Framework](https://www.nist.gov/itl/ai-risk-management-framework)
### **Implementation Guides**
- [Azure API Management MCP Authentication Gateway](https://techcommunity.microsoft.com/blog/integrationsonazureblog/azure-api-management-your-auth-gateway-for-mcp-servers/4402690)
- [Microsoft Entra ID with MCP Servers](https://den.dev/blog/mcp-server-auth-entra-id-session/)
---
> **Security Notice**: MCP security practices evolve rapidly. Always verify against the current [MCP specification](https://spec.modelcontextprotocol.io/) and [official security documentation](https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices) before implementation.
## What's Next
- Read: [MCP Security Controls 2025](./mcp-security-controls-2025.md)
- Return to: [Security Module Overview](./README.md)
- Continue to: [Module 3: Getting Started](../03-GettingStarted/README.md)
+447
View File
@@ -0,0 +1,447 @@
# MCP Security Controls - February 2026 Update
> **Current Standard**: This document reflects [MCP Specification 2025-11-25](https://spec.modelcontextprotocol.io/specification/2025-11-25/) security requirements and official [MCP Security Best Practices](https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices).
The Model Context Protocol (MCP) has matured significantly with enhanced security controls addressing both traditional software security and AI-specific threats. This document provides comprehensive security controls for secure MCP implementations aligned with the OWASP MCP Top 10 framework.
## 🏔️ Hands-On Security Training
For practical, hands-on security implementation experience, we recommend the **[MCP Security Summit Workshop (Sherpa)](https://azure-samples.github.io/sherpa/)** - a comprehensive guided expedition to securing MCP servers in Azure using a "vulnerable → exploit → fix → validate" methodology.
All security controls in this document align with the **[OWASP MCP Azure Security Guide](https://microsoft.github.io/mcp-azure-security-guide/)**, which provides reference architectures and Azure-specific implementation guidance for the OWASP MCP Top 10 risks.
## **MANDATORY Security Requirements**
### **Critical Prohibitions from MCP Specification:**
> **FORBIDDEN**: MCP servers **MUST NOT** accept any tokens that were not explicitly issued for the MCP server
>
> **PROHIBITED**: MCP servers **MUST NOT** use sessions for authentication
>
> **REQUIRED**: MCP servers implementing authorization **MUST** verify ALL inbound requests
>
> **MANDATORY**: MCP proxy servers using static client IDs **MUST** obtain user consent for each dynamically registered client
---
## 1. **Authentication & Authorization Controls**
### **External Identity Provider Integration**
**Current MCP Standard (2025-11-25)** allows MCP servers to delegate authentication to external identity providers, representing a significant security improvement:
**OWASP MCP Risk Addressed**: [MCP07 - Insufficient Authentication & Authorization](https://microsoft.github.io/mcp-azure-security-guide/mcp/mcp07-authz/)
**Security Benefits:**
1. **Eliminates Custom Authentication Risks**: Reduces vulnerability surface by avoiding custom authentication implementations
2. **Enterprise-Grade Security**: Leverages established identity providers like Microsoft Entra ID with advanced security features
3. **Centralized Identity Management**: Simplifies user lifecycle management, access control, and compliance auditing
4. **Multi-Factor Authentication**: Inherits MFA capabilities from enterprise identity providers
5. **Conditional Access Policies**: Benefits from risk-based access controls and adaptive authentication
**Implementation Requirements:**
- **Token Audience Validation**: Verify all tokens are explicitly issued for the MCP server
- **Issuer Verification**: Validate token issuer matches expected identity provider
- **Signature Verification**: Cryptographic validation of token integrity
- **Expiration Enforcement**: Strict enforcement of token lifetime limits
- **Scope Validation**: Ensure tokens contain appropriate permissions for requested operations
### **Authorization Logic Security**
**Critical Controls:**
- **Comprehensive Authorization Audits**: Regular security reviews of all authorization decision points
- **Fail-Safe Defaults**: Deny access when authorization logic cannot make a definitive decision
- **Permission Boundaries**: Clear separation between different privilege levels and resource access
- **Audit Logging**: Complete logging of all authorization decisions for security monitoring
- **Regular Access Reviews**: Periodic validation of user permissions and privilege assignments
## 2. **Token Security & Anti-Passthrough Controls**
**OWASP MCP Risk Addressed**: [MCP01 - Token Mismanagement & Secret Exposure](https://microsoft.github.io/mcp-azure-security-guide/mcp/mcp01-token-mismanagement/)
### **Token Passthrough Prevention**
**Token passthrough is explicitly prohibited** in the MCP Authorization Specification due to critical security risks:
**Security Risks Addressed:**
- **Control Circumvention**: Bypasses essential security controls like rate limiting, request validation, and traffic monitoring
- **Accountability Breakdown**: Makes client identification impossible, corrupting audit trails and incident investigation
- **Proxy-Based Exfiltration**: Enables malicious actors to use servers as proxies for unauthorized data access
- **Trust Boundary Violations**: Breaks downstream service trust assumptions about token origins
- **Lateral Movement**: Compromised tokens across multiple services enable broader attack expansion
**Implementation Controls:**
```yaml
Token Validation Requirements:
audience_validation: MANDATORY
issuer_verification: MANDATORY
signature_check: MANDATORY
expiration_enforcement: MANDATORY
scope_validation: MANDATORY
Token Lifecycle Management:
rotation_frequency: "Short-lived tokens preferred"
secure_storage: "Azure Key Vault or equivalent"
transmission_security: "TLS 1.3 minimum"
replay_protection: "Implemented via nonce/timestamp"
```
### **Secure Token Management Patterns**
**Best Practices:**
- **Short-Lived Tokens**: Minimize exposure window with frequent token rotation
- **Just-in-Time Issuance**: Issue tokens only when needed for specific operations
- **Secure Storage**: Use hardware security modules (HSMs) or secure key vaults
- **Token Binding**: Bind tokens to specific clients, sessions, or operations where possible
- **Monitoring & Alerting**: Real-time detection of token misuse or unauthorized access patterns
## 3. **Session Security Controls**
### **Session Hijacking Prevention**
**Attack Vectors Addressed:**
- **Session Hijack Prompt Injection**: Malicious events injected into shared session state
- **Session Impersonation**: Unauthorized use of stolen session IDs to bypass authentication
- **Resumable Stream Attacks**: Exploitation of server-sent event resumption for malicious content injection
**Mandatory Session Controls:**
```yaml
Session ID Generation:
randomness_source: "Cryptographically secure RNG"
entropy_bits: 128 # Minimum recommended
format: "Base64url encoded"
predictability: "MUST be non-deterministic"
Session Binding:
user_binding: "REQUIRED - <user_id>:<session_id>"
additional_identifiers: "Device fingerprint, IP validation"
context_binding: "Request origin, user agent validation"
Session Lifecycle:
expiration: "Configurable timeout policies"
rotation: "After privilege escalation events"
invalidation: "Immediate on security events"
cleanup: "Automated expired session removal"
```
**Transport Security:**
- **HTTPS Enforcement**: All session communication over TLS 1.3
- **Secure Cookie Attributes**: HttpOnly, Secure, SameSite=Strict
- **Certificate Pinning**: For critical connections to prevent MITM attacks
### **Stateful vs Stateless Considerations**
**For Stateful Implementations:**
- Shared session state requires additional protection against injection attacks
- Queue-based session management needs integrity verification
- Multiple server instances require secure session state synchronization
**For Stateless Implementations:**
- JWT or similar token-based session management
- Cryptographic verification of session state integrity
- Reduced attack surface but requires robust token validation
## 4. **AI-Specific Security Controls**
**OWASP MCP Risks Addressed**:
- [MCP06 - Intent Flow Subversion](https://microsoft.github.io/mcp-azure-security-guide/mcp/mcp06-prompt-injection/)
- [MCP03 - Tool Poisoning](https://microsoft.github.io/mcp-azure-security-guide/mcp/mcp03-tool-poisoning/)
- [MCP05 - Command Injection & Execution](https://microsoft.github.io/mcp-azure-security-guide/mcp/mcp05-command-injection/)
### **Prompt Injection Defense**
**Microsoft Prompt Shields Integration:**
```yaml
Detection Mechanisms:
- "Advanced ML-based instruction detection"
- "Contextual analysis of external content"
- "Real-time threat pattern recognition"
Protection Techniques:
- "Spotlighting trusted vs untrusted content"
- "Delimiter systems for content boundaries"
- "Data marking for content source identification"
Integration Points:
- "Azure Content Safety service"
- "Real-time content filtering"
- "Threat intelligence updates"
```
**Implementation Controls:**
- **Input Sanitization**: Comprehensive validation and filtering of all user inputs
- **Content Boundary Definition**: Clear separation between system instructions and user content
- **Instruction Hierarchy**: Proper precedence rules for conflicting instructions
- **Output Monitoring**: Detection of potentially harmful or manipulated outputs
### **Tool Poisoning Prevention**
**Tool Security Framework:**
```yaml
Tool Definition Protection:
validation:
- "Schema validation against expected formats"
- "Content analysis for malicious instructions"
- "Parameter injection detection"
- "Hidden instruction identification"
integrity_verification:
- "Cryptographic hashing of tool definitions"
- "Digital signatures for tool packages"
- "Version control with change auditing"
- "Tamper detection mechanisms"
monitoring:
- "Real-time change detection"
- "Behavioral analysis of tool usage"
- "Anomaly detection for execution patterns"
- "Automated alerting for suspicious modifications"
```
**Dynamic Tool Management:**
- **Approval Workflows**: Explicit user consent for tool modifications
- **Rollback Capabilities**: Ability to revert to previous tool versions
- **Change Auditing**: Complete history of tool definition modifications
- **Risk Assessment**: Automated evaluation of tool security posture
## 5. **Confused Deputy Attack Prevention**
### **OAuth Proxy Security**
**Attack Prevention Controls:**
```yaml
Client Registration:
static_client_protection:
- "Explicit user consent for dynamic registration"
- "Consent bypass prevention mechanisms"
- "Cookie-based consent validation"
- "Redirect URI strict validation"
authorization_flow:
- "PKCE implementation (OAuth 2.1)"
- "State parameter validation"
- "Authorization code binding"
- "Nonce verification for ID tokens"
```
**Implementation Requirements:**
- **User Consent Verification**: Never skip consent screens for dynamic client registration
- **Redirect URI Validation**: Strict whitelist-based validation of redirect destinations
- **Authorization Code Protection**: Short-lived codes with single-use enforcement
- **Client Identity Verification**: Robust validation of client credentials and metadata
## 6. **Tool Execution Security**
### **Sandboxing & Isolation**
**Container-Based Isolation:**
```yaml
Execution Environment:
containerization: "Docker/Podman with security profiles"
resource_limits:
cpu: "Configurable CPU quotas"
memory: "Memory usage restrictions"
disk: "Storage access limitations"
network: "Network policy enforcement"
privilege_restrictions:
user_context: "Non-root execution mandatory"
capability_dropping: "Remove unnecessary Linux capabilities"
syscall_filtering: "Seccomp profiles for syscall restriction"
filesystem: "Read-only root with minimal writable areas"
```
**Process Isolation:**
- **Separate Process Contexts**: Each tool execution in isolated process space
- **Inter-Process Communication**: Secure IPC mechanisms with validation
- **Process Monitoring**: Runtime behavior analysis and anomaly detection
- **Resource Enforcement**: Hard limits on CPU, memory, and I/O operations
### **Least Privilege Implementation**
**Permission Management:**
```yaml
Access Control:
file_system:
- "Minimal required directory access"
- "Read-only access where possible"
- "Temporary file cleanup automation"
network_access:
- "Explicit allowlist for external connections"
- "DNS resolution restrictions"
- "Port access limitations"
- "SSL/TLS certificate validation"
system_resources:
- "No administrative privilege elevation"
- "Limited system call access"
- "No hardware device access"
- "Restricted environment variable access"
```
## 7. **Supply Chain Security Controls**
**OWASP MCP Risk Addressed**: [MCP04 - Software Supply Chain Attacks & Dependency Tampering](https://microsoft.github.io/mcp-azure-security-guide/mcp/mcp04-supply-chain/)
### **Dependency Verification**
**Comprehensive Component Security:**
```yaml
Software Dependencies:
scanning:
- "Automated vulnerability scanning (GitHub Advanced Security)"
- "License compliance verification"
- "Known vulnerability database checks"
- "Malware detection and analysis"
verification:
- "Package signature verification"
- "Checksum validation"
- "Provenance attestation"
- "Software Bill of Materials (SBOM)"
AI Components:
model_verification:
- "Model provenance validation"
- "Training data source verification"
- "Model behavior testing"
- "Adversarial robustness assessment"
service_validation:
- "Third-party API security assessment"
- "Service level agreement review"
- "Data handling compliance verification"
- "Incident response capability evaluation"
```
### **Continuous Monitoring**
**Supply Chain Threat Detection:**
- **Dependency Health Monitoring**: Continuous assessment of all dependencies for security issues
- **Threat Intelligence Integration**: Real-time updates on emerging supply chain threats
- **Behavioral Analysis**: Detection of unusual behavior in external components
- **Automated Response**: Immediate containment of compromised components
## 8. **Monitoring & Detection Controls**
**OWASP MCP Risk Addressed**: [MCP08 - Lack of Audit and Telemetry](https://microsoft.github.io/mcp-azure-security-guide/mcp/mcp08-telemetry/)
### **Security Information and Event Management (SIEM)**
**Comprehensive Logging Strategy:**
```yaml
Authentication Events:
- "All authentication attempts (success/failure)"
- "Token issuance and validation events"
- "Session creation, modification, termination"
- "Authorization decisions and policy evaluations"
Tool Execution:
- "Tool invocation details and parameters"
- "Execution duration and resource usage"
- "Output generation and content analysis"
- "Error conditions and exception handling"
Security Events:
- "Potential prompt injection attempts"
- "Tool poisoning detection events"
- "Session hijacking indicators"
- "Unusual access patterns and anomalies"
```
### **Real-Time Threat Detection**
**Behavioral Analytics:**
- **User Behavior Analytics (UBA)**: Detection of unusual user access patterns
- **Entity Behavior Analytics (EBA)**: Monitoring of MCP server and tool behavior
- **Machine Learning Anomaly Detection**: AI-powered identification of security threats
- **Threat Intelligence Correlation**: Matching observed activities against known attack patterns
## 9. **Incident Response & Recovery**
### **Automated Response Capabilities**
**Immediate Response Actions:**
```yaml
Threat Containment:
session_management:
- "Immediate session termination"
- "Account lockout procedures"
- "Access privilege revocation"
system_isolation:
- "Network segmentation activation"
- "Service isolation protocols"
- "Communication channel restriction"
Recovery Procedures:
credential_rotation:
- "Automated token refresh"
- "API key regeneration"
- "Certificate renewal"
system_restoration:
- "Clean state restoration"
- "Configuration rollback"
- "Service restart procedures"
```
### **Forensic Capabilities**
**Investigation Support:**
- **Audit Trail Preservation**: Immutable logging with cryptographic integrity
- **Evidence Collection**: Automated gathering of relevant security artifacts
- **Timeline Reconstruction**: Detailed sequence of events leading to security incidents
- **Impact Assessment**: Evaluation of compromise scope and data exposure
## **Key Security Architecture Principles**
### **Defense in Depth**
- **Multiple Security Layers**: No single point of failure in security architecture
- **Redundant Controls**: Overlapping security measures for critical functions
- **Fail-Safe Mechanisms**: Secure defaults when systems encounter errors or attacks
### **Zero Trust Implementation**
- **Never Trust, Always Verify**: Continuous validation of all entities and requests
- **Principle of Least Privilege**: Minimal access rights for all components
- **Micro-Segmentation**: Granular network and access controls
### **Continuous Security Evolution**
- **Threat Landscape Adaptation**: Regular updates to address emerging threats
- **Security Control Effectiveness**: Ongoing evaluation and improvement of controls
- **Specification Compliance**: Alignment with evolving MCP security standards
---
## **Implementation Resources**
### **Official MCP Documentation**
- [MCP Specification (2025-11-25)](https://spec.modelcontextprotocol.io/specification/2025-11-25/)
- [MCP Security Best Practices](https://modelcontextprotocol.io/specification/2025-11-25/basic/security_best_practices)
- [MCP Authorization Specification](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization)
### **OWASP MCP Security Resources**
- [OWASP MCP Azure Security Guide](https://microsoft.github.io/mcp-azure-security-guide/) - Comprehensive OWASP MCP Top 10 with Azure implementation
- [OWASP MCP Top 10](https://owasp.org/www-project-mcp-top-10/) - Official OWASP MCP security risks
- [MCP Security Summit Workshop (Sherpa)](https://azure-samples.github.io/sherpa/) - Hands-on security training for MCP on Azure
### **Microsoft Security Solutions**
- [Microsoft Prompt Shields](https://learn.microsoft.com/azure/ai-services/content-safety/concepts/jailbreak-detection)
- [Azure Content Safety](https://learn.microsoft.com/azure/ai-services/content-safety/)
- [GitHub Advanced Security](https://github.com/security/advanced-security)
- [Azure Key Vault](https://learn.microsoft.com/azure/key-vault/)
### **Security Standards**
- [OAuth 2.0 Security Best Practices (RFC 9700)](https://datatracker.ietf.org/doc/html/rfc9700)
- [OWASP Top 10 for Large Language Models](https://genai.owasp.org/)
- [NIST Cybersecurity Framework](https://www.nist.gov/cyberframework)
---
> **Important**: These security controls reflect the current MCP specification (2025-11-25). Always verify against the latest [official documentation](https://spec.modelcontextprotocol.io/) as standards continue to evolve rapidly.
## What's Next
- Return to: [Security Module Overview](./README.md)
- Continue to: [Module 3: Getting Started](../03-GettingStarted/README.md)
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

@@ -0,0 +1,7 @@
Here's the solutions for each runtime:
- [TypeScript](./typescript/README.md)
- [Python](./python/README.md)
- [.NET](./dotnet/README.md)
- [Java](./java/README.md)
- [Rust](./rust/README.md)
@@ -0,0 +1,25 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(consoleLogOptions =>
{
// Configure all logs to go to stderr
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
[McpServerToolType]
public static class CalculatorTool
{
[McpServerTool, Description("Adds two numbers")]
public static string Add(int a, int b) => $"Sum {a + b}";
}
@@ -0,0 +1,91 @@
# Running this sample
## -1- Install the dependencies
```bash
dotnet restore
```
## -3- Run the sample
```bash
dotnet run
```
## -4- Test the sample
With the server running in one terminal, open another terminal and run the following command:
```bash
npx @modelcontextprotocol/inspector dotnet run
```
This should start a web server with a visual interface allowing you to test the sample.
Once the server is connected:
- try listing tools and run `add`, with args 2 and 4, you should see 6 in the result.
- go to resources and resource template and call "greeting", type in a name and you should see a greeting with the name you provided.
### Testing in CLI mode
You can launch it directly in CLI mode by running the following command:
```bash
npx @modelcontextprotocol/inspector --cli dotnet run --method tools/list
```
This will list all the tools available in the server. You should see the following output:
```text
{
"tools": [
{
"name": "Add",
"description": "Adds two numbers",
"inputSchema": {
"type": "object",
"properties": {
"a": {
"type": "integer"
},
"b": {
"type": "integer"
}
},
"title": "Add",
"description": "Adds two numbers",
"required": [
"a",
"b"
]
}
}
]
}
```
To invoke a tool type:
```bash
npx @modelcontextprotocol/inspector --cli dotnet run --method tools/call --tool-name Add --tool-arg a=1 --tool-arg b=2
```
You should see the following output:
```text
{
"content": [
{
"type": "text",
"text": "Sum 3"
}
],
"isError": false
}
```
> [!TIP]
> It's usually a lot faster to run the inspector in CLI mode than in the browser.
> Read more about the inspector [here](https://github.com/modelcontextprotocol/inspector).
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.*" />
<PackageReference Include="ModelContextProtocol" Version="0.*-*" />
</ItemGroup>
</Project>
@@ -0,0 +1,34 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet", "dotnet.csproj", "{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Debug|x64.ActiveCfg = Debug|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Debug|x64.Build.0 = Debug|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Debug|x86.ActiveCfg = Debug|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Debug|x86.Build.0 = Debug|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Release|Any CPU.Build.0 = Release|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Release|x64.ActiveCfg = Release|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Release|x64.Build.0 = Release|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Release|x86.ActiveCfg = Release|Any CPU
{A8AF6EEA-40DE-47B2-AEDC-2259FF376B6B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -0,0 +1,117 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}
@@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
@@ -0,0 +1,190 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2025 Spring AI MCP Echo Server Sample
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,67 @@
# Basic Calculator MCP Service
This service provides basic calculator operations through the Model Context Protocol (MCP) using Spring Boot with WebFlux transport. It's designed as a simple example for beginners learning about MCP implementations.
For more information, see the [MCP Server Boot Starter](https://docs.spring.io/spring-ai/reference/api/mcp/mcp-server-boot-starter-docs.html) reference documentation.
## Using the Service
The service exposes the following API endpoints through the MCP protocol:
- `add(a, b)`: Add two numbers together
- `subtract(a, b)`: Subtract the second number from the first
- `multiply(a, b)`: Multiply two numbers
- `divide(a, b)`: Divide the first number by the second (with zero check)
- `power(base, exponent)`: Calculate the power of a number
- `squareRoot(number)`: Calculate the square root (with negative number check)
- `modulus(a, b)`: Calculate the remainder when dividing
- `absolute(number)`: Calculate the absolute value
## Dependencies
The project requires the following key dependencies:
```xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
```
## Building the Project
Build the project using Maven:
```bash
./mvnw clean install -DskipTests
```
## Running the Server
### Using Java
```bash
java -jar target/calculator-server-0.0.1-SNAPSHOT.jar
```
### Using MCP Inspector
The MCP Inspector is a helpful tool for interacting with MCP services. To use it with this calculator service:
1. **Install and run MCP Inspector** in a new terminal window:
```bash
npx @modelcontextprotocol/inspector
```
2. **Access the web UI** by clicking the URL displayed by the app (typically http://localhost:6274)
3. **Configure the connection**:
- Set the transport type to "SSE"
- Set the URL to your running server's SSE endpoint: `http://localhost:8080/sse`
- Click "Connect"
4. **Use the tools**:
- Click "List Tools" to see available calculator operations
- Select a tool and click "Run Tool" to execute an operation
![MCP Inspector Screenshot](images/tool.png)
Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

+310
View File
@@ -0,0 +1,310 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
+182
View File
@@ -0,0 +1,182 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%
@@ -0,0 +1,108 @@
<?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>3.5.0</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>calculator-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Calculator Server</name>
<description>Basic calculator MCP service for beginners</description>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<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>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>21</release>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<java.version>21</java.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
</project>
@@ -0,0 +1,64 @@
package com.microsoft.mcp.sample.client;
import java.util.Map;
import org.springframework.web.reactive.function.client.WebClient;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
public class SDKClient {
public static void main(String[] args) {
var transport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
new SDKClient(transport).run();
}
private final McpClientTransport transport;
public SDKClient(McpClientTransport transport) {
this.transport = transport;
}
public void run() {
var client = McpClient.sync(this.transport).build();
client.initialize();
client.ping();
// List and demonstrate tools
ListToolsResult toolsList = client.listTools();
System.out.println("Available Tools = " + toolsList);
CallToolResult resultAdd = client.callTool(new CallToolRequest("add", Map.of("a", 5.0, "b", 3.0)));
System.out.println("Add Result = " + resultAdd);
CallToolResult resultSubtract = client.callTool(new CallToolRequest("subtract", Map.of("a", 10.0, "b", 4.0)));
System.out.println("Subtract Result = " + resultSubtract);
CallToolResult resultMultiply = client.callTool(new CallToolRequest("multiply", Map.of("a", 6.0, "b", 7.0)));
System.out.println("Multiply Result = " + resultMultiply);
CallToolResult resultDivide = client.callTool(new CallToolRequest("divide", Map.of("a", 20.0, "b", 4.0)));
System.out.println("Divide Result = " + resultDivide);
CallToolResult resultPower = client.callTool(new CallToolRequest("power", Map.of("base", 2.0, "exponent", 8.0)));
System.out.println("Power Result = " + resultPower);
CallToolResult resultSqrt = client.callTool(new CallToolRequest("squareRoot", Map.of("number", 16.0)));
System.out.println("Square Root Result = " + resultSqrt);
CallToolResult resultAbsolute = client.callTool(new CallToolRequest("absolute", Map.of("number", -5.5)));
System.out.println("Absolute Result = " + resultAbsolute);
CallToolResult resultHelp = client.callTool(new CallToolRequest("help", Map.of()));
System.out.println("Help = " + resultHelp);
client.closeGracefully();
}
}
@@ -0,0 +1,22 @@
package com.microsoft.mcp.sample.server;
import org.springframework.ai.tool.ToolCallbackProvider;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import com.microsoft.mcp.sample.server.service.CalculatorService;
@SpringBootApplication
public class McpServerApplication {
public static void main(String[] args) {
SpringApplication.run(McpServerApplication.class, args);
}
@Bean
public ToolCallbackProvider calculatorTools(CalculatorService calculator) {
return MethodToolCallbackProvider.builder().toolObjects(calculator).build();
}
}
@@ -0,0 +1,41 @@
package com.microsoft.mcp.sample.server.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Configuration class that displays welcome and usage information at application startup.
*/
@Configuration
public class StartupConfig {
@Value("${calculator.service.welcome:Welcome to the Calculator Service!}")
private String welcomeMessage;
@Value("${calculator.service.usage:}")
private String usageMessage;
/**
* Display startup information when the application launches.
*/
@Bean
public CommandLineRunner startupInfo() {
return args -> {
System.out.println("\n" + "=".repeat(80));
System.out.println(welcomeMessage);
System.out.println("=".repeat(80));
if (usageMessage != null && !usageMessage.isEmpty()) {
System.out.println("\nUsage Information:");
System.out.println(usageMessage);
System.out.println("\nEndpoint: http://localhost:8080/v1/tools");
System.out.println("\nSee the README.md for more information on how to use the service.");
}
System.out.println("\nThe Calculator service is now ready to accept requests!");
System.out.println("=".repeat(80) + "\n");
};
}
}
@@ -0,0 +1,69 @@
package com.microsoft.mcp.sample.server.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import com.microsoft.mcp.sample.server.service.CalculatorService;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
/**
* Controller for health check and information endpoints.
*/
@RestController
public class HealthController {
private final CalculatorService calculatorService;
public HealthController(CalculatorService calculatorService) {
this.calculatorService = calculatorService;
}
/**
* Simple health check endpoint.
*
* @return Health status information
*/
@GetMapping("/health")
public ResponseEntity<Map<String, Object>> healthCheck() {
Map<String, Object> response = new HashMap<>();
response.put("status", "UP");
response.put("timestamp", LocalDateTime.now().toString());
response.put("service", "Basic Calculator Service");
// Add calculator service status information
response.put("calculatorService", calculatorService != null ? "Available" : "Unavailable");
return ResponseEntity.ok(response);
}
/**
* Information endpoint about the service.
*
* @return Service information
*/
@GetMapping("/info")
public ResponseEntity<Map<String, Object>> serviceInfo() {
Map<String, Object> response = new HashMap<>();
response.put("service", "Basic Calculator Service");
response.put("version", "1.0.0");
response.put("endpoint", "/v1/tools");
Map<String, String> tools = new HashMap<>();
tools.put("add", "Add two numbers together");
tools.put("subtract", "Subtract the second number from the first number");
tools.put("multiply", "Multiply two numbers together");
tools.put("divide", "Divide the first number by the second number");
tools.put("power", "Calculate the power of a number (base raised to an exponent)");
tools.put("squareRoot", "Calculate the square root of a number");
tools.put("modulus", "Calculate the remainder when one number is divided by another");
tools.put("absolute", "Calculate the absolute value of a number");
tools.put("help", "Get help about available calculator operations");
response.put("availableTools", tools);
return ResponseEntity.ok(response);
}
}
@@ -0,0 +1,72 @@
package com.microsoft.mcp.sample.server.exception;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
/**
* Global exception handler for the Calculator recommendation service.
*/
@RestControllerAdvice
public class GlobalExceptionHandler {
/**
* Handle IllegalArgumentException which occurs when invalid input is provided.
*
* @param ex The exception that was thrown
* @return A response with error details
*/
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> handleIllegalArgumentException(IllegalArgumentException ex) {
ErrorResponse error = new ErrorResponse(
"Invalid_Input",
"Invalid input parameter: " + ex.getMessage(),
"Please check your input values and try again.");
return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
}
/**
* Handle generic exceptions that are not specifically handled elsewhere.
*
* @param ex The exception that was thrown
* @return A response with error details
*/
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> handleGenericException(Exception ex) {
ErrorResponse error = new ErrorResponse(
"Internal_Error",
"An unexpected error occurred: " + ex.getMessage(),
"Please try again later or contact support if the issue persists.");
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
/**
* Simple error response class for consistent error reporting.
*/
public static class ErrorResponse {
private String code;
private String message;
private String resolution;
public ErrorResponse(String code, String message, String resolution) {
this.code = code;
this.message = message;
this.resolution = resolution;
}
public String getCode() {
return code;
}
public String getMessage() {
return message;
}
public String getResolution() {
return resolution;
}
}
}
@@ -0,0 +1,141 @@
package com.microsoft.mcp.sample.server.service;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.stereotype.Service;
/**
* Service for basic calculator operations.
* This service provides simple calculator functionality through MCP.
*/
@Service
public class CalculatorService {
/**
* Add two numbers
* @param a The first number
* @param b The second number
* @return The sum of the two numbers
*/
@Tool(description = "Add two numbers together")
public String add(double a, double b) {
double result = a + b;
return formatResult(a, "+", b, result);
}
/**
* Subtract one number from another
* @param a The number to subtract from
* @param b The number to subtract
* @return The result of the subtraction
*/
@Tool(description = "Subtract the second number from the first number")
public String subtract(double a, double b) {
double result = a - b;
return formatResult(a, "-", b, result);
}
/**
* Multiply two numbers
* @param a The first number
* @param b The second number
* @return The product of the two numbers
*/
@Tool(description = "Multiply two numbers together")
public String multiply(double a, double b) {
double result = a * b;
return formatResult(a, "*", b, result);
}
/**
* Divide one number by another
* @param a The numerator
* @param b The denominator
* @return The result of the division
*/
@Tool(description = "Divide the first number by the second number")
public String divide(double a, double b) {
if (b == 0) {
return "Error: Cannot divide by zero";
}
double result = a / b;
return formatResult(a, "/", b, result);
}
/**
* Calculate the power of a number
* @param base The base number
* @param exponent The exponent
* @return The result of raising the base to the exponent
*/
@Tool(description = "Calculate the power of a number (base raised to an exponent)")
public String power(double base, double exponent) {
double result = Math.pow(base, exponent);
return formatResult(base, "^", exponent, result);
}
/**
* Calculate the square root of a number
* @param number The number to find the square root of
* @return The square root of the number
*/
@Tool(description = "Calculate the square root of a number")
public String squareRoot(double number) {
if (number < 0) {
return "Error: Cannot calculate square root of a negative number";
}
double result = Math.sqrt(number);
return String.format("√%.2f = %.2f", number, result);
}
/**
* Format the result of a calculation
*/
private String formatResult(double a, String operator, double b, double result) {
return String.format("%.2f %s %.2f = %.2f", a, operator, b, result);
}
/**
* Calculate the modulus (remainder) of division
* @param a The dividend
* @param b The divisor
* @return The remainder of the division
*/
@Tool(description = "Calculate the remainder when one number is divided by another")
public String modulus(double a, double b) {
if (b == 0) {
return "Error: Cannot divide by zero";
}
double result = a % b;
return formatResult(a, "%", b, result);
}
/**
* Calculate the absolute value of a number
* @param number The number to find the absolute value of
* @return The absolute value of the number
*/
@Tool(description = "Calculate the absolute value of a number")
public String absolute(double number) {
double result = Math.abs(number);
return String.format("|%.2f| = %.2f", number, result);
}
/**
* Get help about available calculator operations
* @return Information about available operations
*/
@Tool(description = "Get help about available calculator operations")
public String help() {
return "Basic Calculator MCP Service\n\n" +
"Available operations:\n" +
"1. add(a, b) - Adds two numbers\n" +
"2. subtract(a, b) - Subtracts the second number from the first\n" +
"3. multiply(a, b) - Multiplies two numbers\n" +
"4. divide(a, b) - Divides the first number by the second\n" +
"5. power(base, exponent) - Raises a number to a power\n" +
"6. squareRoot(number) - Calculates the square root\n" +
"7. modulus(a, b) - Calculates the remainder of division\n" +
"8. absolute(number) - Calculates the absolute value\n\n" +
"Example usage: add(5, 3) will return 5 + 3 = 8";
}
}
@@ -0,0 +1,9 @@
_____ _ _ _
/ ____| | | | | | |
| | __ _| | ___ _ _| | __ _| |_ ___ _ __
| | / _` | |/ __| | | | |/ _` | __/ _ \| '__|
| |___| (_| | | (__| |_| | | (_| | || (_) | |
\_____\__,_|_|\___|\__,_|_|\__,_|\__\___/|_|
Calculator Service v1.0
Spring Boot Calculator Application
@@ -0,0 +1,109 @@
# Running this sample
You're recommended to install `uv` but it's not a must, see [instructions](https://docs.astral.sh/uv/#highlights)
## -0- Create a virtual environment
```bash
python -m venv venv
```
## -1- Activate the virtual environment
```bash
venv\Scripts\activate
```
## -2- Install the dependencies
```bash
pip install "mcp[cli]"
```
## -3- Run the sample
```bash
mcp run server.py
```
## -4- Test the sample
With the server running in one terminal, open another terminal and run the following command:
```bash
mcp dev server.py
```
This should start a web server with a visual interface allowing you to test the sample.
Once the server is connected:
- try listing tools and run `add`, with args 2 and 4, you should see 6 in the result.
- go to resources and resource template and call get_greeting, type in a name and you should see a greeting with the name you provided.
### Testing in ClI mode
The inspector you ran is actually a Node.js app and `mcp dev` is a wrapper around it.
You can launch it directly in CLI mode by running the following command:
```bash
npx @modelcontextprotocol/inspector --cli mcp run server.py --method tools/list
```
This will list all the tools available in the server. You should see the following output:
```text
{
"tools": [
{
"name": "add",
"description": "Add two numbers",
"inputSchema": {
"type": "object",
"properties": {
"a": {
"title": "A",
"type": "integer"
},
"b": {
"title": "B",
"type": "integer"
}
},
"required": [
"a",
"b"
],
"title": "addArguments"
}
}
]
}
```
To invoke a tool type:
```bash
npx @modelcontextprotocol/inspector --cli mcp run server.py --method tools/call --tool-name add --tool-arg a=1 --tool-arg b=2
```
You should see the following output:
```text
{
"content": [
{
"type": "text",
"text": "3"
}
],
"isError": false
}
```
> [!TIP]
> It's usually a lot faster to run the ispector in CLI mode than in the browser.
> Read more about the inspector [here](https://github.com/modelcontextprotocol/inspector).
@@ -0,0 +1,27 @@
# server.py
from mcp.server.fastmcp import FastMCP
# Create an MCP server
mcp = FastMCP("Demo")
# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
@mcp.tool()
def subtract(a: int, b: int) -> int:
"""Subtract two numbers"""
return a - b
# Add a dynamic greeting resource
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"
# Main execution block - this is required to run the server
if __name__ == "__main__":
mcp.run()
+889
View File
@@ -0,0 +1,889 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "async-trait"
version = "0.1.89"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "autocfg"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
[[package]]
name = "backtrace"
version = "0.3.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-targets",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "bitflags"
version = "2.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
[[package]]
name = "bumpalo"
version = "3.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
[[package]]
name = "bytes"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
[[package]]
name = "calculator-server"
version = "0.1.0"
dependencies = [
"rmcp",
"serde",
"slab",
"tokio",
]
[[package]]
name = "cc"
version = "1.2.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2352e5597e9c544d5e6d9c95190d5d27738ade584fa8db0a16e130e5c2b5296e"
dependencies = [
"shlex",
]
[[package]]
name = "cfg-if"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
[[package]]
name = "chrono"
version = "0.4.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"serde",
"wasm-bindgen",
"windows-link",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]]
name = "darling"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0"
dependencies = [
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "dyn-clone"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555"
[[package]]
name = "futures"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
dependencies = [
"futures-channel",
"futures-core",
"futures-executor",
"futures-io",
"futures-sink",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
dependencies = [
"futures-core",
"futures-sink",
]
[[package]]
name = "futures-core"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
[[package]]
name = "futures-executor"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
dependencies = [
"futures-core",
"futures-task",
"futures-util",
]
[[package]]
name = "futures-io"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
[[package]]
name = "futures-macro"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "futures-sink"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
[[package]]
name = "futures-task"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
[[package]]
name = "futures-util"
version = "0.3.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
dependencies = [
"futures-channel",
"futures-core",
"futures-io",
"futures-macro",
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-utils",
"slab",
]
[[package]]
name = "gimli"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "iana-time-zone"
version = "0.1.63"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"log",
"wasm-bindgen",
"windows-core",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "io-uring"
version = "0.7.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4"
dependencies = [
"bitflags",
"cfg-if",
"libc",
]
[[package]]
name = "itoa"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
[[package]]
name = "js-sys"
version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [
"once_cell",
"wasm-bindgen",
]
[[package]]
name = "libc"
version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "log"
version = "0.4.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
[[package]]
name = "memchr"
version = "2.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
dependencies = [
"libc",
"wasi",
"windows-sys",
]
[[package]]
name = "num-traits"
version = "0.2.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
dependencies = [
"autocfg",
]
[[package]]
name = "object"
version = "0.36.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
[[package]]
name = "pastey"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5a797f0e07bdf071d15742978fc3128ec6c22891c31a3a931513263904c982a"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d61789d7719defeb74ea5fe81f2fdfdbd28a803847077cecce2ff14e1472f6f1"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
dependencies = [
"proc-macro2",
]
[[package]]
name = "ref-cast"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf"
dependencies = [
"ref-cast-impl",
]
[[package]]
name = "ref-cast-impl"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "rmcp"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f542f74cf247da16f19bbc87e298cd201e912314f4083e88cdd671f44f5fcb53"
dependencies = [
"async-trait",
"base64",
"chrono",
"futures",
"pastey",
"pin-project-lite",
"rmcp-macros",
"schemars",
"serde",
"serde_json",
"thiserror",
"tokio",
"tokio-util",
"tracing",
]
[[package]]
name = "rmcp-macros"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7caa6743cc0888e433105fe1bc551a7f607940b126a37bc97b478e86064627eb"
dependencies = [
"darling",
"proc-macro2",
"quote",
"serde_json",
"syn",
]
[[package]]
name = "rustc-demangle"
version = "0.1.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace"
[[package]]
name = "rustversion"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "ryu"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
[[package]]
name = "schemars"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82d20c4491bc164fa2f6c5d44565947a52ad80b9505d8e36f8d54c27c739fcd0"
dependencies = [
"chrono",
"dyn-clone",
"ref-cast",
"schemars_derive",
"serde",
"serde_json",
]
[[package]]
name = "schemars_derive"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d020396d1d138dc19f1165df7545479dcd58d93810dc5d646a16e55abefa80"
dependencies = [
"proc-macro2",
"quote",
"serde_derive_internals",
"syn",
]
[[package]]
name = "serde"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.219"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_derive_internals"
version = "0.29.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.142"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
dependencies = [
"itoa",
"memchr",
"ryu",
"serde",
]
[[package]]
name = "shlex"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "slab"
version = "0.4.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
[[package]]
name = "strsim"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
[[package]]
name = "syn"
version = "2.0.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b0949c3a6c842cbde3f1686d6eea5a010516deb7085f79db747562d4102f41e"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc5b44b4ab9c2fdd0e0512e6bece8388e214c0749f5862b114cc5b7a25daf227"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio"
version = "1.47.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
dependencies = [
"backtrace",
"bytes",
"io-uring",
"libc",
"mio",
"pin-project-lite",
"slab",
"tokio-macros",
]
[[package]]
name = "tokio-macros"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tokio-util"
version = "0.7.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5"
dependencies = [
"bytes",
"futures-core",
"futures-sink",
"pin-project-lite",
"tokio",
]
[[package]]
name = "tracing"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
"tracing-attributes",
"tracing-core",
]
[[package]]
name = "tracing-attributes"
version = "0.1.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tracing-core"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
dependencies = [
"once_cell",
]
[[package]]
name = "unicode-ident"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
[[package]]
name = "wasm-bindgen"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [
"cfg-if",
"once_cell",
"rustversion",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [
"bumpalo",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]]
name = "windows-core"
version = "0.61.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
dependencies = [
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-link"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
[[package]]
name = "windows-result"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
@@ -0,0 +1,12 @@
[package]
name = "calculator-server"
version = "0.1.0"
edition = "2024"
[dependencies]
rmcp = { version = "1.4.0", features = ["server", "transport-io"] }
serde = "1.0.219"
tokio = { version = "1.46.1", features = ["rt-multi-thread"] }
[dev-dependencies]
slab = "0.4.11"
@@ -0,0 +1,13 @@
# Running this sample
## -1- Install the dependencies and build the project
```bash
cargo build
```
## -3- Run the sample
```bash
cargo run
```
@@ -0,0 +1,54 @@
use rmcp::{
ServerHandler, ServiceExt,
handler::server::{router::tool::ToolRouter, tool::Parameters},
model::{ServerCapabilities, ServerInfo},
schemars, tool, tool_handler, tool_router,
transport::stdio,
};
use std::error::Error;
#[derive(Debug, Clone)]
pub struct Calculator {
tool_router: ToolRouter<Self>,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct CalculatorRequest {
pub a: f64,
pub b: f64,
}
#[tool_router]
impl Calculator {
pub fn new() -> Self {
Self {
tool_router: Self::tool_router(),
}
}
#[tool(description = "Adds a and b")]
async fn add(
&self,
Parameters(CalculatorRequest { a, b }): Parameters<CalculatorRequest>,
) -> String {
(a + b).to_string()
}
}
#[tool_handler]
impl ServerHandler for Calculator {
fn get_info(&self) -> ServerInfo {
ServerInfo {
instructions: Some("A simple calculator tool".into()),
capabilities: ServerCapabilities::builder().enable_tools().build(),
..Default::default()
}
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let service = Calculator::new().serve(stdio()).await?;
service.waiting().await?;
Ok(())
}
@@ -0,0 +1,96 @@
# Running this sample
You're recommended to install `uv` but it's not a must, see [instructions](https://docs.astral.sh/uv/#highlights)
## -1- Install the dependencies
```bash
npm install
```
## -3- Run the sample
```bash
npm run build
```
## -4- Test the sample
With the server running in one terminal, open another terminal and run the following command:
```bash
npm run inspector
```
This should start a web server with a visual interface allowing you to test the sample.
Once the server is connected:
- try listing tools and run `add`, with args 2 and 4, you should see 6 in the result.
- go to resources and resource template and call "greeting", type in a name and you should see a greeting with the name you provided.
### Testing in CLI mode
The inspector you ran is actually a Node.js app and `mcp dev` is a wrapper around it.
You can launch it directly in CLI mode by running the following command:
```bash
npx @modelcontextprotocol/inspector --cli node ./build/index.js --method tools/list
```
This will list all the tools available in the server. You should see the following output:
```text
{
"tools": [
{
"name": "add",
"description": "Add two numbers",
"inputSchema": {
"type": "object",
"properties": {
"a": {
"title": "A",
"type": "integer"
},
"b": {
"title": "B",
"type": "integer"
}
},
"required": [
"a",
"b"
],
"title": "addArguments"
}
}
]
}
```
To invoke a tool type:
```bash
nnpx @modelcontextprotocol/inspector --cli node ./build/index.js --method tools/call --tool-name add --tool-arg a=1 --tool-arg b=2
```
You should see the following output:
```text
{
"content": [
{
"type": "text",
"text": "3"
}
],
"isError": false
}
```
> [!TIP]
> It's usually a lot faster to run the ispector in CLI mode than in the browser.
> Read more about the inspector [here](https://github.com/modelcontextprotocol/inspector).
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,24 @@
{
"type": "module",
"bin": {
"weather": "./build/index.js"
},
"scripts": {
"build": "tsc && node ./build/index.js",
"inspector": "npx @modelcontextprotocol/inspector node build/index.js"
},
"files": [
"build"
],
"dependencies": {
"@modelcontextprotocol/sdk": ">=1.26.0",
"type": "^2.7.3",
"uuid": "^11.1.0",
"zod": "^3.24.2"
},
"devDependencies": {
"@types/node": "^20.11.24",
"@types/uuid": "^10.0.0",
"typescript": "^5.3.3"
}
}
@@ -0,0 +1,47 @@
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create an MCP server
const server = new McpServer({
name: "Demo",
version: "1.0.0"
});
// Add an addition tool
server.tool("add",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }]
})
);
// Add a dynamic greeting resource
server.resource(
"file",
new ResourceTemplate("file://{path}", { list: undefined }),
async (uri, { path }) => ({
contents: [{
uri: uri.href,
text: `File, ${path}!`
}]
})
);
server.prompt(
"review-code",
{ code: z.string() },
({ code }) => ({
messages: [{
role: "user",
content: {
type: "text",
text: `Please review this code:\n\n${code}`
}
}]
})
);
// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
await server.connect(transport);
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": false
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
+908
View File
@@ -0,0 +1,908 @@
# Creating a client
Clients are custom applications or scripts that communicate directly with an MCP Server to request resources, tools, and prompts. Unlike using the inspector tool, which provides a graphical interface for interacting with the server, writing your own client allows for programmatic and automated interactions. This enables developers to integrate MCP capabilities into their own workflows, automate tasks, and build custom solutions tailored to specific needs.
## Overview
This lesson introduces the concept of clients within the Model Context Protocol (MCP) ecosystem. You'll learn how to write your own client and have it connect to an MCP Server.
## Learning Objectives
By the end of this lesson, you will be able to:
- Understand what a client can do.
- Write your own client.
- Connect and test the client with an MCP server to ensure the latter works as expected.
## What goes into writing a client?
To write a client, you'll need to do the following:
- **Import the correct libraries**. You'll be using the same library as before, just different constructs.
- **Instantiate a client**. This will involve creating a client instance and connect it to the chosen transport method.
- **Decide on what resources to list**. Your MCP server comes with resources, tools and prompts, you need to decide which one to list.
- **Integrate the client to a host application**. Once you know the capabilities of the server you need to integrate this your host application so that if a user types a prompt or other command the corresponding server feature is invoked.
Now that we understand at high level what we're about to do, let's look at an example next.
### An example client
Let's have a look at this example client:
### TypeScript
```typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["server.js"]
});
const client = new Client(
{
name: "example-client",
version: "1.0.0"
}
);
await client.connect(transport);
// List prompts
const prompts = await client.listPrompts();
// Get a prompt
const prompt = await client.getPrompt({
name: "example-prompt",
arguments: {
arg1: "value"
}
});
// List resources
const resources = await client.listResources();
// Read a resource
const resource = await client.readResource({
uri: "file:///example.txt"
});
// Call a tool
const result = await client.callTool({
name: "example-tool",
arguments: {
arg1: "value"
}
});
```
In the preceding code we:
- Import the libraries
- Create an instance of a client and connect it using stdio for transport.
- List prompts, resources and tools and invoke them all.
There you have it, a client that can talk to an MCP Server.
Let's take our time in the next exercise section and break down each code snippet and explain what's going on.
## Exercise: Writing a client
As said above, let's take our time explaining the code, and by all means code along if you want.
### -1- Import the libraries
Let's import the libraries we need, we will need references to a client and to our chosen transport protocol, stdio. stdio is a protocol for things meant to run on your local machine. SSE is another transport protocol we will show in future chapters but that's your other option. For now though, let's continue with stdio.
#### TypeScript
```typescript
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
```
#### Python
```python
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_client
```
#### .NET
```csharp
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Client;
```
#### Java
For Java, you'll create a client that connects to the MCP server from the previous exercise. Using the same Java Spring Boot project structure from [Getting Started with MCP Server](../01-first-server/solution/java), create a new Java class called `SDKClient` in the `src/main/java/com/microsoft/mcp/sample/client/` folder and add the following imports:
```java
import java.util.Map;
import org.springframework.web.reactive.function.client.WebClient;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
```
#### Rust
You will need to add the following dependencies to your `Cargo.toml` file.
```toml
[package]
name = "calculator-client"
version = "0.1.0"
edition = "2024"
[dependencies]
rmcp = { version = "0.5.0", features = ["client", "transport-child-process"] }
serde_json = "1.0.141"
tokio = { version = "1.46.1", features = ["rt-multi-thread"] }
```
From there, you can import the necessary libraries in your client code.
```rust
use rmcp::{
RmcpError,
model::CallToolRequestParam,
service::ServiceExt,
transport::{ConfigureCommandExt, TokioChildProcess},
};
use tokio::process::Command;
```
Let's move on to instantiation.
### -2- Instantiating client and transport
We will need to create an instance of the transport and that of our client:
#### TypeScript
```typescript
const transport = new StdioClientTransport({
command: "node",
args: ["server.js"]
});
const client = new Client(
{
name: "example-client",
version: "1.0.0"
}
);
await client.connect(transport);
```
In the preceding code we've:
- Created an stdio transport instance. Note how it specifies command and args for how to find and start up the server as that's something we will need to do as we create the client.
```typescript
const transport = new StdioClientTransport({
command: "node",
args: ["server.js"]
});
```
- Instantiated a client by giving it a name and version.
```typescript
const client = new Client(
{
name: "example-client",
version: "1.0.0"
});
```
- Connected the client to the chosen transport.
```typescript
await client.connect(transport);
```
#### Python
```python
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_client
# Create server parameters for stdio connection
server_params = StdioServerParameters(
command="mcp", # Executable
args=["run", "server.py"], # Optional command line arguments
env=None, # Optional environment variables
)
async def run():
async with stdio_client(server_params) as (read, write):
async with ClientSession(
read, write
) as session:
# Initialize the connection
await session.initialize()
if __name__ == "__main__":
import asyncio
asyncio.run(run())
```
In the preceding code we've:
- Imported the needed libraries
- Instantiated a server parameters object as we will use this to run the server so we can connect to it with our client.
- Defined a method `run` that in turn calls `stdio_client` which starts a client session.
- Created an entry point where we provide the `run` method to `asyncio.run`.
#### .NET
```dotnet
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Client;
var builder = Host.CreateApplicationBuilder(args);
builder.Configuration
.AddEnvironmentVariables()
.AddUserSecrets<Program>();
var clientTransport = new StdioClientTransport(new()
{
Name = "Demo Server",
Command = "dotnet",
Arguments = ["run", "--project", "path/to/file.csproj"],
});
await using var mcpClient = await McpClient.CreateAsync(clientTransport);
```
In the preceding code we've:
- Imported the needed libraries.
- Create an stdio transport and created a client `mcpClient`. The latter is something we will use to list and invoke features on the MCP Server.
Note, in "Arguments", you can either point to the *.csproj* or to the executable.
#### Java
```java
public class SDKClient {
public static void main(String[] args) {
var transport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
new SDKClient(transport).run();
}
private final McpClientTransport transport;
public SDKClient(McpClientTransport transport) {
this.transport = transport;
}
public void run() {
var client = McpClient.sync(this.transport).build();
client.initialize();
// Your client logic goes here
}
}
```
In the preceding code we've:
- Created a main method that sets up an SSE transport pointing to `http://localhost:8080` where our MCP server will be running.
- Created a client class that takes the transport as a constructor parameter.
- In the `run` method, we create a synchronous MCP client using the transport and initialize the connection.
- Used SSE (Server-Sent Events) transport which is suitable for HTTP-based communication with Java Spring Boot MCP servers.
#### Rust
Note this Rust client assumes the server is a sibling project named "calculator-server" in the same directory. The code below will start the server and connect to it.
```rust
async fn main() -> Result<(), RmcpError> {
// Assume the server is a sibling project named "calculator-server" in the same directory
let server_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("failed to locate workspace root")
.join("calculator-server");
let client = ()
.serve(
TokioChildProcess::new(Command::new("cargo").configure(|cmd| {
cmd.arg("run").current_dir(server_dir);
}))
.map_err(RmcpError::transport_creation::<TokioChildProcess>)?,
)
.await?;
// TODO: Initialize
// TODO: List tools
// TODO: Call add tool with arguments = {"a": 3, "b": 2}
client.cancel().await?;
Ok(())
}
```
### -3- Listing the server features
Now, we have a client that can connect to should the program be run. However, it doesn't actually list its features so let's do that next:
#### TypeScript
```typescript
// List prompts
const prompts = await client.listPrompts();
// List resources
const resources = await client.listResources();
// list tools
const tools = await client.listTools();
```
#### Python
```python
# List available resources
resources = await session.list_resources()
print("LISTING RESOURCES")
for resource in resources:
print("Resource: ", resource)
# List available tools
tools = await session.list_tools()
print("LISTING TOOLS")
for tool in tools.tools:
print("Tool: ", tool.name)
```
Here we list the available resources, `list_resources()` and tools, `list_tools` and print them out.
#### .NET
```dotnet
foreach (var tool in await client.ListToolsAsync())
{
Console.WriteLine($"{tool.Name} ({tool.Description})");
}
```
Above is an example how we can list the tools on the server. For each tool, we then print out its name.
#### Java
```java
// List and demonstrate tools
ListToolsResult toolsList = client.listTools();
System.out.println("Available Tools = " + toolsList);
// You can also ping the server to verify connection
client.ping();
```
In the preceding code we've:
- Called `listTools()` to get all available tools from the MCP server.
- Used `ping()` to verify that the connection to the server is working.
- The `ListToolsResult` contains information about all tools including their names, descriptions, and input schemas.
Great, now we've captures all the features. Now the question is when do we use them? Well, this client is pretty simple, simple in the sense that we will need to explicitly call the features when we want them. In the next chapter, we will create a more advanced client that has access to it's own large language model, LLM. For now though, let's see how we can invoke the features on the server:
#### Rust
In the main function, after initializing the client, we can initialize the server and list some of its features.
```rust
// Initialize
let server_info = client.peer_info();
println!("Server info: {:?}", server_info);
// List tools
let tools = client.list_tools(Default::default()).await?;
println!("Available tools: {:?}", tools);
```
### -4- Invoke features
To invoke the features we need to ensure we specify the correct arguments and in some cases the name of what we're trying to invoke.
#### TypeScript
```typescript
// Read a resource
const resource = await client.readResource({
uri: "file:///example.txt"
});
// Call a tool
const result = await client.callTool({
name: "example-tool",
arguments: {
arg1: "value"
}
});
// call prompt
const promptResult = await client.getPrompt({
name: "review-code",
arguments: {
code: "console.log(\"Hello world\")"
}
})
```
In the preceding code we:
- Read a resource, we call the resource by calling `readResource()` specifying `uri`. Here's what it most likely look like on the server side:
```typescript
server.resource(
"readFile",
new ResourceTemplate("file://{name}", { list: undefined }),
async (uri, { name }) => ({
contents: [{
uri: uri.href,
text: `Hello, ${name}!`
}]
})
);
```
Our `uri` value `file://example.txt` matches `file://{name}` on the server. `example.txt` will be mapped to `name`.
- Call a tool, we call it by specifying its `name` and its `arguments` like so:
```typescript
const result = await client.callTool({
name: "example-tool",
arguments: {
arg1: "value"
}
});
```
- Get prompt, to get a prompt, you call `getPrompt()` with `name` and `arguments`. The server code looks like so:
```typescript
server.prompt(
"review-code",
{ code: z.string() },
({ code }) => ({
messages: [{
role: "user",
content: {
type: "text",
text: `Please review this code:\n\n${code}`
}
}]
})
);
```
and your resulting client code therefore looks like so to match what's declared on the server:
```typescript
const promptResult = await client.getPrompt({
name: "review-code",
arguments: {
code: "console.log(\"Hello world\")"
}
})
```
#### Python
```python
# Read a resource
print("READING RESOURCE")
content, mime_type = await session.read_resource("greeting://hello")
# Call a tool
print("CALL TOOL")
result = await session.call_tool("add", arguments={"a": 1, "b": 7})
print(result.content)
```
In the preceding code, we've:
- Called a resource called `greeting` using `read_resource`.
- Invoked a tool called `add` using `call_tool`.
#### .NET
1. Let's add some code to call a tool:
```csharp
var result = await mcpClient.CallToolAsync(
"Add",
new Dictionary<string, object?>() { ["a"] = 1, ["b"] = 3 },
cancellationToken:CancellationToken.None);
```
1. To print out the result, here's some code to handle that:
```csharp
Console.WriteLine(result.Content.First(c => c.Type == "text").Text);
// Sum 4
```
#### Java
```java
// Call various calculator tools
CallToolResult resultAdd = client.callTool(new CallToolRequest("add", Map.of("a", 5.0, "b", 3.0)));
System.out.println("Add Result = " + resultAdd);
CallToolResult resultSubtract = client.callTool(new CallToolRequest("subtract", Map.of("a", 10.0, "b", 4.0)));
System.out.println("Subtract Result = " + resultSubtract);
CallToolResult resultMultiply = client.callTool(new CallToolRequest("multiply", Map.of("a", 6.0, "b", 7.0)));
System.out.println("Multiply Result = " + resultMultiply);
CallToolResult resultDivide = client.callTool(new CallToolRequest("divide", Map.of("a", 20.0, "b", 4.0)));
System.out.println("Divide Result = " + resultDivide);
CallToolResult resultHelp = client.callTool(new CallToolRequest("help", Map.of()));
System.out.println("Help = " + resultHelp);
```
In the preceding code we've:
- Called multiple calculator tools using `callTool()` method with `CallToolRequest` objects.
- Each tool call specifies the tool name and a `Map` of arguments required by that tool.
- The server tools expect specific parameter names (like "a", "b" for mathematical operations).
- Results are returned as `CallToolResult` objects containing the response from the server.
#### Rust
```rust
// Call add tool with arguments = {"a": 3, "b": 2}
let a = 3;
let b = 2;
let tool_result = client
.call_tool(CallToolRequestParam {
name: "add".into(),
arguments: serde_json::json!({ "a": a, "b": b }).as_object().cloned(),
})
.await?;
println!("Result of {:?} + {:?}: {:?}", a, b, tool_result);
```
### -5- Run the client
To run the client, type the following command in the terminal:
#### TypeScript
Add the following entry to your "scripts" section in *package.json*:
```json
"client": "tsc && node build/client.js"
```
```sh
npm run client
```
#### Python
Call the client with the following command:
```sh
python client.py
```
#### .NET
```sh
dotnet run
```
#### Java
First, ensure your MCP server is running on `http://localhost:8080`. Then run the client:
```bash
# Build you project
./mvnw clean compile
# Run the client
./mvnw exec:java -Dexec.mainClass="com.microsoft.mcp.sample.client.SDKClient"
```
Alternatively, you can run the complete client project provided in the solution folder `03-GettingStarted\02-client\solution\java`:
```bash
# Navigate to the solution directory
cd 03-GettingStarted/02-client/solution/java
# Build and run the JAR
./mvnw clean package
java -jar target/calculator-client-0.0.1-SNAPSHOT.jar
```
#### Rust
```bash
cargo fmt
cargo run
```
## Assignment
In this assignment, you'll use what you've learned in creating a client but create a client of your own.
Here's a server you can use that you need to call via your client code, see if you can add more features to the server to make it more interesting.
### TypeScript
```typescript
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create an MCP server
const server = new McpServer({
name: "Demo",
version: "1.0.0"
});
// Add an addition tool
server.tool("add",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }]
})
);
// Add a dynamic greeting resource
server.resource(
"greeting",
new ResourceTemplate("greeting://{name}", { list: undefined }),
async (uri, { name }) => ({
contents: [{
uri: uri.href,
text: `Hello, ${name}!`
}]
})
);
// Start receiving messages on stdin and sending messages on stdout
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCPServer started on stdin/stdout");
}
main().catch((error) => {
console.error("Fatal error: ", error);
process.exit(1);
});
```
### Python
```python
# server.py
from mcp.server.fastmcp import FastMCP
# Create an MCP server
mcp = FastMCP("Demo")
# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
# Add a dynamic greeting resource
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"
```
### .NET
```csharp
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(consoleLogOptions =>
{
// Configure all logs to go to stderr
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
[McpServerToolType]
public static class CalculatorTool
{
[McpServerTool, Description("Adds two numbers")]
public static string Add(int a, int b) => $"Sum {a + b}";
}
```
See this project to see how you can [add prompts and resources](https://github.com/modelcontextprotocol/csharp-sdk/blob/main/samples/EverythingServer/Program.cs).
Also, check this link for how to invoke [prompts and resources](https://github.com/modelcontextprotocol/csharp-sdk/blob/main/src/ModelContextProtocol/Client/).
### Rust
In the [previous section](../01-first-server), you learned how to create a simple MCP server with Rust. You can continue to build on that or check this link for more Rust-based MCP server examples: [MCP Server Examples](https://github.com/modelcontextprotocol/rust-sdk/tree/main/examples/servers)
## Solution
The **solution folder** contains complete, ready-to-run client implementations that demonstrate all the concepts covered in this tutorial. Each solution includes both client and server code organized in separate, self-contained projects.
### 📁 Solution Structure
The solution directory is organized by programming language:
```text
solution/
├── typescript/ # TypeScript client with npm/Node.js setup
│ ├── package.json # Dependencies and scripts
│ ├── tsconfig.json # TypeScript configuration
│ └── src/ # Source code
├── java/ # Java Spring Boot client project
│ ├── pom.xml # Maven configuration
│ ├── src/ # Java source files
│ └── mvnw # Maven wrapper
├── python/ # Python client implementation
│ ├── client.py # Main client code
│ ├── server.py # Compatible server
│ └── README.md # Python-specific instructions
├── dotnet/ # .NET client project
│ ├── dotnet.csproj # Project configuration
│ ├── Program.cs # Main client code
│ └── dotnet.sln # Solution file
├── rust/ # Rust client implementation
| ├── Cargo.lock # Cargo lock file
| ├── Cargo.toml # Project configuration and dependencies
| ├── src # Source code
| │ └── main.rs # Main client code
└── server/ # Additional .NET server implementation
├── Program.cs # Server code
└── server.csproj # Server project file
```
### 🚀 What Each Solution Includes
Each language-specific solution provides:
- **Complete client implementation** with all features from the tutorial
- **Working project structure** with proper dependencies and configuration
- **Build and run scripts** for easy setup and execution
- **Detailed README** with language-specific instructions
- **Error handling** and result processing examples
### 📖 Using the Solutions
1. **Navigate to your preferred language folder**:
```bash
cd solution/typescript/ # For TypeScript
cd solution/java/ # For Java
cd solution/python/ # For Python
cd solution/dotnet/ # For .NET
```
2. **Follow the README instructions** in each folder for:
- Installing dependencies
- Building the project
- Running the client
3. **Example output** you should see:
```text
Prompt: Please review this code: console.log("hello");
Resource template: file
Tool result: { content: [ { type: 'text', text: '9' } ] }
```
For complete documentation and step-by-step instructions, see: **[📖 Solution Documentation](./solution/README.md)**
## 🎯 Complete Examples
We've provided complete, working client implementations for all programming languages covered in this tutorial. These examples demonstrate the full functionality described above and can be used as reference implementations or starting points for your own projects.
### Available Complete Examples
| Language | File | Description |
|----------|------|-------------|
| **Java** | [`client_example_java.java`](./client_example_java.java) | Complete Java client using SSE transport with comprehensive error handling |
| **C#** | [`client_example_csharp.cs`](./client_example_csharp.cs) | Complete C# client using stdio transport with automatic server startup |
| **TypeScript** | [`client_example_typescript.ts`](./client_example_typescript.ts) | Complete TypeScript client with full MCP protocol support |
| **Python** | [`client_example_python.py`](./client_example_python.py) | Complete Python client using async/await patterns |
| **Rust** | [`client_example_rust.rs`](./client_example_rust.rs) | Complete Rust client using Tokio for async operations |
Each complete example includes:
- ✅ **Connection establishment** and error handling
- ✅ **Server discovery** (tools, resources, prompts where applicable)
- ✅ **Calculator operations** (add, subtract, multiply, divide, help)
- ✅ **Result processing** and formatted output
- ✅ **Comprehensive error handling**
- ✅ **Clean, documented code** with step-by-step comments
### Getting Started with Complete Examples
1. **Choose your preferred language** from the table above
2. **Review the complete example file** to understand the full implementation
3. **Run the example** following the instructions in [`complete_examples.md`](./complete_examples.md)
4. **Modify and extend** the example for your specific use case
For detailed documentation about running and customizing these examples, see: **[📖 Complete Examples Documentation](./complete_examples.md)**
### 💡 Solution vs. Complete Examples
| **Solution Folder** | **Complete Examples** |
|--------------------|--------------------- |
| Full project structure with build files | Single-file implementations |
| Ready-to-run with dependencies | Focused code examples |
| Production-like setup | Educational reference |
| Language-specific tooling | Cross-language comparison |
Both approaches are valuable - use the **solution folder** for complete projects and the **complete examples** for learning and reference.
## Key Takeaways
The key takeaways for this chapter is the following about clients:
- Can be used to both discover and invoke features on the server.
- Can start a server while it starts itself (like in this chapter) but clients can connect to running servers as well.
- Is a great way to test out server capabilities next to alternatives like the Inspector as was described in the previous chapter.
## Additional Resources
- [Building clients in MCP](https://modelcontextprotocol.io/quickstart/client)
## Samples
- [Java Calculator](../samples/java/calculator/README.md)
- [.Net Calculator](../samples/csharp/)
- [JavaScript Calculator](../samples/javascript/README.md)
- [TypeScript Calculator](../samples/typescript/README.md)
- [Python Calculator](../samples/python/)
- [Rust Calculator](../samples/rust/)
## What's Next
- Next: [Creating a client with an LLM](../03-llm-client/README.md)
@@ -0,0 +1,149 @@
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Client;
using System.Text.Json;
/**
* Complete C# MCP Client Example
*
* This client demonstrates how to:
* 1. Connect to an MCP server using stdio transport
* 2. List available tools and resources
* 3. Call calculator tools
* 4. Handle responses from the server
*/
Console.WriteLine("🚀 Starting MCP C# Client...");
try
{
// Create configuration builder
var builder = Host.CreateApplicationBuilder(args);
builder.Configuration
.AddEnvironmentVariables()
.AddUserSecrets<Program>();
// Create stdio transport to connect to the MCP server
var clientTransport = new StdioClientTransport(new()
{
Name = "Calculator Server",
Command = "dotnet",
Arguments = ["run", "--project", "../01-first-server/solution/csharp/calculator-server.csproj"],
});
Console.WriteLine("📡 Connecting to MCP server...");
// Create and connect the MCP client
await using var mcpClient = await McpClient.CreateAsync(clientTransport);
Console.WriteLine("✅ Connected to MCP server successfully!");
// List available tools
Console.WriteLine("\n📋 Listing available tools:");
var tools = await mcpClient.ListToolsAsync();
foreach (var tool in tools)
{
Console.WriteLine($" - {tool.Name}: {tool.Description}");
}
// Test calculator operations
Console.WriteLine("\n🧮 Testing Calculator Operations:");
// Addition
var addResult = await mcpClient.CallToolAsync(
"Add",
new Dictionary<string, object?>() { ["a"] = 5, ["b"] = 3 },
cancellationToken: CancellationToken.None
);
Console.WriteLine($"Add 5 + 3 = {ExtractTextResult(addResult)}");
// Subtraction
var subtractResult = await mcpClient.CallToolAsync(
"Subtract",
new Dictionary<string, object?>() { ["a"] = 10, ["b"] = 4 },
cancellationToken: CancellationToken.None
);
Console.WriteLine($"Subtract 10 - 4 = {ExtractTextResult(subtractResult)}");
// Multiplication
var multiplyResult = await mcpClient.CallToolAsync(
"Multiply",
new Dictionary<string, object?>() { ["a"] = 6, ["b"] = 7 },
cancellationToken: CancellationToken.None
);
Console.WriteLine($"Multiply 6 × 7 = {ExtractTextResult(multiplyResult)}");
// Division
var divideResult = await mcpClient.CallToolAsync(
"Divide",
new Dictionary<string, object?>() { ["a"] = 20, ["b"] = 4 },
cancellationToken: CancellationToken.None
);
Console.WriteLine($"Divide 20 ÷ 4 = {ExtractTextResult(divideResult)}");
// Help
var helpResult = await mcpClient.CallToolAsync(
"Help",
new Dictionary<string, object?>(),
cancellationToken: CancellationToken.None
);
Console.WriteLine($"\n📖 Help Information:");
Console.WriteLine(ExtractTextResult(helpResult));
// List resources if available
try
{
Console.WriteLine("\n📄 Listing available resources:");
var resources = await mcpClient.ListResourcesAsync();
foreach (var resource in resources)
{
Console.WriteLine($" - {resource.Name}: {resource.Description}");
}
}
catch (Exception ex)
{
Console.WriteLine(" No resources available or error listing resources: " + ex.Message);
}
Console.WriteLine("\n✨ Client operations completed successfully!");
}
catch (Exception ex)
{
Console.WriteLine($"❌ Error running MCP client: {ex.Message}");
Console.WriteLine($"Stack trace: {ex.StackTrace}");
}
/// <summary>
/// Extracts the text result from a tool call response object.
/// </summary>
/// <param name="result">The result object, which may contain text content or other data.</param>
/// <returns>
/// A string containing the extracted text if found, a serialized representation of the result if no text is found,
/// or a fallback string if serialization fails.
/// </returns>
static string ExtractTextResult(object result)
{
try
{
if (result is IEnumerable<object> contentList)
{
foreach (var content in contentList)
{
if (content is IDictionary<string, object> contentDict &&
contentDict.TryGetValue("text", out var text))
{
return text?.ToString() ?? "No text content";
}
}
}
// Fallback: try to serialize the entire result
return JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true });
}
catch
{
return result?.ToString() ?? "No result";
}
}
@@ -0,0 +1,113 @@
package com.microsoft.mcp.sample.client;
import java.util.Map;
import org.springframework.web.reactive.function.client.WebClient;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
/**
* Complete Java MCP Client Example
*
* This client demonstrates how to:
* 1. Connect to an MCP server using SSE transport
* 2. List available tools
* 3. Call various calculator tools
* 4. Handle responses from the server
*/
public class SDKClient {
public static void main(String[] args) {
// Create SSE transport pointing to the MCP server
var transport = new WebFluxSseClientTransport(
WebClient.builder().baseUrl("http://localhost:8080")
);
// Create and run the client
new SDKClient(transport).run();
}
private final McpClientTransport transport;
public SDKClient(McpClientTransport transport) {
this.transport = transport;
}
public void run() {
try {
// Create synchronous MCP client
var client = McpClient.sync(this.transport).build();
// Initialize the connection
client.initialize();
System.out.println("✅ Connected to MCP server successfully!");
// Verify connection with ping
client.ping();
System.out.println("✅ Server ping successful!");
// List available tools
ListToolsResult toolsList = client.listTools();
System.out.println("\n📋 Available Tools:");
toolsList.tools().forEach(tool -> {
System.out.println(" - " + tool.name() + ": " + tool.description());
});
// Test calculator operations
System.out.println("\n🧮 Testing Calculator Operations:");
// Addition
CallToolResult resultAdd = client.callTool(
new CallToolRequest("add", Map.of("a", 5.0, "b", 3.0))
);
System.out.println("Add 5 + 3 = " + extractResult(resultAdd));
// Subtraction
CallToolResult resultSubtract = client.callTool(
new CallToolRequest("subtract", Map.of("a", 10.0, "b", 4.0))
);
System.out.println("Subtract 10 - 4 = " + extractResult(resultSubtract));
// Multiplication
CallToolResult resultMultiply = client.callTool(
new CallToolRequest("multiply", Map.of("a", 6.0, "b", 7.0))
);
System.out.println("Multiply 6 × 7 = " + extractResult(resultMultiply));
// Division
CallToolResult resultDivide = client.callTool(
new CallToolRequest("divide", Map.of("a", 20.0, "b", 4.0))
);
System.out.println("Divide 20 ÷ 4 = " + extractResult(resultDivide));
// Help
CallToolResult resultHelp = client.callTool(
new CallToolRequest("help", Map.of())
);
System.out.println("\n📖 Help Information:");
System.out.println(extractResult(resultHelp));
System.out.println("\n✨ Client operations completed successfully!");
} catch (Exception e) {
System.err.println("❌ Error running MCP client: " + e.getMessage());
e.printStackTrace();
}
}
/**
* Extract the text result from a CallToolResult
*/
private String extractResult(CallToolResult result) {
if (result != null && result.content() != null && !result.content().isEmpty()) {
var firstContent = result.content().get(0);
if (firstContent.text() != null) {
return firstContent.text();
}
}
return "No result";
}
}
@@ -0,0 +1,150 @@
"""
Complete Python MCP Client Example
This client demonstrates how to:
1. Connect to an MCP server using stdio transport
2. List available tools and resources
3. Call calculator tools
4. Handle responses from the server
"""
import asyncio
import json
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_client
class MCPCalculatorClient:
def __init__(self):
# Create server parameters for stdio connection
self.server_params = StdioServerParameters(
command="python", # Executable
args=["../01-first-server/solution/python/server.py"], # Server script
env=None, # Optional environment variables
)
async def run(self):
"""Main client execution function"""
print("🚀 Starting MCP Python Client...")
try:
async with stdio_client(self.server_params) as (read, write):
async with ClientSession(read, write) as session:
print("📡 Connecting to MCP server...")
# Initialize the connection
await session.initialize()
print("✅ Connected to MCP server successfully!")
# List available tools
await self.list_tools(session)
# Test calculator operations
await self.test_calculator_operations(session)
# List and test resources
await self.list_and_test_resources(session)
print("\n✨ Client operations completed successfully!")
except Exception as e:
print(f"❌ Error running MCP client: {e}")
raise
async def list_tools(self, session: ClientSession):
"""List all available tools on the server"""
print("\n📋 Listing available tools:")
try:
tools = await session.list_tools()
for tool in tools.tools:
print(f" - {tool.name}: {tool.description}")
except Exception as e:
print(f" Error listing tools: {e}")
async def test_calculator_operations(self, session: ClientSession):
"""Test various calculator operations"""
print("\n🧮 Testing Calculator Operations:")
operations = [
("add", {"a": 5, "b": 3}, "Add 5 + 3"),
("subtract", {"a": 10, "b": 4}, "Subtract 10 - 4"),
("multiply", {"a": 6, "b": 7}, "Multiply 6 × 7"),
("divide", {"a": 20, "b": 4}, "Divide 20 ÷ 4"),
("help", {}, "Help Information"),
]
for tool_name, arguments, description in operations:
try:
result = await session.call_tool(tool_name, arguments=arguments)
result_text = self.extract_text_result(result)
if tool_name == "help":
print(f"\n📖 {description}:")
print(result_text)
else:
print(f"{description} = {result_text}")
except Exception as e:
print(f" Error calling {tool_name}: {e}")
async def list_and_test_resources(self, session: ClientSession):
"""List and test reading resources"""
print("\n📄 Listing available resources:")
try:
resources = await session.list_resources()
for resource in resources.resources:
print(f" - {resource.name}: {resource.description}")
print(f" URI: {resource.uri}")
# Test reading a resource if available
if resources.resources:
first_resource = resources.resources[0]
print(f"\n📖 Reading resource: {first_resource.name}")
try:
content = await session.read_resource(first_resource.uri)
print(f"Resource content: {content}")
except Exception as e:
print(f" Error reading resource: {e}")
else:
print(" No resources available")
except Exception as e:
print(f" Error listing resources: {e}")
def extract_text_result(self, result) -> str:
"""
Extract text content from a tool result object.
This method attempts to extract the text content from the `content` attribute
of the result object. If no text content is found, it falls back to converting
the result to a string. If an error occurs during extraction, it returns "No result".
Args:
result: The result object returned by a tool, which may contain a `content` attribute
with text or other types of data.
Returns:
A string representing the extracted text content, or a fallback string if no text is found.
"""
try:
if hasattr(result, 'content') and result.content:
for content_item in result.content:
if hasattr(content_item, 'text') and content_item.text:
return content_item.text
elif hasattr(content_item, 'type') and content_item.type == "text":
return getattr(content_item, 'text', str(content_item))
# Fallback: try to convert to string
return str(result)
except Exception:
return "No result"
async def main():
"""Entry point for the client"""
client = MCPCalculatorClient()
await client.run()
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,57 @@
use rmcp::{
RmcpError,
model::CallToolRequestParam,
service::ServiceExt,
transport::{ConfigureCommandExt, TokioChildProcess},
};
use tokio::process::Command;
/**
* Complete Rust MCP Client Example
*
* This client demonstrates how to:
* 1. Connect to an MCP server using child process transport
* 2. List available tools
* 3. Call various calculator tools
* 4. Handle responses from the server
*/
#[tokio::main]
async fn main() -> Result<(), RmcpError> {
// Assume the server is a sibling project named "calculator-server" in the same directory
let server_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.expect("failed to locate workspace root")
.join("calculator-server");
let client = ()
.serve(
TokioChildProcess::new(Command::new("cargo").configure(|cmd| {
cmd.arg("run").current_dir(server_dir);
}))
.map_err(RmcpError::transport_creation::<TokioChildProcess>)?,
)
.await?;
// Initialize
let server_info = client.peer_info();
println!("Server info: {:?}", server_info);
// List tools
let tools = client.list_tools(Default::default()).await?;
println!("Available tools: {:?}", tools);
// Call add tool with arguments = {"a": 3, "b": 2}
let a = 3;
let b = 2;
let tool_result = client
.call_tool(CallToolRequestParam {
name: "add".into(),
arguments: serde_json::json!({ "a": a, "b": b }).as_object().cloned(),
})
.await?;
println!("Result of {:?} + {:?}: {:?}", a, b, tool_result);
client.cancel().await?;
Ok(())
}
@@ -0,0 +1,179 @@
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
/**
* Complete TypeScript MCP Client Example
*
* This client demonstrates how to:
* 1. Connect to an MCP server using stdio transport
* 2. List available tools, resources, and prompts
* 3. Call calculator tools
* 4. Read resources and get prompts
* 5. Handle responses from the server
*/
async function runClient() {
console.log("🚀 Starting MCP TypeScript Client...");
try {
// Create stdio transport to connect to the MCP server
const transport = new StdioClientTransport({
command: "node",
args: ["../01-first-server/solution/typescript/server.js"]
});
// Create client instance
const client = new Client({
name: "calculator-client",
version: "1.0.0"
});
console.log("📡 Connecting to MCP server...");
// Connect to the server
await client.connect(transport);
console.log("✅ Connected to MCP server successfully!");
// List available tools
console.log("\n📋 Listing available tools:");
const tools = await client.listTools();
tools.tools.forEach(tool => {
console.log(` - ${tool.name}: ${tool.description}`);
});
// Test calculator operations
console.log("\n🧮 Testing Calculator Operations:");
// Addition
const addResult = await client.callTool({
name: "add",
arguments: {
a: 5,
b: 3
}
});
console.log(`Add 5 + 3 = ${extractTextResult(addResult)}`);
// Subtraction
const subtractResult = await client.callTool({
name: "subtract",
arguments: {
a: 10,
b: 4
}
});
console.log(`Subtract 10 - 4 = ${extractTextResult(subtractResult)}`);
// Multiplication
const multiplyResult = await client.callTool({
name: "multiply",
arguments: {
a: 6,
b: 7
}
});
console.log(`Multiply 6 × 7 = ${extractTextResult(multiplyResult)}`);
// Division
const divideResult = await client.callTool({
name: "divide",
arguments: {
a: 20,
b: 4
}
});
console.log(`Divide 20 ÷ 4 = ${extractTextResult(divideResult)}`);
// Help
const helpResult = await client.callTool({
name: "help",
arguments: {}
});
console.log(`\n📖 Help Information:`);
console.log(extractTextResult(helpResult));
// List available resources
try {
console.log("\n📄 Listing available resources:");
const resources = await client.listResources();
resources.resources.forEach(resource => {
console.log(` - ${resource.name}: ${resource.description}`);
});
// Test reading a resource if available
if (resources.resources.length > 0) {
const firstResource = resources.resources[0];
console.log(`\n📖 Reading resource: ${firstResource.name}`);
const resourceContent = await client.readResource({
uri: firstResource.uri
});
console.log(`Resource content: ${JSON.stringify(resourceContent, null, 2)}`);
}
} catch (error) {
console.log(" No resources available or error listing resources:", error.message);
}
// List available prompts
try {
console.log("\n💬 Listing available prompts:");
const prompts = await client.listPrompts();
prompts.prompts.forEach(prompt => {
console.log(` - ${prompt.name}: ${prompt.description}`);
});
// Test getting a prompt if available
if (prompts.prompts.length > 0) {
const firstPrompt = prompts.prompts[0];
console.log(`\n💬 Getting prompt: ${firstPrompt.name}`);
const promptResult = await client.getPrompt({
name: firstPrompt.name,
arguments: {
code: "console.log('Hello, MCP!');"
}
});
console.log(`Prompt result: ${JSON.stringify(promptResult, null, 2)}`);
}
} catch (error) {
console.log(" No prompts available or error listing prompts:", error.message);
}
console.log("\n✨ Client operations completed successfully!");
} catch (error) {
console.error("❌ Error running MCP client:", error);
process.exit(1);
}
}
/**
* Extract the text result from a tool call response
*/
function extractTextResult(result: any): string {
try {
if (result && result.content && Array.isArray(result.content)) {
const textContent = result.content.find((c: any) => c.type === "text");
if (textContent && textContent.text) {
return textContent.text;
}
}
// Fallback: stringify the entire result
return JSON.stringify(result, null, 2);
} catch {
return result?.toString() || "No result";
}
}
// Error handling wrapper
async function main() {
try {
await runClient();
} catch (error) {
console.error("Fatal error:", error);
process.exit(1);
}
}
// Run the client
main();
@@ -0,0 +1,159 @@
# Complete MCP Client Examples
This directory contains complete, working examples of MCP clients in different programming languages. Each client demonstrates the full functionality described in the main README.md tutorial.
## Available Clients
### 1. Java Client (`client_example_java.java`)
- **Transport**: SSE (Server-Sent Events) over HTTP
- **Target Server**: `http://localhost:8080`
- **Features**:
- Connection establishment and ping
- Tool listing
- Calculator operations (add, subtract, multiply, divide, help)
- Error handling and result extraction
**To run:**
```bash
# Ensure your MCP server is running on localhost:8080
javac client_example_java.java
java client_example_java
```
### 2. C# Client (`client_example_csharp.cs`)
- **Transport**: Stdio (Standard Input/Output)
- **Target Server**: Local .NET MCP server via dotnet run
- **Features**:
- Automatic server startup via stdio transport
- Tool and resource listing
- Calculator operations
- JSON result parsing
- Comprehensive error handling
**To run:**
```bash
dotnet run
```
### 3. TypeScript Client (`client_example_typescript.ts`)
- **Transport**: Stdio (Standard Input/Output)
- **Target Server**: Local Node.js MCP server
- **Features**:
- Full MCP protocol support
- Tool, resource, and prompt operations
- Calculator operations
- Resource reading and prompt execution
- Robust error handling
**To run:**
```bash
# First compile TypeScript (if needed)
npm run build
# Then run the client
npm run client
# or
node client_example_typescript.js
```
### 4. Python Client (`client_example_python.py`)
- **Transport**: Stdio (Standard Input/Output)
- **Target Server**: Local Python MCP server
- **Features**:
- Async/await pattern for operations
- Tool and resource discovery
- Calculator operations testing
- Resource content reading
- Class-based organization
**To run:**
```bash
python client_example_python.py
```
## Common Features Across All Clients
Each client implementation demonstrates:
1. **Connection Management**
- Establishing connection to MCP server
- Handling connection errors
- Proper cleanup and resource management
2. **Server Discovery**
- Listing available tools
- Listing available resources (where supported)
- Listing available prompts (where supported)
3. **Tool Invocation**
- Basic calculator operations (add, subtract, multiply, divide)
- Help command for server information
- Proper argument passing and result handling
4. **Error Handling**
- Connection errors
- Tool execution errors
- Graceful failure and user feedback
5. **Result Processing**
- Extracting text content from responses
- Formatting output for readability
- Handling different response formats
## Prerequisites
Before running these clients, ensure you have:
1. **The corresponding MCP server running** (from `../01-first-server/`)
2. **Required dependencies installed** for your chosen language
3. **Proper network connectivity** (for HTTP-based transports)
## Key Differences Between Implementations
| Language | Transport | Server Startup | Async Model | Key Libraries |
|------------|-----------|----------------|-------------|---------------------|
| Java | SSE/HTTP | External | Sync | WebFlux, MCP SDK |
| C# | Stdio | Automatic | Async/Await | .NET MCP SDK |
| TypeScript | Stdio | Automatic | Async/Await | Node MCP SDK |
| Python | Stdio | Automatic | AsyncIO | Python MCP SDK |
| Rust | Stdio | Automatic | Async/Await | Rust MCP SDK, Tokio |
## Next Steps
After exploring these client examples:
1. **Modify the clients** to add new features or operations
2. **Create your own server** and test it with these clients
3. **Experiment with different transports** (SSE vs. Stdio)
4. **Build a more complex application** that integrates MCP functionality
## Troubleshooting
### Common Issues
1. **Connection refused**: Ensure the MCP server is running on the expected port/path
2. **Module not found**: Install the required MCP SDK for your language
3. **Permission denied**: Check file permissions for stdio transport
4. **Tool not found**: Verify the server implements the expected tools
### Debug Tips
1. **Enable verbose logging** in your MCP SDK
2. **Check server logs** for error messages
3. **Verify tool names and signatures** match between client and server
4. **Test with MCP Inspector** first to validate server functionality
## Related Documentation
- [Main Client Tutorial](./README.md)
- [MCP Server Examples](../01-first-server/)
- [MCP with LLM Integration](../03-llm-client/)
- [Official MCP Documentation](https://modelcontextprotocol.io/)
@@ -0,0 +1,7 @@
Here's the solutions for each runtime:
- [TypeScript](./typescript/README.md)
- [Python](./python/README.md)
- [.NET](./dotnet/)
- [Java](./java/README.md)
- [Rust](./rust)
@@ -0,0 +1,44 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ModelContextProtocol.Client;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Protocol;
using System.Diagnostics;
using System.Net;
using System.Text;
var builder = Host.CreateApplicationBuilder(args);
builder.Configuration
.AddEnvironmentVariables()
.AddUserSecrets<Program>();
var clientTransport = new StdioClientTransport(new()
{
Name = "Demo Server",
Command = $"{Path.Combine(AppContext.BaseDirectory, "../../../../", "server/bin/Debug/net9.0/server")}",
Arguments = [],
});
Console.WriteLine("Setting up stdio transport");
await using var mcpClient = await McpClient.CreateAsync(clientTransport);
Console.WriteLine("Listing tools");
var tools = await mcpClient.ListToolsAsync();
foreach (var tool in tools)
{
Console.WriteLine($"Connected to server with tools: {tool.Name}");
}
var result = await mcpClient.CallToolAsync(
"add",
new Dictionary<string, object?>() { ["a"] = 1, ["b"] = 3 },
cancellationToken:CancellationToken.None);
Console.WriteLine("Result: " + ((TextContentBlock)result.Content[0]).Text);
@@ -0,0 +1,13 @@
# Running this sample
## -1- Install the dependencies
```bash
dotnet restore
```
## -3- Run the sample
```bash
dotnet run
```
@@ -0,0 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.*" />
<PackageReference Include="ModelContextProtocol" Version="0.*-*" />
</ItemGroup>
</Project>
@@ -0,0 +1,48 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet", "dotnet.csproj", "{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server", "..\server\server.csproj", "{C9862061-A22C-494F-BB09-FD49225F05A2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Debug|x64.ActiveCfg = Debug|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Debug|x64.Build.0 = Debug|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Debug|x86.ActiveCfg = Debug|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Debug|x86.Build.0 = Debug|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Release|Any CPU.Build.0 = Release|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Release|x64.ActiveCfg = Release|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Release|x64.Build.0 = Release|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Release|x86.ActiveCfg = Release|Any CPU
{5D513F3A-8CCF-47ED-AF59-5FBB9D335B27}.Release|x86.Build.0 = Release|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Debug|x64.ActiveCfg = Debug|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Debug|x64.Build.0 = Debug|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Debug|x86.ActiveCfg = Debug|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Debug|x86.Build.0 = Debug|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Release|Any CPU.Build.0 = Release|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Release|x64.ActiveCfg = Release|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Release|x64.Build.0 = Release|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Release|x86.ActiveCfg = Release|Any CPU
{C9862061-A22C-494F-BB09-FD49225F05A2}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -0,0 +1,117 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}
@@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
@@ -0,0 +1,190 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2025 Spring AI MCP Echo Server Sample
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,165 @@
# MCP Java Client - Calculator Demo
This project demonstrates how to create a Java client that connects to and interacts with an MCP (Model Context Protocol) server. In this example, we'll connect to the calculator server from Chapter 01 and perform various mathematical operations.
## Prerequisites
Before running this client, you need to:
1. **Start the Calculator Server** from Chapter 01:
- Navigate to the calculator server directory: `03-GettingStarted/01-first-server/solution/java/`
- Build and run the calculator server:
```cmd
cd ..\01-first-server\solution\java
.\mvnw clean install -DskipTests
java -jar target\calculator-server-0.0.1-SNAPSHOT.jar
```
- The server should be running on `http://localhost:8080`
2. **Java 21 or higher** installed on your system
3. **Maven** (included via Maven Wrapper)
## What is the SDKClient?
The `SDKClient` is a Java application that demonstrates how to:
- Connect to an MCP server using Server-Sent Events (SSE) transport
- List available tools from the server
- Call various calculator functions remotely
- Handle responses and display results
## How It Works
The client uses the Spring AI MCP framework to:
1. **Establish Connection**: Creates a WebFlux SSE client transport to connect to the calculator server
2. **Initialize Client**: Sets up the MCP client and establishes the connection
3. **Discover Tools**: Lists all available calculator operations
4. **Execute Operations**: Calls various mathematical functions with sample data
5. **Display Results**: Shows the results of each calculation
## Project Structure
```
src/
└── main/
└── java/
└── com/
└── microsoft/
└── mcp/
└── sample/
└── client/
└── SDKClient.java # Main client implementation
```
## Key Dependencies
The project uses the following key dependencies:
```xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
```
This dependency provides:
- `McpClient` - The main client interface
- `WebFluxSseClientTransport` - SSE transport for web-based communication
- MCP protocol schemas and request/response types
## Building the Project
Build the project using the Maven wrapper:
```cmd
.\mvnw clean install
```
## Running the Client
```cmd
java -jar .\target\calculator-client-0.0.1-SNAPSHOT.jar
```
**Note**: Make sure the calculator server is running on `http://localhost:8080` before executing any of these commands.
## What the Client Does
When you run the client, it will:
1. **Connect** to the calculator server at `http://localhost:8080`
2. **List Tools** - Shows all available calculator operations
3. **Perform Calculations**:
- Addition: 5 + 3 = 8
- Subtraction: 10 - 4 = 6
- Multiplication: 6 × 7 = 42
- Division: 20 ÷ 4 = 5
- Power: 2^8 = 256
- Square Root: √16 = 4
- Absolute Value: |-5.5| = 5.5
- Help: Shows available operations
## Expected Output
```
Available Tools = ListToolsResult[tools=[Tool[name=add, description=Add two numbers together, ...], ...]]
Add Result = CallToolResult[content=[TextContent[text="5,00 + 3,00 = 8,00"]], isError=false]
Subtract Result = CallToolResult[content=[TextContent[text="10,00 - 4,00 = 6,00"]], isError=false]
Multiply Result = CallToolResult[content=[TextContent[text="6,00 * 7,00 = 42,00"]], isError=false]
Divide Result = CallToolResult[content=[TextContent[text="20,00 / 4,00 = 5,00"]], isError=false]
Power Result = CallToolResult[content=[TextContent[text="2,00 ^ 8,00 = 256,00"]], isError=false]
Square Root Result = CallToolResult[content=[TextContent[text="√16,00 = 4,00"]], isError=false]
Absolute Result = CallToolResult[content=[TextContent[text="|-5,50| = 5,50"]], isError=false]
Help = CallToolResult[content=[TextContent[text="Basic Calculator MCP Service\n\nAvailable operations:\n1. add(a, b) - Adds two numbers\n2. subtract(a, b) - Subtracts the second number from the first\n..."]], isError=false]
```
**Note**: You may see Maven warnings about lingering threads at the end - this is normal for reactive applications and doesn't indicate an error.
## Understanding the Code
### 1. Transport Setup
```java
var transport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
```
This creates an SSE (Server-Sent Events) transport that connects to the calculator server.
### 2. Client Creation
```java
var client = McpClient.sync(this.transport).build();
client.initialize();
```
Creates a synchronous MCP client and initializes the connection.
### 3. Calling Tools
```java
CallToolResult resultAdd = client.callTool(new CallToolRequest("add", Map.of("a", 5.0, "b", 3.0)));
```
Calls the "add" tool with parameters a=5.0 and b=3.0.
## Troubleshooting
### Server Not Running
If you get connection errors, make sure the calculator server from Chapter 01 is running:
```
Error: Connection refused
```
**Solution**: Start the calculator server first.
### Port Already in Use
If port 8080 is busy:
```
Error: Address already in use
```
**Solution**: Stop other applications using port 8080 or modify the server to use a different port.
### Build Errors
If you encounter build errors:
```cmd
.\mvnw clean install -DskipTests
```
## Learn More
- [Spring AI MCP Documentation](https://docs.spring.io/spring-ai/reference/api/mcp/)
- [Model Context Protocol Specification](https://modelcontextprotocol.io/)
- [Spring WebFlux Documentation](https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html)
+310
View File
@@ -0,0 +1,310 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
+182
View File
@@ -0,0 +1,182 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%
@@ -0,0 +1,133 @@
<?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>3.4.4</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>calculator-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Calculator client</name>
<description>Basic calculator MCP client for beginners</description>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- JUnit dependencies for testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>21</release>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<mainClass>com.microsoft.mcp.sample.client.SDKClient</mainClass>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<java.version>21</java.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
</project>
@@ -0,0 +1,64 @@
package com.microsoft.mcp.sample.client;
import java.util.Map;
import org.springframework.web.reactive.function.client.WebClient;
import io.modelcontextprotocol.client.McpClient;
import io.modelcontextprotocol.client.transport.WebFluxSseClientTransport;
import io.modelcontextprotocol.spec.McpClientTransport;
import io.modelcontextprotocol.spec.McpSchema.CallToolRequest;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.ListToolsResult;
public class SDKClient {
public static void main(String[] args) {
var transport = new WebFluxSseClientTransport(WebClient.builder().baseUrl("http://localhost:8080"));
new SDKClient(transport).run();
}
private final McpClientTransport transport;
public SDKClient(McpClientTransport transport) {
this.transport = transport;
}
public void run() {
var client = McpClient.sync(this.transport).build();
client.initialize();
client.ping();
// List and demonstrate tools
ListToolsResult toolsList = client.listTools();
System.out.println("Available Tools = " + toolsList);
CallToolResult resultAdd = client.callTool(new CallToolRequest("add", Map.of("a", 5.0, "b", 3.0)));
System.out.println("Add Result = " + resultAdd);
CallToolResult resultSubtract = client.callTool(new CallToolRequest("subtract", Map.of("a", 10.0, "b", 4.0)));
System.out.println("Subtract Result = " + resultSubtract);
CallToolResult resultMultiply = client.callTool(new CallToolRequest("multiply", Map.of("a", 6.0, "b", 7.0)));
System.out.println("Multiply Result = " + resultMultiply);
CallToolResult resultDivide = client.callTool(new CallToolRequest("divide", Map.of("a", 20.0, "b", 4.0)));
System.out.println("Divide Result = " + resultDivide);
CallToolResult resultPower = client.callTool(new CallToolRequest("power", Map.of("base", 2.0, "exponent", 8.0)));
System.out.println("Power Result = " + resultPower);
CallToolResult resultSqrt = client.callTool(new CallToolRequest("squareRoot", Map.of("number", 16.0)));
System.out.println("Square Root Result = " + resultSqrt);
CallToolResult resultAbsolute = client.callTool(new CallToolRequest("absolute", Map.of("number", -5.5)));
System.out.println("Absolute Result = " + resultAbsolute);
CallToolResult resultHelp = client.callTool(new CallToolRequest("help", Map.of()));
System.out.println("Help = " + resultHelp);
client.closeGracefully();
}
}
@@ -0,0 +1,45 @@
# Running this sample
You're recommended to install `uv` but it's not a must, see [instructions](https://docs.astral.sh/uv/#highlights)
## -0- Create a virtual environment
```bash
python -m venv venv
```
## -1- Activate the virtual environment
```bash
venv\Scrips\activate
```
## -2- Install the dependencies
```bash
pip install "mcp[cli]"
```
## -3- Run the sample
```bash
python client.py
```
You should see an output similar to:
```text
LISTING RESOURCES
Resource: ('meta', None)
Resource: ('nextCursor', None)
Resource: ('resources', [])
INFO Processing request of type ListToolsRequest server.py:534
LISTING TOOLS
Tool: add
READING RESOURCE
INFO Processing request of type ReadResourceRequest server.py:534
CALL TOOL
INFO Processing request of type CallToolRequest server.py:534
[TextContent(type='text', text='8', annotations=None)]
```
@@ -0,0 +1,44 @@
from mcp import ClientSession, StdioServerParameters, types
from mcp.client.stdio import stdio_client
# Create server parameters for stdio connection
server_params = StdioServerParameters(
command="mcp", # Executable
args=["run", "server.py"], # Optional command line arguments
env=None, # Optional environment variables
)
async def run():
async with stdio_client(server_params) as (read, write):
async with ClientSession(
read, write
) as session:
# Initialize the connection
await session.initialize()
# List available resources
resources = await session.list_resources()
print("LISTING RESOURCES")
for resource in resources:
print("Resource: ", resource)
# List available tools
tools = await session.list_tools()
print("LISTING TOOLS")
for tool in tools.tools:
print("Tool: ", tool.name)
# Read a resource
print("READING RESOURCE")
content, mime_type = await session.read_resource("greeting://hello")
# Call a tool
print("CALL TOOL")
result = await session.call_tool("add", arguments={"a": 1, "b": 7})
print(result.content)
if __name__ == "__main__":
import asyncio
asyncio.run(run())
@@ -0,0 +1,20 @@
# server.py
from mcp.server.fastmcp import FastMCP
# Create an MCP server
mcp = FastMCP("Demo")
# Add an addition tool
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
# Add a dynamic greeting resource
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,12 @@
[package]
name = "calculator-client"
version = "0.1.0"
edition = "2024"
[dependencies]
rmcp = { version = "1.4.0", features = ["client", "transport-child-process"] }
serde_json = "1.0.141"
tokio = { version = "1.46.1", features = ["rt-multi-thread"] }
[dev-dependencies]
slab = "0.4.11"
@@ -0,0 +1,16 @@
# Running this sample
> [!NOTE]
> This Rust client assumes you have dotnet 8.0 SDK installed to run the sample MCP server in the [server](../server/) directory, which is a sibling directory to this project.
## -1- Install the dependencies and build the project
```bash
cargo build
```
## -2- Run the sample
```bash
cargo run
```
@@ -0,0 +1,113 @@
use rmcp::{
RmcpError,
model::CallToolRequestParam,
service::ServiceExt,
transport::{ConfigureCommandExt, TokioChildProcess},
};
use tokio::process::Command;
#[tokio::main]
async fn main() -> Result<(), RmcpError> {
// Assume the server is a sibling project named "server" in the same directory
let workspace_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent();
let server_dir = match workspace_root {
Some(root) => root.join("server"),
None => {
eprintln!("Error: Failed to locate workspace root");
return Ok(()); // or return an appropriate error
}
};
let client = ()
.serve(
TokioChildProcess::new(Command::new("dotnet").configure(|cmd| {
cmd.arg("run").current_dir(server_dir);
}))
.map_err(RmcpError::transport_creation::<TokioChildProcess>)?,
)
.await?;
// Initialize
let server_info = client.peer_info();
match server_info {
Some(info) => {
println!("🔗 Connected to server:");
println!(" Name: {}", info.server_info.name);
println!(" Version: {}", info.server_info.version);
println!(" Protocol: {:?}", info.protocol_version);
if let Some(instructions) = &info.instructions {
println!(" Instructions: {}", instructions);
}
// Display capabilities
println!(" Capabilities:");
if info.capabilities.tools.is_some() {
println!(" • Tools support enabled");
}
if info.capabilities.resources.is_some() {
println!(" • Resources support enabled");
}
if info.capabilities.prompts.is_some() {
println!(" • Prompts support enabled");
}
if info.capabilities.completions.is_some() {
println!(" • Completions support enabled");
}
if info.capabilities.logging.is_some() {
println!(" • Logging support enabled");
}
}
None => {
println!("🔗 Connected to server (no info available)");
}
}
// List tools
let tools = client.list_tools(Default::default()).await?;
println!("📋 Available tools: {}", tools.tools.len());
for tool in &tools.tools {
println!("{} - {}",
tool.name,
tool.description.as_deref().unwrap_or("No description")
);
}
// Call add tool with arguments = {"a": 3, "b": 2}
let a = 3;
let b = 2;
println!("\n🧮 Calculating {} + {}...", a, b);
let tool_result = client
.call_tool(CallToolRequestParam {
name: "add".into(),
arguments: serde_json::json!({ "a": a, "b": b }).as_object().cloned(),
})
.await?;
// Parse and display result
match &tool_result.content.expect("REASON").first() {
Some(content) => {
match &content.raw {
rmcp::model::RawContent::Text(text_content) => {
println!("✅ Result: {}", text_content.text);
}
other => {
println!("✅ Result: {:?}", other);
}
}
}
None => {
println!("⚠️ No result returned from tool");
}
}
if let Some(error) = &tool_result.is_error {
if *error {
println!("❌ Tool reported an error");
}
}
client.cancel().await?;
Ok(())
}
@@ -0,0 +1,25 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ModelContextProtocol.Server;
using System.ComponentModel;
var builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddConsole(consoleLogOptions =>
{
// Configure all logs to go to stderr
consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace;
});
builder.Services
.AddMcpServer()
.WithStdioServerTransport()
.WithToolsFromAssembly();
await builder.Build().RunAsync();
[McpServerToolType]
public static class CalculatorTool
{
[McpServerTool, Description("Adds two numbers")]
public static string Add(int a, int b) => $"Sum {a + b}";
}
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.*" />
<PackageReference Include="ModelContextProtocol" Version="0.*-*" />
</ItemGroup>
</Project>
@@ -0,0 +1,33 @@
# Running this sample
You're recommended to install `uv` but it's not a must, see [instructions](https://docs.astral.sh/uv/#highlights)
## -1- Install the dependencies
```bash
npm install
```
## -3- Run the server
```bash
npm run build
```
## -4- Run the client
```sh
npm run client
```
You should see a result similar to:
```text
Prompt: {
type: 'text',
text: 'Please review this code:\n\nconsole.log("hello");'
}
Resource template: file
Tool result: { content: [ { type: 'text', text: '9' } ] }
```
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,25 @@
{
"type": "module",
"bin": {
"weather": "./build/index.js"
},
"scripts": {
"build": "tsc && node ./build/index.js",
"client": "tsc && node ./build/client.js",
"inspector": "npx @modelcontextprotocol/inspector node build/index.js"
},
"files": [
"build"
],
"dependencies": {
"@modelcontextprotocol/sdk": ">=1.26.0",
"type": "^2.7.3",
"uuid": "^14.0.0",
"zod": "^3.24.2"
},
"devDependencies": {
"@types/node": "^20.11.24",
"@types/uuid": "^10.0.0",
"typescript": "^5.3.3"
}
}
@@ -0,0 +1,52 @@
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
const transport = new StdioClientTransport({
command: "node",
args: ["./build/index.js"]
});
const client = new Client(
{
name: "example-client",
version: "1.0.0"
}
);
await client.connect(transport);
// List prompts
const prompts = await client.listPrompts();
// Get a prompt
const prompt = await client.getPrompt({
name: "review-code",
arguments: {
code: "console.log(\"hello\");"
}
});
console.log("Prompt: ", prompt.messages[0].content);
// List resources
const resources = await client.listResources();
for(let resource in resources.resources) {
console.log("Resource: ", resource);
}
// List resource templates
const templates = await client.listResourceTemplates();
for(let template of templates.resourceTemplates) {
console.log("Resource template: ", template.name);
}
// Call a tool
const result = await client.callTool({
name: "add",
arguments: {
a: 1,
b: 8
}
});
console.log("Tool result: ", result);
@@ -0,0 +1,47 @@
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create an MCP server
const server = new McpServer({
name: "Demo",
version: "1.0.0"
});
// Add an addition tool
server.tool("add",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }]
})
);
// Add a dynamic greeting resource
server.resource(
"file",
new ResourceTemplate("file://{path}", { list: undefined }),
async (uri, { path }) => ({
contents: [{
uri: uri.href,
text: `File, ${path}!`
}]
})
);
server.prompt(
"review-code",
{ code: z.string() },
({ code }) => ({
messages: [{
role: "user",
content: {
type: "text",
text: `Please review this code:\n\n${code}`
}
}]
})
);
// Start receiving messages on stdin and sending messages on stdout
const transport = new StdioServerTransport();
await server.connect(transport);
@@ -0,0 +1,16 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"outDir": "./build",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"noImplicitAny": false
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
Here's the solutions for each runtime:
- [TypeScript](./typescript/README.md)
- [Python](./python/README.md)
- [.NET](./dotnet/README.md)
- [Java](./java/README.md)
- [Rust](./rust/README.md)
@@ -0,0 +1,129 @@
using Azure;
using Azure.AI.Inference;
using ModelContextProtocol.Client;
using ModelContextProtocol.Protocol;
using System.Text.Json;
var endpoint = "https://models.inference.ai.azure.com";
var token = Environment.GetEnvironmentVariable("GITHUB_TOKEN"); // Your GitHub Access Token
if (string.IsNullOrWhiteSpace(token))
{
Console.WriteLine("Please set the GITHUB_TOKEN environment variable to your GitHub Access Token.");
return;
}
var client = new ChatCompletionsClient(new Uri(endpoint), new AzureKeyCredential(token));
var chatHistory = new List<ChatRequestMessage>
{
new ChatRequestSystemMessage("You are a helpful assistant that knows about AI")
};
var clientTransport = new StdioClientTransport(new()
{
Name = "Demo Server",
Command = $"{Path.Combine(AppContext.BaseDirectory, "../../../../../../", "02-client/solution/server/bin/Debug/net9.0/server")}",
Arguments = [],
});
Console.WriteLine("Setting up stdio transport");
await using var mcpClient = await McpClient.CreateAsync(clientTransport);
ChatCompletionsToolDefinition ConvertFrom(string name, string description, JsonElement jsonElement)
{
// convert the tool to a function definition
FunctionDefinition functionDefinition = new(name)
{
Description = description,
Parameters = BinaryData.FromObjectAsJson(new
{
Type = "object",
Properties = jsonElement
},
new JsonSerializerOptions() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase })
};
// create a tool definition
ChatCompletionsToolDefinition toolDefinition = new(functionDefinition);
return toolDefinition;
}
async Task<List<ChatCompletionsToolDefinition>> GetMcpTools()
{
Console.WriteLine("Listing tools");
var tools = await mcpClient.ListToolsAsync();
List<ChatCompletionsToolDefinition> toolDefinitions = [];
foreach (var tool in tools)
{
Console.WriteLine($"Connected to server with tools: {tool.Name}");
Console.WriteLine($"Tool description: {tool.Description}");
Console.WriteLine($"Tool parameters: {tool.JsonSchema}");
tool.JsonSchema.TryGetProperty("properties", out JsonElement propertiesElement);
var def = ConvertFrom(tool.Name, tool.Description, propertiesElement);
Console.WriteLine($"Tool definition: {def}");
toolDefinitions.Add(def);
Console.WriteLine($"Properties: {propertiesElement}");
}
return toolDefinitions;
}
// 1. List tools on mcp server
var tools = await GetMcpTools();
for (int i = 0; i < tools.Count; i++)
{
var tool = tools[i];
Console.WriteLine($"MCP Tools def: {i}: {tool}");
}
// 2. Define the chat history and the user message
var userMessage = "add 2 and 4";
chatHistory.Add(new ChatRequestUserMessage(userMessage));
// 3. Define options, including the tools
var options = new ChatCompletionsOptions(chatHistory)
{
Model = "gpt-4.1-mini",
Tools = { tools[0] }
};
// 4. Call the model
ChatCompletions? response = await client.CompleteAsync(options);
var content = response.Content;
// 5. Check if the response contains a function call
ChatCompletionsToolCall? calls = response.ToolCalls.FirstOrDefault();
for (int i = 0; i < response.ToolCalls.Count; i++)
{
var call = response.ToolCalls[i];
Console.WriteLine($"Tool call {i}: {call.Name} with arguments {call.Arguments}");
//Tool call 0: add with arguments {"a":2,"b":4}
var dict = JsonSerializer.Deserialize<Dictionary<string, object>>(call.Arguments);
var result = await mcpClient.CallToolAsync(
call.Name,
dict!,
cancellationToken: CancellationToken.None
);
var textBlock = result.Content.OfType<TextContentBlock>().FirstOrDefault();
if (textBlock != null)
{
Console.WriteLine(textBlock.Text);
}
}
// 6. Print the generic response
Console.WriteLine($"Assistant response: {content}");
// Console.WriteLine($"Function call: {functionCall?.Name}");
// check if tool call, if so, call the tool
@@ -0,0 +1,45 @@
# Run this sample
> [!NOTE]
> This sample assumes you're using a GitHub Codespaces instance. If you want to run this locally, you need to set up a personal access token (PAT) on GitHub.
>
> ```bash
> # zsh/bash
> export GITHUB_TOKEN="{{YOUR_GITHUB_PAT}}"
> ```
>
> ```powershell
> # PowerShell
> $env:GITHUB_TOKEN = "{{YOUR_GITHUB_PAT}}"
> ```
## Install libraries
```sh
dotnet restore
```
Should install the following libraries: Azure AI Inference, Azure Identity, Microsoft.Extension, Model.Hosting, ModelContextProtcol
## Run
```sh
dotnet run
```
You should see an output similar to:
```text
Setting up stdio transport
Listing tools
Connected to server with tools: Add
Tool description: Adds two numbers
Tool parameters: {"title":"Add","description":"Adds two numbers","type":"object","properties":{"a":{"type":"integer"},"b":{"type":"integer"}},"required":["a","b"]}
Tool definition: Azure.AI.Inference.ChatCompletionsToolDefinition
Properties: {"a":{"type":"integer"},"b":{"type":"integer"}}
MCP Tools def: 0: Azure.AI.Inference.ChatCompletionsToolDefinition
Tool call 0: Add with arguments {"a":2,"b":4}
Sum 6
```
A lot of the output us just debugging but what's important is that you are listing tools from the MCP Server, turn those into LLM tools and you end up with an MCP client response "Sum 6".
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Azure.AI.Inference" Version="1.*-*" />
<PackageReference Include="Azure.Identity" Version="1.*-*" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.*-*" />
<PackageReference Include="ModelContextProtocol" Version="0.*-*" />
</ItemGroup>
</Project>
@@ -0,0 +1,48 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "dotnet", "dotnet.csproj", "{989BF94A-D5A2-448C-B9C4-C85068EE21BB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "server", "..\..\..\02-client\solution\server\server.csproj", "{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Debug|x64.ActiveCfg = Debug|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Debug|x64.Build.0 = Debug|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Debug|x86.ActiveCfg = Debug|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Debug|x86.Build.0 = Debug|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Release|Any CPU.Build.0 = Release|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Release|x64.ActiveCfg = Release|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Release|x64.Build.0 = Release|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Release|x86.ActiveCfg = Release|Any CPU
{989BF94A-D5A2-448C-B9C4-C85068EE21BB}.Release|x86.Build.0 = Release|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Debug|x64.ActiveCfg = Debug|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Debug|x64.Build.0 = Debug|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Debug|x86.ActiveCfg = Debug|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Debug|x86.Build.0 = Debug|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Release|Any CPU.Build.0 = Release|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Release|x64.ActiveCfg = Release|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Release|x64.Build.0 = Release|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Release|x86.ActiveCfg = Release|Any CPU
{D4E3EB9E-B535-4B7A-9B64-4AC156A80C61}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
@@ -0,0 +1,117 @@
/*
* Copyright 2007-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}
@@ -0,0 +1,2 @@
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
@@ -0,0 +1,190 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
Copyright 2025 Spring AI MCP Echo Server Sample
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,137 @@
# Calculator LLM Client
A Java application that demonstrates how to use LangChain4j to connect to an MCP (Model Context Protocol) calculator service with GitHub Models integration.
## Prerequisites
- Java 21 or higher
- Maven 3.6+ (or use the included Maven wrapper)
- A GitHub account with access to GitHub Models
- An MCP calculator service running on `http://localhost:8080`
## Getting the GitHub Token
This application uses GitHub Models which requires a GitHub personal access token. Follow these steps to get your token:
### 1. Access GitHub Models
1. Go to [GitHub Models](https://github.com/marketplace/models)
2. Sign in with your GitHub account
3. Request access to GitHub Models if you haven't already
### 2. Create a Personal Access Token
1. Go to [GitHub Settings → Developer settings → Personal access tokens → Tokens (classic)](https://github.com/settings/tokens)
2. Click "Generate new token" → "Generate new token (classic)"
3. Give your token a descriptive name (e.g., "MCP Calculator Client")
4. Set expiration as needed
5. Select the following scopes:
- `repo` (if accessing private repositories)
- `user:email`
6. Click "Generate token"
7. **Important**: Copy the token immediately - you won't be able to see it again!
### 3. Set the Environment Variable
#### On Windows (Command Prompt):
```cmd
set GITHUB_TOKEN=your_github_token_here
```
#### On Windows (PowerShell):
```powershell
$env:GITHUB_TOKEN="your_github_token_here"
```
#### On macOS/Linux:
```bash
export GITHUB_TOKEN=your_github_token_here
```
## Setup and Installation
1. **Clone or navigate to the project directory**
2. **Install dependencies**:
```cmd
mvnw clean install
```
Or if you have Maven installed globally:
```cmd
mvn clean install
```
3. **Set up the environment variable** (see "Getting the GitHub Token" section above)
4. **Start the MCP Calculator Service**:
Make sure you have chapter 1's MCP calculator service running on `http://localhost:8080/sse`. This should be running before you start the client.
## Running the Application
```cmd
mvnw clean package
java -jar target\calculator-llm-client-0.0.1-SNAPSHOT.jar
```
## What the Application Does
The application demonstrates three main interactions with the calculator service:
1. **Addition**: Calculates the sum of 24.5 and 17.3
2. **Square Root**: Calculates the square root of 144
3. **Help**: Shows available calculator functions
## Expected Output
When running successfully, you should see output similar to:
```
The sum of 24.5 and 17.3 is 41.8.
The square root of 144 is 12.
The calculator service provides the following functions: add, subtract, multiply, divide, sqrt, power...
```
## Troubleshooting
### Common Issues
1. **"GITHUB_TOKEN environment variable not set"**
- Make sure you've set the `GITHUB_TOKEN` environment variable
- Restart your terminal/command prompt after setting the variable
2. **"Connection refused to localhost:8080"**
- Ensure the MCP calculator service is running on port 8080
- Check if another service is using port 8080
3. **"Authentication failed"**
- Verify your GitHub token is valid and has the correct permissions
- Check if you have access to GitHub Models
4. **Maven build errors**
- Ensure you're using Java 21 or higher: `java -version`
- Try cleaning the build: `mvnw clean`
### Debugging
To enable debug logging, add the following JVM argument when running:
```cmd
java -Dlogging.level.dev.langchain4j=DEBUG -jar target\calculator-llm-client-0.0.1-SNAPSHOT.jar
```
## Configuration
The application is configured to:
- Use GitHub Models with the `gpt-4.1-nano` model
- Connect to MCP service at `http://localhost:8080/sse`
- Use a 60-second timeout for requests
- Enable request/response logging for debugging
## Dependencies
Key dependencies used in this project:
- **LangChain4j**: For AI integration and tool management
- **LangChain4j MCP**: For Model Context Protocol support
- **LangChain4j GitHub Models**: For GitHub Models integration
- **Spring Boot**: For application framework and dependency injection
## License
This project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.
+310
View File
@@ -0,0 +1,310 @@
#!/bin/sh
# ----------------------------------------------------------------------------
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Maven Start Up Batch script
#
# Required ENV vars:
# ------------------
# JAVA_HOME - location of a JDK home dir
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
# ----------------------------------------------------------------------------
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
if [ -f "$HOME/.mavenrc" ] ; then
. "$HOME/.mavenrc"
fi
fi
# OS specific support. $var _must_ be set to either true or false.
cygwin=false;
darwin=false;
mingw=false
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
export JAVA_HOME="`/usr/libexec/java_home`"
else
export JAVA_HOME="/Library/Java/Home"
fi
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
fi
fi
if [ -z "$JAVACMD" ] ; then
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="`which java`"
fi
fi
if [ ! -x "$JAVACMD" ] ; then
echo "Error: JAVA_HOME is not defined correctly." >&2
echo " We cannot execute $JAVACMD" >&2
exit 1
fi
if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=`cd "$wdir/.."; pwd`
fi
# end of workaround
done
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
BASE_DIR=`find_maven_basedir "$(pwd)"`
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found .mvn/wrapper/maven-wrapper.jar"
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
fi
if [ -n "$MVNW_REPOURL" ]; then
jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
else
jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
fi
while IFS="=" read key value; do
case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
esac
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
if [ "$MVNW_VERBOSE" = true ]; then
echo "Downloading from: $jarUrl"
fi
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
if $cygwin; then
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
fi
if command -v wget > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found wget ... using wget"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget "$jarUrl" -O "$wrapperJarPath"
else
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
if [ "$MVNW_VERBOSE" = true ]; then
echo "Found curl ... using curl"
fi
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl -o "$wrapperJarPath" "$jarUrl" -f
else
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
fi
else
if [ "$MVNW_VERBOSE" = true ]; then
echo "Falling back to using Java to download"
fi
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaClass=`cygpath --path --windows "$javaClass"`
fi
if [ -e "$javaClass" ]; then
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Compiling MavenWrapperDownloader.java ..."
fi
# Compiling the Java class
("$JAVA_HOME/bin/javac" "$javaClass")
fi
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
# Running the downloader
if [ "$MVNW_VERBOSE" = true ]; then
echo " - Running MavenWrapperDownloader.java ..."
fi
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
if [ "$MVNW_VERBOSE" = true ]; then
echo $MAVEN_PROJECTBASEDIR
fi
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
exec "$JAVACMD" \
$MAVEN_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
+182
View File
@@ -0,0 +1,182 @@
@REM ----------------------------------------------------------------------------
@REM Licensed to the Apache Software Foundation (ASF) under one
@REM or more contributor license agreements. See the NOTICE file
@REM distributed with this work for additional information
@REM regarding copyright ownership. The ASF licenses this file
@REM to you under the Apache License, Version 2.0 (the
@REM "License"); you may not use this file except in compliance
@REM with the License. You may obtain a copy of the License at
@REM
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
@REM KIND, either express or implied. See the License for the
@REM specific language governing permissions and limitations
@REM under the License.
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Maven Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
@REM ----------------------------------------------------------------------------
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
set ERROR_CODE=0
@REM To isolate internal variables from possible post scripts, we use another setlocal
@setlocal
@REM ==== START VALIDATION ====
if not "%JAVA_HOME%" == "" goto OkJHome
echo.
echo Error: JAVA_HOME not found in your environment. >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
:OkJHome
if exist "%JAVA_HOME%\bin\java.exe" goto init
echo.
echo Error: JAVA_HOME is set to an invalid directory. >&2
echo JAVA_HOME = "%JAVA_HOME%" >&2
echo Please set the JAVA_HOME variable in your environment to match the >&2
echo location of your Java installation. >&2
echo.
goto error
@REM ==== END VALIDATION ====
:init
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
set EXEC_DIR=%CD%
set WDIR=%EXEC_DIR%
:findBaseDir
IF EXIST "%WDIR%"\.mvn goto baseDirFound
cd ..
IF "%WDIR%"=="%CD%" goto baseDirNotFound
set WDIR=%CD%
goto findBaseDir
:baseDirFound
set MAVEN_PROJECTBASEDIR=%WDIR%
cd "%EXEC_DIR%"
goto endDetectBaseDir
:baseDirNotFound
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
cd "%EXEC_DIR%"
:endDetectBaseDir
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
@setlocal EnableExtensions EnableDelayedExpansion
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %DOWNLOAD_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
if ERRORLEVEL 1 goto error
goto end
:error
set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
exit /B %ERROR_CODE%
@@ -0,0 +1,143 @@
<?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>3.4.4</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>calculator-llm-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Calculator llm client</name>
<description>Basic calculator llm client for beginners</description>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
<distribution>repo</distribution>
</license>
</licenses>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-mcp</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai-official</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-github-models</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- JUnit dependencies for testing -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.10.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<release>21</release>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<java.version>21</java.version>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<langchain4j.version>1.0.0-beta3</langchain4j.version>
</properties>
<repositories>
<repository>
<name>Central Portal Snapshots</name>
<id>central-portal-snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
</project>

Some files were not shown because too many files have changed in this diff Show More