An MCP (Model Context Protocol) server that exposes tools for retrieving web resources over HTTP and for reading the current date-time.
fetch— fetch one or more URLs in a single call. By default each HTML body is converted to Markdown; setmarkdowntofalseto receive the raw response body instead. URLs are fetched concurrently and the results are returned together, one entry per URL.current_datetime— return the current date-time as an ISO-8601 string with an offset. Pass an IANAtimezone(e.g.Asia/Tokyo) to convert it; when omitted, the server's default timezone is used.
Use this server when an MCP client (Claude Desktop, custom agents, etc.) needs
to read web pages or call HTTP APIs as part of its workflow. The server speaks
the MCP Streamable HTTP transport on POST /mcp.
Built with Spring Boot 4 + Spring AI 2.0. Supports GraalVM native image.
- JDK 25 (GraalVM CE 25 if you want to build a native image)
- Maven Wrapper bundled in the repository (
./mvnw)
./mvnw spring-boot:run./mvnw -DskipTests native:compile
./target/fetch-serverThe server listens on port 8090 by default (override with PORT).
| name | type | required | default |
|---|---|---|---|
urls |
string array | yes | — |
markdown |
boolean | no | true |
headers |
object (string→string) | no | none |
timeoutSeconds |
integer | no | 30 |
maxBytes |
integer | no | 1 MB |
headers, timeoutSeconds, and maxBytes apply to every URL in the call.
The tool returns a results array with one entry per requested URL, in the same
order as urls:
| field | type | description |
|---|---|---|
url |
string | the requested URL |
status |
integer | HTTP status code, or 0 when the request failed |
contentType |
string | response Content-Type (empty when absent or on failure) |
title |
string | HTML document title; null when markdown is false |
content |
string | Markdown when markdown is true, otherwise the raw body |
truncated |
boolean | true when the body exceeded maxBytes |
error |
string | failure message when this URL could not be fetched, else null |
A failure of one URL does not abort the others; only that entry carries an
error.
| name | type | required | default |
|---|---|---|---|
timezone |
string | no | server's default |
timezone is an IANA zone ID such as Asia/Tokyo or America/New_York. An
unknown value results in an error response.
| field | type | description |
|---|---|---|
dateTime |
string | current date-time, ISO-8601 with offset (e.g. 2026-05-29T14:30:00+09:00) |
The MCP Streamable HTTP transport requires a handshake before any tool call:
POST /mcpwith methodinitialize. The response carries anMcp-Session-Idheader that must be sent on every subsequent request.POST /mcpwith methodnotifications/initializedto confirm the session.POST /mcpwith methodtools/listortools/call.
The examples below assume the server is running on http://localhost:8090.
curl -i -X POST http://localhost:8090/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": "curl", "version": "1"}
}
}'Look for the response header Mcp-Session-Id: <uuid> and reuse the UUID below.
SESSION_ID=<paste-uuid-here>curl -X POST http://localhost:8090/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json,text/event-stream" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc": "2.0", "method": "notifications/initialized"}'This returns HTTP 202 with no body.
curl -X POST http://localhost:8090/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json,text/event-stream" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc": "2.0", "id": 2, "method": "tools/list"}'curl -X POST http://localhost:8090/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json,text/event-stream" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "fetch",
"arguments": {"urls": ["https://example.com"]}
}
}'Pass several URLs in urls, and set markdown to false to skip the Markdown
conversion. The response results array preserves the input order.
curl -X POST http://localhost:8090/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json,text/event-stream" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "fetch",
"arguments": {
"urls": ["https://example.com", "https://example.org"],
"markdown": false,
"maxBytes": 200
}
}
}'curl -X POST http://localhost:8090/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json,text/event-stream" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "fetch",
"arguments": {
"urls": ["https://httpbin.org/headers"],
"headers": {"User-Agent": "fetch-server/0.0.1", "X-Trace-Id": "demo"},
"timeoutSeconds": 5
}
}
}'Omit arguments (or pass an empty object) to use the server's default timezone,
or pass a timezone to convert it.
curl -X POST http://localhost:8090/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json,text/event-stream" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "current_datetime",
"arguments": {"timezone": "Asia/Tokyo"}
}
}'The full handshake plus a tool call as a single script:
#!/usr/bin/env bash
set -euo pipefail
BASE=http://localhost:8090/mcp
INIT=$(curl -s -i -X POST "$BASE" \
-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":"curl","version":"1"}}}')
SESSION_ID=$(printf '%s' "$INIT" | awk -F': ' 'tolower($1)=="mcp-session-id"{print $2}' | tr -d '\r\n')
curl -s -o /dev/null -X POST "$BASE" \
-H "Content-Type: application/json" \
-H "Accept: application/json,text/event-stream" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","method":"notifications/initialized"}'
curl -s -X POST "$BASE" \
-H "Content-Type: application/json" \
-H "Accept: application/json,text/event-stream" \
-H "Mcp-Session-Id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"fetch","arguments":{"urls":["https://example.com"]}}}'./mvnw testThe unit tests spin up an in-process com.sun.net.httpserver.HttpServer and
exercise the tools end-to-end, including header propagation, timeout,
truncation, and charset decoding.