Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<dependency>
<groupId>io.modelcontextprotocol.sdk</groupId>
<artifactId>mcp</artifactId>
<version>0.7.0</version>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
Expand Down
14 changes: 11 additions & 3 deletions src/main/java/net/experimentalworks/App.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
package net.experimentalworks;

import io.modelcontextprotocol.server.transport.StdioServerTransport;
import io.modelcontextprotocol.json.McpJsonMapper;
import io.modelcontextprotocol.json.McpJsonMapperSupplier;
import io.modelcontextprotocol.server.transport.StdioServerTransportProvider;
import java.util.ServiceLoader;

/** Hello world! */
public class App {

public static void main(String[] args) {
var server = new SteamGamesServer(new StdioServerTransport());
McpJsonMapper jsonMapper =
ServiceLoader.load(McpJsonMapperSupplier.class)
.findFirst()
.orElseThrow(() -> new IllegalStateException("No McpJsonMapperSupplier found on classpath"))
.get();
var transportProvider = new StdioServerTransportProvider(jsonMapper);
var server = new SteamGamesServer(transportProvider);
server.run().block();
}
}
149 changes: 70 additions & 79 deletions src/main/java/net/experimentalworks/SteamGamesServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,21 @@
import io.modelcontextprotocol.server.McpAsyncServer;
import io.modelcontextprotocol.server.McpServer;
import io.modelcontextprotocol.server.McpServerFeatures;
import io.modelcontextprotocol.spec.McpSchema;
import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
import io.modelcontextprotocol.spec.McpSchema.TextContent;
import io.modelcontextprotocol.spec.McpSchema.Tool;
import io.modelcontextprotocol.spec.ServerMcpTransport;
import io.modelcontextprotocol.spec.McpServerTransportProvider;
import reactor.core.publisher.Mono;

