Add a weather server example using the MCP Ruby SDK and the National Weather Service API, following the same pattern as other language examples. https://github.com/modelcontextprotocol/ruby-sdk
127 lines
3.2 KiB
Ruby
127 lines
3.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "json"
|
|
require "mcp"
|
|
require "net/http"
|
|
require "uri"
|
|
|
|
NWS_API_BASE = "https://api.weather.gov"
|
|
USER_AGENT = "weather-app/1.0"
|
|
|
|
module HelperMethods
|
|
def make_nws_request(url)
|
|
uri = URI(url)
|
|
request = Net::HTTP::Get.new(uri)
|
|
request["User-Agent"] = USER_AGENT
|
|
request["Accept"] = "application/geo+json"
|
|
|
|
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
|
|
http.request(request)
|
|
end
|
|
|
|
raise "HTTP #{response.code}: #{response.message}" unless response.is_a?(Net::HTTPSuccess)
|
|
|
|
JSON.parse(response.body)
|
|
end
|
|
|
|
def format_alert(feature)
|
|
properties = feature["properties"]
|
|
|
|
<<~ALERT
|
|
Event: #{properties["event"] || "Unknown"}
|
|
Area: #{properties["areaDesc"] || "Unknown"}
|
|
Severity: #{properties["severity"] || "Unknown"}
|
|
Description: #{properties["description"] || "No description available"}
|
|
Instructions: #{properties["instruction"] || "No specific instructions provided"}
|
|
ALERT
|
|
end
|
|
end
|
|
|
|
class GetAlerts < MCP::Tool
|
|
extend HelperMethods
|
|
|
|
tool_name "get_alerts"
|
|
description "Get weather alerts for a US state"
|
|
input_schema(
|
|
properties: {
|
|
state: {
|
|
type: "string",
|
|
description: "Two-letter US state code (e.g. CA, NY)"
|
|
}
|
|
},
|
|
required: ["state"]
|
|
)
|
|
|
|
def self.call(state:)
|
|
url = "#{NWS_API_BASE}/alerts/active/area/#{state.upcase}"
|
|
data = make_nws_request(url)
|
|
|
|
if data["features"].empty?
|
|
return MCP::Tool::Response.new([{
|
|
type: "text",
|
|
text: "No active alerts for this state."
|
|
}])
|
|
end
|
|
|
|
alerts = data["features"].map { |feature| format_alert(feature) }
|
|
MCP::Tool::Response.new([{
|
|
type: "text",
|
|
text: alerts.join("\n---\n")
|
|
}])
|
|
end
|
|
end
|
|
|
|
class GetForecast < MCP::Tool
|
|
extend HelperMethods
|
|
|
|
tool_name "get_forecast"
|
|
description "Get weather forecast for a location"
|
|
input_schema(
|
|
properties: {
|
|
latitude: {
|
|
type: "number",
|
|
description: "Latitude of the location"
|
|
},
|
|
longitude: {
|
|
type: "number",
|
|
description: "Longitude of the location"
|
|
}
|
|
},
|
|
required: ["latitude", "longitude"]
|
|
)
|
|
|
|
def self.call(latitude:, longitude:)
|
|
# First get the forecast grid endpoint.
|
|
points_url = "#{NWS_API_BASE}/points/#{latitude},#{longitude}"
|
|
points_data = make_nws_request(points_url)
|
|
|
|
# Get the forecast URL from the points response.
|
|
forecast_url = points_data["properties"]["forecast"]
|
|
forecast_data = make_nws_request(forecast_url)
|
|
|
|
# Format the periods into a readable forecast.
|
|
periods = forecast_data["properties"]["periods"]
|
|
forecasts = periods.first(5).map do |period|
|
|
<<~FORECAST
|
|
#{period["name"]}:
|
|
Temperature: #{period["temperature"]}°#{period["temperatureUnit"]}
|
|
Wind: #{period["windSpeed"]} #{period["windDirection"]}
|
|
Forecast: #{period["detailedForecast"]}
|
|
FORECAST
|
|
end
|
|
|
|
MCP::Tool::Response.new([{
|
|
type: "text",
|
|
text: forecasts.join("\n---\n")
|
|
}])
|
|
end
|
|
end
|
|
|
|
server = MCP::Server.new(
|
|
name: "weather",
|
|
version: "1.0.0",
|
|
tools: [GetAlerts, GetForecast]
|
|
)
|
|
|
|
transport = MCP::Server::Transports::StdioTransport.new(server)
|
|
transport.open
|