使用说明
Web Search MCP Server
A secure Spring Boot MCP (Model Context Protocol) server that provides web search functionality to AI models. Designed for deployment on Cloud Foundry with OAuth2/JWT authentication via SSO service binding.
Features
- MCP Protocol Support: Streamable HTTP transport for real-time communication
- Multiple Search Providers: Brave Search API, SerpAPI, or Google Custom Search
- Cloud Foundry SSO Integration: Auto-configures OAuth2 from p-identity/UAA service bindings
- Security: JWT authentication, rate limiting, and secure credential management
- CredHub bindings: Resolves an exact
WEBSEARCH_API_KEYcredential from Cloud FoundryVCAP_SERVICES, while explicit environment configuration wins - Cloud Foundry Ready: Health checks, VCAP_SERVICES support, Java 21
MCP Tools
| Tool | Description |
|---|---|
web_search |
Full web search with configurable result count (returns formatted text) |
web_search_json |
Web search returning structured JSON for programmatic processing |
quick_search |
Fast search returning top 3 results |
Prerequisites
- Java 21+
- Maven 3.8+
- A web search API key (Brave Search, SerpAPI, or Google Custom Search)
- Cloud Foundry CLI (for deployment)
- Cloud Foundry SSO service (p-identity, UAA, etc.)
Quick Start
Local Development (No Auth)
SPRING_PROFILES_ACTIVE=local \
WEBSEARCH_API_KEY=your-api-key \
./mvnw spring-boot:run
The server will be available at http://localhost:8080
Test Locally
# Health check
curl http://localhost:8080/actuator/health
# MCP endpoint - Initialize (no auth in local mode)
curl -X POST http://localhost:8080/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "test-client", "version": "1.0"}
}
}'
Cloud Foundry Deployment
1. Create/Bind SSO Service
# Create SSO service (if not exists)
cf create-service p-identity uaa my-sso
# Or use existing SSO service
cf services # List available services
2. Deploy the Application
# Build
./mvnw clean package
# Push without starting
cf push --no-start
# Set API key securely (not in manifest)
cf set-env web-search-mcp WEBSEARCH_API_KEY "your-api-key"
# Start the app
cf start web-search-mcp
Recommended: bind the API key through CredHub
On foundations with the CredHub service broker, store one typed field in a dedicated service instance. Load the variable from your approved secret store without printing it:
set +x
cf create-service credhub default web-search-brave-secret \
-c "$(jq -nc --arg key "${BRAVE_API_KEY}" \
'{WEBSEARCH_API_KEY:$key}')"
unset BRAVE_API_KEY
cf bind-service web-search-mcp web-search-brave-secret
cf restage web-search-mcp
The application accepts only the exact WEBSEARCH_API_KEY field. It does not
scan for generic api_key values, does not log the value, and explicit
WEBSEARCH_API_KEY environment configuration takes precedence. The CredHub
service payload has this shape:
{"WEBSEARCH_API_KEY":"your-Brave-key"}
Do not store another credhub-ref inside the CredHub broker payload. Binding
references are interpolated once; nested references are not recursively
resolved.
For a gateway-fronted deployment, map only an internal route to this backend and let the managed MCP Gateway provide the external boundary. Do not expose an unauthenticated backend on a public route.
3. Update manifest.yml
Edit manifest.yml to bind your SSO service:
applications:
- name: web-search-mcp
services:
- my-sso # Your SSO service instance name
Authentication
The MCP endpoints require a valid JWT token from your Cloud Foundry SSO.
Disabling auth (demos / gateway-fronted deployments)
On the cloud profile, /mcp is JWT-protected by default. Set
WEBSEARCH_SECURITY_ENABLED=false to leave /mcp open — useful for quick
demos, or when an MCP gateway in front of this server brokers authentication.
The default is true (secured); when disabled the app logs a startup warning.
cf set-env web-search-mcp WEBSEARCH_SECURITY_ENABLED false
cf restart web-search-mcp
Getting a Token
Using CF CLI (easiest):
TOKEN=$(cf oauth-token | sed 's/bearer //')
echo $TOKEN
Using curl with UAA:
# Client credentials flow (if configured)
curl -X POST "https://uaa.sys.your-domain.com/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET"
Creating a Long-Lived Token (Service Account)
For automated integrations or service accounts, you can create a UAA client with a long-lived token (e.g., 10 years):
Prerequisites:
- Ruby installed (
ruby --version) - UAA admin client secret (from Ops Manager > TAS tile > Credentials > UAA Admin Client)
Step 1: Install the UAA CLI
gem install cf-uaac
Step 2: Target and authenticate to UAA
# Target your UAA
uaac target https://uaa.sys.your-domain.com --skip-ssl-validation
# Login with admin client (get secret from Ops Manager)
uaac token client get admin -s <ADMIN_CLIENT_SECRET>
Step 3: Create a long-lived client
# Create client with 10-year token validity (315360000 seconds)
uaac client add mcp-service-account \
--name mcp-service-account \
--secret "your-secure-secret-here" \
--authorized_grant_types client_credentials \
--authorities openid,uaa.resource \
--access_token_validity 315360000
Step 4: Get a token with your new client
# Authenticate as the new client
uaac token client get mcp-service-account -s "your-secure-secret-here"
# View the token
uaac context
# Or get just the token value
TOKEN=$(uaac context | grep access_token | awk '{print $2}')
echo $TOKEN
Alternative: Get token via curl
TOKEN=$(curl -sk -X POST "https://uaa.sys.your-domain.com/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=mcp-service-account&client_secret=your-secure-secret-here" \
| jq -r '.access_token')
echo $TOKEN
Making Authenticated Requests
TOKEN=$(cf oauth-token | sed 's/bearer //')
# Initialize MCP session
curl -X POST "https://your-app.apps.your-domain.com/mcp" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "my-client", "version": "1.0"}
}
}'
Using the MCP Server
With Streamable HTTP, all MCP communication happens via a single POST /mcp endpoint. No separate SSE connection is needed — the server uses session IDs returned in the Mcp-Session-Id response header to maintain state.
1. Initialize Session
TOKEN=$(cf oauth-token | sed 's/bearer //')
curl -X POST "https://your-app.apps.your-domain.com/mcp" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {"name": "my-client", "version": "1.0"}
}
}'
Save the Mcp-Session-Id header from the response for subsequent requests.
2. List Available Tools
curl -X POST "https://your-app.apps.your-domain.com/mcp" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: <session-id-from-init>" \
-d '{"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}'
3. Call a Tool
curl -X POST "https://your-app.apps.your-domain.com/mcp" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: <session-id-from-init>" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "web_search",
"arguments": {
"query": "Spring Boot MCP server",
"maxResults": 5
}
}
}'
Configuration
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
WEBSEARCH_API_KEY |
Yes | - | API key for search provider |
WEBSEARCH_PROVIDER |
No | BRAVE |
BRAVE, SERPAPI, or GOOGLE_CUSTOM_SEARCH |
WEBSEARCH_MAX_RESPONSE_BYTES |
No | 4194304 |
Maximum buffered provider response, from 262144 through 16777216 bytes |
SPRING_PROFILES_ACTIVE |
No | - | Set to cloud for CF, local for dev |
The response limit applies to the outbound Spring WebFlux client, not the MCP response sent to a caller. The 4 MiB default accommodates normal Brave result payloads that exceed Spring's 256 KiB codec default while retaining a bounded memory ceiling. Raise it only when a measured provider response requires it.
Search Provider Setup
Brave Search API (Recommended)
- Sign up at Brave Search API
- Get your API key from the dashboard
- Set
WEBSEARCH_API_KEYandWEBSEARCH_PROVIDER=BRAVE
SerpAPI
- Sign up at SerpAPI
- Set
WEBSEARCH_API_KEYandWEBSEARCH_PROVIDER=SERPAPI
Google Custom Search
- Create a Custom Search Engine
- Set
WEBSEARCH_API_KEY=apiKey:searchEngineId(colon-separated) - Set
WEBSEARCH_PROVIDER=GOOGLE_CUSTOM_SEARCH
Security Features
OAuth2 JWT Authentication
- Automatically configures from Cloud Foundry SSO service binding
- Supports p-identity, UAA, and other OAuth2 providers
- JWK endpoint auto-discovered from
auth_domain
Rate Limiting
- Default: 60 requests per minute per client
- Configurable via
websearch.rate-limit-per-minute
Health Endpoints (Unauthenticated)
/actuator/health- Overall health/actuator/health/liveness- Kubernetes/CF liveness probe/actuator/health/readiness- Kubernetes/CF readiness probe
Architecture
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ AI Model │────▶│ MCP Server │────▶│ Search API │
│ (Client) │◀────│ (Spring Boot) │◀────│ (Brave/etc) │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │
│ HTTP + JWT │ VCAP_SERVICES
▼ ▼
MCP Protocol CF SSO Service
(Streamable HTTP)
(p-identity/UAA)
Endpoints
| Endpoint | Auth | Description |
|---|---|---|
POST /mcp |
Yes | Streamable HTTP MCP endpoint (all JSON-RPC messages) |
GET /mcp |
Yes | SSE stream for server-initiated notifications |
DELETE /mcp |
Yes | Terminate MCP session |
GET /actuator/health |
No | Health check |
GET /actuator/health/liveness |
No | Liveness probe |
GET /actuator/health/readiness |
No | Readiness probe |
Tool Implementation
Tools are defined using the @McpTool annotation:
@Component
public class WebSearchTools {
@McpTool(name = "web_search", description = "Search the web for information")
public String webSearch(
@McpToolParam(description = "The search query", required = true) String query,
@McpToolParam(description = "Max results", required = false) Integer maxResults
) {
// Implementation
}
}
References
- Spring AI MCP Documentation
- MCP Annotations Examples
- Model Context Protocol Specification
- Brave Search API
- SerpAPI
- Cloud Foundry SSO
License
MIT