public class SteamGamesServer {

private static final String STEAM_API_KEY = System.getenv("STEAM_API_KEY");
private static final String STEAM_ID = System.getenv("STEAM_ID");
private static final String TOOL_PREFIX = SteamGamesServer.getenvOrDefault("TOOL_PREFIX", "");
private static final String TOOL_PREFIX = getenvOrDefault("TOOL_PREFIX", "");

private static final McpSchema.JsonSchema EMPTY_SCHEMA =
new McpSchema.JsonSchema("object", Map.of(), null, null, null, null);

private final McpAsyncServer server;

Expand All @@ -29,13 +32,13 @@ private static String getenvOrDefault(String key, String defaultValue) {
return value != null ? value : defaultValue;
}

public SteamGamesServer(ServerMcpTransport transport) {
public SteamGamesServer(McpServerTransportProvider transportProvider) {
String version = getClass().getPackage().getImplementationVersion();
if (version == null) {
version = "1.0.0"; // Fallback version if not found
version = "1.0.0";
}
this.server =
McpServer.async(transport)
McpServer.async(transportProvider)
.serverInfo("steam-games", version)
.capabilities(ServerCapabilities.builder().tools(true).logging().build())
.build();
Expand All @@ -48,84 +51,72 @@ public Mono<Void> run() {
.then(Mono.never());
}

private static McpServerFeatures.AsyncToolRegistration createGetGamesTool() {
var schema =
"""
{
"type": "object",
"properties": {}
}
""";

private McpServerFeatures.AsyncToolSpecification createGetGamesTool() {
var tool =
new Tool(
TOOL_PREFIX + "get-games",
"""
Get a comprehensive list of all games owned by the specified Steam user, including their total playtime in minutes.
This includes all games in their Steam library, both installed and uninstalled, free and purchased. For each game,
returns details like the game name, AppID, total playtime, and whether they've played it recently. The data comes
directly from Steam's official API using the provided Steam ID.
NOTE: playtime is sent in minutes.
""",
schema);

return new McpServerFeatures.AsyncToolRegistration(tool, args -> handleGetGames(args));
}

private static Mono<CallToolResult> handleGetGames(Map<String, Object> args) {
return Mono.fromCallable(
() -> {
var steamGames = new SteamGames(STEAM_API_KEY);
var games = steamGames.getGames(STEAM_ID);

var json =
new JSONObject()
.put("owner", STEAM_ID)
.put("description", "Played games by the given steam id")
.put("all_games", new JSONArray(games));
Tool.builder()
.name(TOOL_PREFIX + "get-games")
.description(
"""
Get a comprehensive list of all games owned by the specified Steam user, including their total playtime in minutes.
This includes all games in their Steam library, both installed and uninstalled, free and purchased. For each game,
returns details like the game name, AppID, total playtime, and whether they've played it recently. The data comes
directly from Steam's official API using the provided Steam ID.
NOTE: playtime is sent in minutes.
""")
.inputSchema(EMPTY_SCHEMA)
.build();

return new CallToolResult(List.of(new TextContent(json.toString())), false);
});
return new McpServerFeatures.AsyncToolSpecification(
tool,
(exchange, request) ->
Mono.fromCallable(
() -> {
var steamGames = new SteamGames(STEAM_API_KEY);
var games = steamGames.getGames(STEAM_ID);
var json =
new JSONObject()
.put("owner", STEAM_ID)
.put("description", "Played games by the given steam id")
.put("all_games", new JSONArray(games));
return CallToolResult.builder()
.content(List.of(new McpSchema.TextContent(json.toString())))
.isError(false)
.build();
}));
}

private static McpServerFeatures.AsyncToolRegistration createGetRecentGamesTool() {
var schema =
"""
{
"type": "object",
"properties": {}
}
""";

private McpServerFeatures.AsyncToolSpecification createGetRecentGamesTool() {
var tool =
new Tool(
TOOL_PREFIX + "get-recent-games",
"""
Retrieve a list of recently played games for the specified Steam user, including playtime
details from the last 2 weeks. This tool fetches data directly from Steam's API using the
provided Steam ID and returns information like game names, AppIDs, and recent playtime
statistics in minutes. The results only include games that have been played in the recent time period,
making it useful for tracking current gaming activity and habits.
NOTE: playtime is sent in minutes.
""",
schema);

return new McpServerFeatures.AsyncToolRegistration(tool, args -> handleGetRecentGames(args));
}

private static Mono<CallToolResult> handleGetRecentGames(Map<String, Object> args) {
return Mono.fromCallable(
() -> {
var steamGames = new SteamGames(STEAM_API_KEY);
var games = steamGames.getRecentlyGames(STEAM_ID);

var json =
new JSONObject()
.put("owner", STEAM_ID)
.put("description", "Recently played games by the given steam id")
.put("recent_games", new JSONArray(games));
Tool.builder()
.name(TOOL_PREFIX + "get-recent-games")
.description(
"""
Retrieve a list of recently played games for the specified Steam user, including playtime
details from the last 2 weeks. This tool fetches data directly from Steam's API using the
provided Steam ID and returns information like game names, AppIDs, and recent playtime
statistics in minutes. The results only include games that have been played in the recent time period,
making it useful for tracking current gaming activity and habits.
NOTE: playtime is sent in minutes.
""")
.inputSchema(EMPTY_SCHEMA)
.build();

return new CallToolResult(List.of(new TextContent(json.toString())), false);
});
return new McpServerFeatures.AsyncToolSpecification(
tool,
(exchange, request) ->
Mono.fromCallable(
() -> {
var steamGames = new SteamGames(STEAM_API_KEY);
var games = steamGames.getRecentlyGames(STEAM_ID);
var json =
new JSONObject()
.put("owner", STEAM_ID)
.put("description", "Recently played games by the given steam id")
.put("recent_games", new JSONArray(games));
return CallToolResult.builder()
.content(List.of(new McpSchema.TextContent(json.toString())))
.isError(false)
.build();
}));
}
}