#
tokens: 7313/50000 14/14 files
lines: on (toggle) GitHub
raw markdown copy reset
# Directory Structure

```
├── .github
│   ├── actions
│   │   └── setup-java
│   │       └── action.yml
│   └── workflows
│       ├── default.yml
│       └── publish.yml
├── .gitignore
├── dependency-reduced-pom.xml
├── devenv.lock
├── devenv.nix
├── devenv.yaml
├── Dockerfile
├── LICENSE
├── pom.xml
├── pom.xml.versionsBackup
├── README.md
└── src
    ├── main
    │   └── java
    │       └── net
    │           └── experimentalworks
    │               ├── App.java
    │               ├── Game.java
    │               ├── SteamGames.java
    │               └── SteamGamesServer.java
    └── test
        └── java
            └── net
                └── experimentalworks
                    └── AppTest.java
```

# Files

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
 1 | # Compiled class file
 2 | *.class
 3 | 
 4 | # Log file
 5 | *.log
 6 | 
 7 | # BlueJ files
 8 | *.ctxt
 9 | 
10 | # Mobile Tools for Java (J2ME)
11 | .mtj.tmp/
12 | 
13 | # Package Files #
14 | *.jar
15 | *.war
16 | *.nar
17 | *.ear
18 | *.zip
19 | *.tar.gz
20 | *.rar
21 | 
22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
23 | hs_err_pid*
24 | replay_pid*
25 | 
26 | # Devenv and Maven
27 | target/
28 | 
29 | *.swp
30 | .devenv.flake.nix
31 | .devenv/
32 | 
```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
 1 | # MCP Steam Server
 2 | 
 3 | A Model Context Protocol (MCP) server that provides Steam gaming context to AI assistants. This service integrates with the Steam API to fetch user gaming information and exposes it through the MCP protocol, allowing AI assistants to access and understand users' gaming activities and preferences.
 4 | 
 5 | ## Installation
 6 | 
 7 | ### Using Docker (Recommended)
 8 | 
 9 | The easiest way to run the MCP Steam server is using Docker:
10 | 
11 | ```bash
12 | docker run --rm -i ghcr.io/dsp/mcp-server-steam:latest
13 | ```
14 | 
15 | ### Configuration
16 | 
17 | The server can be configured using environment variables:
18 | 
19 | ```bash
20 | # Required configuration
21 | STEAM_API_KEY=your_steam_api_key
22 | ```
23 | 
24 | ## Development
25 | 
26 | ### Prerequisites
27 | 
28 | - OpenJDK 21
29 | - Docker (for container builds)
30 | - Git
31 | - [devenv.sh](https://devenv.sh)
32 | 
33 | ### Setting Up Development Environment
34 | 
35 | 1. Clone the repository:
36 |    ```bash
37 |    git clone https://github.com/dsp/mcp-steam.git
38 |    cd mcp-steam
39 |    ```
40 | 
41 | 2. Use the development shell:
42 |    ```bash
43 |    devshell shell
44 |    ```
45 |    This will set up the required development environment with all necessary dependencies.
46 | 
47 | 3. Build the project:
48 |    ```bash
49 |    mvn package
50 |    ```
51 | 
52 | ### Building Docker Image Locally
53 | 
54 | ```bash
55 | docker build -t mcp-server-steam .
56 | ```
57 | 
58 | ## API Documentation
59 | 
60 | The server implements the Model Context Protocol (MCP) specification. For detailed API documentation, please refer to the [MCP Documentation](https://modelcontextprotocol.io).
61 | 
62 | ## Contributing
63 | 
64 | Contributions are welcome! Please feel free to submit a Pull Request.
65 | 
66 | ## License
67 | 
68 | MIT License
69 | 
```

--------------------------------------------------------------------------------
/devenv.yaml:
--------------------------------------------------------------------------------

```yaml
1 | inputs:
2 |   nix2container:
3 |     url: github:nlewo/nix2container
4 |     inputs:
5 |       nixpkgs:
6 |         follows: nixpkgs
7 |   mk-shell-bin:
8 |     url: github:rrbutani/nix-mk-shell-bin
9 | 
```

--------------------------------------------------------------------------------
/src/main/java/net/experimentalworks/App.java:
--------------------------------------------------------------------------------

```java
 1 | package net.experimentalworks;
 2 | 
 3 | import io.modelcontextprotocol.server.transport.StdioServerTransport;
 4 | 
 5 | /** Hello world! */
 6 | public class App {
 7 | 
 8 |   public static void main(String[] args) {
 9 |     var server = new SteamGamesServer(new StdioServerTransport());
10 |     server.run().block();
11 |   }
12 | }
13 | 
```

--------------------------------------------------------------------------------
/.github/workflows/default.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Build Check
 2 | run-name: Push and PR checks
 3 | on:
 4 |   - push
 5 |   - pull_request
 6 | 
 7 | jobs:
 8 |   checks:
 9 |     runs-on: ubuntu-latest
10 |     steps:
11 |       - uses: actions/checkout@v4
12 |       - uses: ./.github/actions/setup-java
13 |       - run: mvn spotless:check
14 | 
15 |   build:
16 |     runs-on: ubuntu-latest
17 |     steps:
18 |       - uses: actions/checkout@v4
19 |       - uses: ./.github/actions/setup-java
20 |       - run: mvn compile
21 | 
```

--------------------------------------------------------------------------------
/src/test/java/net/experimentalworks/AppTest.java:
--------------------------------------------------------------------------------

```java
 1 | package net.experimentalworks;
 2 | 
 3 | import junit.framework.Test;
 4 | import junit.framework.TestCase;
 5 | import junit.framework.TestSuite;
 6 | 
 7 | /** Unit test for simple App. */
 8 | public class AppTest extends TestCase {
 9 |   /**
10 |    * Create the test case
11 |    *
12 |    * @param testName name of the test case
13 |    */
14 |   public AppTest(String testName) {
15 |     super(testName);
16 |   }
17 | 
18 |   /**
19 |    * @return the suite of tests being tested
20 |    */
21 |   public static Test suite() {
22 |     return new TestSuite(AppTest.class);
23 |   }
24 | 
25 |   /** Rigourous Test :-) */
26 |   public void testApp() {
27 |     assertTrue(true);
28 |   }
29 | }
30 | 
```

--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------

```dockerfile
 1 | # Build stage
 2 | FROM alpine:latest AS builder
 3 | 
 4 | # Add build arg
 5 | ARG VERSION=1.0-SNAPSHOT
 6 | 
 7 | # Install devenv
 8 | RUN apk add --no-cache openjdk21 maven
 9 | 
10 | # Copy project files
11 | WORKDIR /build
12 | COPY pom.xml .
13 | COPY src/ src/
14 | 
15 | # Build with version
16 | RUN mvn package -Dversion=$VERSION
17 | 
18 | # Find the jar with dependencies (most reliable approach)
19 | RUN mv $(find target -name "mcp-steam-*.jar") target/app.jar
20 | 
21 | # Runtime stage
22 | FROM alpine:latest AS mcp-server-steam
23 | 
24 | RUN apk add --no-cache openjdk21-jre
25 | WORKDIR /app
26 | COPY --from=builder /build/target/app.jar app.jar
27 | 
28 | CMD ["java", "-jar", "app.jar"]
29 | 
```

--------------------------------------------------------------------------------
/.github/actions/setup-java/action.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: 'Setup Java'
 2 | description: 'Sets up Java and Maven cache'
 3 | runs:
 4 |   using: "composite"
 5 |   steps:
 6 |     - uses: actions/setup-java@v4
 7 |       with:
 8 |         java-version: '21'
 9 |         distribution: 'temurin'
10 |         architecture: x64
11 | 
12 |     # Handle Maven cache separately with a more reliable key strategy
13 |     - shell: bash
14 |       id: cache-key
15 |       run: |
16 |         echo "timestamp=$(date -u +%Y%m%d%H%M%S)" >> $GITHUB_OUTPUT
17 |         echo "hash=$(echo ${{ hashFiles('**/pom.xml') }})" >> $GITHUB_OUTPUT
18 | 
19 |     - uses: actions/cache@v3
20 |       id: maven-cache
21 |       with:
22 |         path: ~/.m2/repository
23 |         key: ${{ runner.os }}-maven-${{ steps.cache-key.outputs.hash }}
24 |         restore-keys: |
25 |           ${{ runner.os }}-maven-
26 | 
```

--------------------------------------------------------------------------------
/src/main/java/net/experimentalworks/Game.java:
--------------------------------------------------------------------------------

```java
 1 | package net.experimentalworks;
 2 | 
 3 | import java.io.Serializable;
 4 | import java.util.Optional;
 5 | 
 6 | public class Game implements Serializable {
 7 | 
 8 |   private long appId;
 9 |   private String name;
10 |   private float playtimeForever;
11 |   private Optional<Float> playtime2weeks;
12 | 
13 |   public Game(long appId, String name, float playtimeForever) {
14 |     this.appId = appId;
15 |     this.name = name;
16 |     this.playtimeForever = playtimeForever;
17 |   }
18 | 
19 |   public Game(long appId, String name, float playtimeForever, float playtime2weeks) {
20 |     this.appId = appId;
21 |     this.name = name;
22 |     this.playtimeForever = playtimeForever;
23 |     this.playtime2weeks = Optional.of(playtime2weeks);
24 |   }
25 | 
26 |   public long getAppId() {
27 |     return appId;
28 |   }
29 | 
30 |   public String getName() {
31 |     return name;
32 |   }
33 | 
34 |   public float getPlaytimeForever() {
35 |     return playtimeForever;
36 |   }
37 | 
38 |   public Optional<Float> getPlaytime2weeks() {
39 |     return playtime2weeks;
40 |   }
41 | }
42 | 
```

--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------

```yaml
 1 | name: Release Docker Image
 2 | 
 3 | on:
 4 |   release:
 5 |     types: [published]
 6 | 
 7 | env:
 8 |   REGISTRY: ghcr.io
 9 |   IMAGE_NAME: dsp/mcp-server-steam
10 | 
11 | jobs:
12 |   build-and-push:
13 |     runs-on: ubuntu-latest
14 |     permissions:
15 |       contents: read
16 |       packages: write
17 | 
18 |     steps:
19 |       - name: Checkout repository
20 |         uses: actions/checkout@v4
21 | 
22 |       - uses: ./.github/actions/setup-java
23 | 
24 |       - name: Set version in pom.xml
25 |         run: |
26 |           VERSION=${GITHUB_REF#refs/tags/}
27 |           mvn versions:set -DnewVersion=$VERSION
28 | 
29 |       - name: Log in to GitHub Container Registry
30 |         uses: docker/login-action@v3
31 |         with:
32 |           registry: ${{ env.REGISTRY }}
33 |           username: ${{ github.actor }}
34 |           password: ${{ secrets.GITHUB_TOKEN }}
35 | 
36 |       - name: Set up Docker Buildx
37 |         uses: docker/setup-buildx-action@v3
38 | 
39 |       - name: Build and push Docker image
40 |         uses: docker/build-push-action@v5
41 |         with:
42 |           build-args: |
43 |             VERSION=${GITHUB_REF#refs/tags/}
44 |           context: .
45 |           push: true
46 |           tags: |
47 |             ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.ref_name }}
48 |             ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
49 |           cache-from: type=gha
50 |           cache-to: type=gha,mode=max
51 | 
```

--------------------------------------------------------------------------------
/src/main/java/net/experimentalworks/SteamGames.java:
--------------------------------------------------------------------------------

```java
 1 | package net.experimentalworks;
 2 | 
 3 | import java.util.List;
 4 | import java.util.stream.Collectors;
 5 | 
 6 | import com.lukaspradel.steamapi.core.exception.SteamApiException;
 7 | import com.lukaspradel.steamapi.data.json.ownedgames.GetOwnedGames;
 8 | import com.lukaspradel.steamapi.data.json.recentlyplayedgames.GetRecentlyPlayedGames;
 9 | import com.lukaspradel.steamapi.webapi.client.SteamWebApiClient;
10 | import com.lukaspradel.steamapi.webapi.request.GetOwnedGamesRequest;
11 | import com.lukaspradel.steamapi.webapi.request.GetRecentlyPlayedGamesRequest;
12 | import com.lukaspradel.steamapi.webapi.request.builders.SteamWebApiRequestFactory;
13 | 
14 | public class SteamGames {
15 | 
16 |   private final SteamWebApiClient client;
17 | 
18 |   public SteamGames(String apiKey) {
19 |     this.client = new SteamWebApiClient.SteamWebApiClientBuilder(apiKey).build();
20 |   }
21 | 
22 |   public GetOwnedGames getOwnedGames(String steamId) throws SteamApiException {
23 |     GetOwnedGamesRequest request =
24 |         SteamWebApiRequestFactory.createGetOwnedGamesRequest(steamId, true, true, List.of());
25 |     return client.processRequest(request);
26 |   }
27 | 
28 |   public GetRecentlyPlayedGames getRecentlyPlayedGames(String steamId) throws SteamApiException {
29 |     GetRecentlyPlayedGamesRequest request =
30 |         SteamWebApiRequestFactory.createGetRecentlyPlayedGamesRequest(steamId);
31 | 
32 |     return client.processRequest(request);
33 |   }
34 | 
35 |   public List<Game> getGames(String steamId) throws SteamApiException {
36 |     GetOwnedGames ownedGames = getOwnedGames(steamId);
37 |     return ownedGames.getResponse().getGames().stream()
38 |         .map(game -> new Game(game.getAppid(), game.getName(), game.getPlaytimeForever()))
39 |         .collect(Collectors.toList());
40 |   }
41 | 
42 |   public List<Game> getRecentlyGames(String steamId) throws SteamApiException {
43 |     GetRecentlyPlayedGames recentGames = getRecentlyPlayedGames(steamId);
44 |     return recentGames.getResponse().getGames().stream()
45 |         .map(
46 |             game ->
47 |                 new Game(
48 |                     game.getAppid(),
49 |                     game.getName(),
50 |                     game.getPlaytimeForever(),
51 |                     game.getPlaytime2weeks()))
52 |         .collect(Collectors.toList());
53 |   }
54 | }
55 | 
```

--------------------------------------------------------------------------------
/dependency-reduced-pom.xml:
--------------------------------------------------------------------------------

```
 1 | <?xml version="1.0" encoding="UTF-8"?>
 2 | <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/maven-v4_0_0.xsd">
 3 |   <modelVersion>4.0.0</modelVersion>
 4 |   <groupId>net.experimentalworks</groupId>
 5 |   <artifactId>mcp-steam</artifactId>
 6 |   <name>mcp-steam</name>
 7 |   <version>1.0-SNAPSHOT</version>
 8 |   <url>http://maven.apache.org</url>
 9 |   <build>
10 |     <plugins>
11 |       <plugin>
12 |         <artifactId>maven-compiler-plugin</artifactId>
13 |         <version>3.13.0</version>
14 |       </plugin>
15 |       <plugin>
16 |         <groupId>org.codehaus.mojo</groupId>
17 |         <artifactId>versions-maven-plugin</artifactId>
18 |         <version>2.18.0</version>
19 |       </plugin>
20 |       <plugin>
21 |         <groupId>com.diffplug.spotless</groupId>
22 |         <artifactId>spotless-maven-plugin</artifactId>
23 |         <version>${spotless.version}</version>
24 |         <configuration>
25 |           <java>
26 |             <googleJavaFormat>
27 |               <version>1.25.2</version>
28 |               <style>GOOGLE</style>
29 |             </googleJavaFormat>
30 |             <removeUnusedImports />
31 |             <importOrder>
32 |               <order>java,javax,org,com,</order>
33 |             </importOrder>
34 |             <trimTrailingWhitespace />
35 |             <endWithNewline />
36 |           </java>
37 |         </configuration>
38 |       </plugin>
39 |       <plugin>
40 |         <artifactId>maven-shade-plugin</artifactId>
41 |         <version>3.5.1</version>
42 |         <executions>
43 |           <execution>
44 |             <phase>package</phase>
45 |             <goals>
46 |               <goal>shade</goal>
47 |             </goals>
48 |             <configuration>
49 |               <filters>
50 |                 <filter>
51 |                   <artifact>*:*</artifact>
52 |                   <excludes>
53 |                     <exclude>module-info.class</exclude>
54 |                     <exclude>META-INF/*.SF</exclude>
55 |                     <exclude>META-INF/*.DSA</exclude>
56 |                     <exclude>META-INF/*.RSA</exclude>
57 |                     <exclude>META-INF/MANIFEST.MF</exclude>
58 |                     <exclude>META-INF/LICENSE</exclude>
59 |                     <exclude>META-INF/NOTICE</exclude>
60 |                     <exclude>META-INF/versions/9/module-info.class</exclude>
61 |                     <exclude>META-INF.versions.9.module-info</exclude>
62 |                   </excludes>
63 |                 </filter>
64 |               </filters>
65 |               <transformers>
66 |                 <transformer>
67 |                   <mainClass>net.experimentalworks.App</mainClass>
68 |                 </transformer>
69 |                 <transformer />
70 |                 <transformer />
71 |                 <transformer>
72 |                   <addHeader>false</addHeader>
73 |                 </transformer>
74 |               </transformers>
75 |             </configuration>
76 |           </execution>
77 |         </executions>
78 |       </plugin>
79 |     </plugins>
80 |   </build>
81 |   <dependencies>
82 |     <dependency>
83 |       <groupId>junit</groupId>
84 |       <artifactId>junit</artifactId>
85 |       <version>3.8.1</version>
86 |       <scope>test</scope>
87 |     </dependency>
88 |   </dependencies>
89 |   <properties>
90 |     <maven.compiler.release>19</maven.compiler.release>
91 |     <spotless.version>2.44.2</spotless.version>
92 |     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
93 |   </properties>
94 | </project>
95 | 
```

--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------

```
  1 | <?xml version="1.0"?>
  2 | <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/maven-v4_0_0.xsd">
  3 |   <modelVersion>4.0.0</modelVersion>
  4 |   <groupId>net.experimentalworks</groupId>
  5 |   <artifactId>mcp-steam</artifactId>
  6 |   <packaging>jar</packaging>
  7 |   <version>1.0-SNAPSHOT</version>
  8 |   <name>mcp-steam</name>
  9 |   <url>http://maven.apache.org</url>
 10 |   <properties>
 11 |     <maven.compiler.release>19</maven.compiler.release>
 12 |     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 13 |     <spotless.version>2.44.2</spotless.version>
 14 |   </properties>
 15 |   <dependencies>
 16 |     <dependency>
 17 |       <groupId>junit</groupId>
 18 |       <artifactId>junit</artifactId>
 19 |       <version>3.8.1</version>
 20 |       <scope>test</scope>
 21 |     </dependency>
 22 |     <dependency>
 23 |       <groupId>io.modelcontextprotocol.sdk</groupId>
 24 |       <artifactId>mcp</artifactId>
 25 |       <version>0.7.0</version>
 26 |     </dependency>
 27 |     <dependency>
 28 |       <groupId>io.projectreactor</groupId>
 29 |       <artifactId>reactor-core</artifactId>
 30 |       <version>3.7.3</version>
 31 |     </dependency>
 32 |     <dependency>
 33 |       <groupId>com.lukaspradel</groupId>
 34 |       <artifactId>steam-web-api</artifactId>
 35 |       <version>1.9.1</version>
 36 |     </dependency>
 37 |     <dependency>
 38 |       <groupId>org.json</groupId>
 39 |       <artifactId>json</artifactId>
 40 |       <version>20250107</version>
 41 |     </dependency>
 42 |   </dependencies>
 43 |   <build>
 44 |     <plugins>
 45 |       <plugin>
 46 |         <artifactId>maven-compiler-plugin</artifactId>
 47 |         <version>3.13.0</version>
 48 |       </plugin>
 49 |       <plugin>
 50 |         <groupId>org.codehaus.mojo</groupId>
 51 |         <artifactId>versions-maven-plugin</artifactId>
 52 |         <version>2.18.0</version>
 53 |       </plugin>
 54 |       <plugin>
 55 |         <groupId>com.diffplug.spotless</groupId>
 56 |         <artifactId>spotless-maven-plugin</artifactId>
 57 |         <version>${spotless.version}</version>
 58 |         <configuration>
 59 |           <java>
 60 |             <googleJavaFormat>
 61 |               <version>1.25.2</version>
 62 |               <style>GOOGLE</style>
 63 |             </googleJavaFormat>
 64 |             <removeUnusedImports/>
 65 |             <importOrder>
 66 |               <order>java,javax,org,com,</order>
 67 |             </importOrder>
 68 |             <trimTrailingWhitespace/>
 69 |             <endWithNewline/>
 70 |           </java>
 71 |         </configuration>
 72 |       </plugin>
 73 |       <plugin>
 74 |         <groupId>org.apache.maven.plugins</groupId>
 75 |         <artifactId>maven-shade-plugin</artifactId>
 76 |         <version>3.5.1</version>
 77 |         <executions>
 78 |           <execution>
 79 |             <phase>package</phase>
 80 |             <goals>
 81 |               <goal>shade</goal>
 82 |             </goals>
 83 |             <configuration>
 84 |               <filters>
 85 |                 <filter>
 86 |                   <artifact>*:*</artifact>
 87 |                   <excludes>
 88 |                     <exclude>module-info.class</exclude>
 89 |                     <exclude>META-INF/*.SF</exclude>
 90 |                     <exclude>META-INF/*.DSA</exclude>
 91 |                     <exclude>META-INF/*.RSA</exclude>
 92 |                     <exclude>META-INF/MANIFEST.MF</exclude>
 93 |                     <exclude>META-INF/LICENSE</exclude>
 94 |                     <exclude>META-INF/NOTICE</exclude>
 95 |                     <exclude>META-INF/versions/9/module-info.class</exclude>
 96 |                     <exclude>META-INF.versions.9.module-info</exclude>
 97 |                   </excludes>
 98 |                 </filter>
 99 |               </filters>
100 |               <transformers>
101 |                 <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
102 |                   <mainClass>net.experimentalworks.App</mainClass>
103 |                 </transformer>
104 |                 <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
105 |                 <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer"/>
106 |                 <transformer implementation="org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer">
107 |                   <addHeader>false</addHeader>
108 |                 </transformer>
109 |               </transformers>
110 |             </configuration>
111 |           </execution>
112 |         </executions>
113 |       </plugin>
114 |     </plugins>
115 |   </build>
116 | </project>
117 | 
```

--------------------------------------------------------------------------------
/src/main/java/net/experimentalworks/SteamGamesServer.java:
--------------------------------------------------------------------------------

```java
  1 | package net.experimentalworks;
  2 | 
  3 | import java.util.List;
  4 | import java.util.Map;
  5 | 
  6 | import org.json.JSONArray;
  7 | import org.json.JSONObject;
  8 | 
  9 | import io.modelcontextprotocol.server.McpAsyncServer;
 10 | import io.modelcontextprotocol.server.McpServer;
 11 | import io.modelcontextprotocol.server.McpServerFeatures;
 12 | import io.modelcontextprotocol.spec.McpSchema.CallToolResult;
 13 | import io.modelcontextprotocol.spec.McpSchema.ServerCapabilities;
 14 | import io.modelcontextprotocol.spec.McpSchema.TextContent;
 15 | import io.modelcontextprotocol.spec.McpSchema.Tool;
 16 | import io.modelcontextprotocol.spec.ServerMcpTransport;
 17 | import reactor.core.publisher.Mono;
 18 | 
 19 | public class SteamGamesServer {
 20 | 
 21 |   private static final String STEAM_API_KEY = System.getenv("STEAM_API_KEY");
 22 |   private static final String STEAM_ID = System.getenv("STEAM_ID");
 23 |   private static final String TOOL_PREFIX = SteamGamesServer.getenvOrDefault("TOOL_PREFIX", "");
 24 | 
 25 |   private final McpAsyncServer server;
 26 | 
 27 |   private static String getenvOrDefault(String key, String defaultValue) {
 28 |     String value = System.getenv(key);
 29 |     return value != null ? value : defaultValue;
 30 |   }
 31 | 
 32 |   public SteamGamesServer(ServerMcpTransport transport) {
 33 |     String version = getClass().getPackage().getImplementationVersion();
 34 |     if (version == null) {
 35 |       version = "1.0.0"; // Fallback version if not found
 36 |     }
 37 |     this.server =
 38 |         McpServer.async(transport)
 39 |             .serverInfo("steam-games", version)
 40 |             .capabilities(ServerCapabilities.builder().tools(true).logging().build())
 41 |             .build();
 42 |   }
 43 | 
 44 |   public Mono<Void> run() {
 45 |     return server
 46 |         .addTool(createGetGamesTool())
 47 |         .then(server.addTool(createGetRecentGamesTool()))
 48 |         .then(Mono.never());
 49 |   }
 50 | 
 51 |   private static McpServerFeatures.AsyncToolRegistration createGetGamesTool() {
 52 |     var schema =
 53 |         """
 54 |             {
 55 |               "type": "object",
 56 |               "properties": {}
 57 |             }
 58 |             """;
 59 | 
 60 |     var tool =
 61 |         new Tool(
 62 |             TOOL_PREFIX + "get-games",
 63 |             """
 64 |             Get a comprehensive list of all games owned by the specified Steam user, including their total playtime in minutes.
 65 |             This includes all games in their Steam library, both installed and uninstalled, free and purchased. For each game,
 66 |             returns details like the game name, AppID, total playtime, and whether they've played it recently. The data comes
 67 |             directly from Steam's official API using the provided Steam ID.
 68 |             NOTE: playtime is sent in minutes.
 69 |             """,
 70 |             schema);
 71 | 
 72 |     return new McpServerFeatures.AsyncToolRegistration(tool, args -> handleGetGames(args));
 73 |   }
 74 | 
 75 |   private static Mono<CallToolResult> handleGetGames(Map<String, Object> args) {
 76 |     return Mono.fromCallable(
 77 |         () -> {
 78 |           var steamGames = new SteamGames(STEAM_API_KEY);
 79 |           var games = steamGames.getGames(STEAM_ID);
 80 | 
 81 |           var json =
 82 |               new JSONObject()
 83 |                   .put("owner", STEAM_ID)
 84 |                   .put("description", "Played games by the given steam id")
 85 |                   .put("all_games", new JSONArray(games));
 86 | 
 87 |           return new CallToolResult(List.of(new TextContent(json.toString())), false);
 88 |         });
 89 |   }
 90 | 
 91 |   private static McpServerFeatures.AsyncToolRegistration createGetRecentGamesTool() {
 92 |     var schema =
 93 |         """
 94 |             {
 95 |               "type": "object",
 96 |               "properties": {}
 97 |             }
 98 |             """;
 99 | 
100 |     var tool =
101 |         new Tool(
102 |             TOOL_PREFIX + "get-recent-games",
103 |             """
104 |             Retrieve a list of recently played games for the specified Steam user, including playtime
105 |             details from the last 2 weeks. This tool fetches data directly from Steam's API using the
106 |             provided Steam ID and returns information like game names, AppIDs, and recent playtime
107 |             statistics in minutes. The results only include games that have been played in the recent time period,
108 |             making it useful for tracking current gaming activity and habits.
109 |             NOTE: playtime is sent in minutes.
110 |             """,
111 |             schema);
112 | 
113 |     return new McpServerFeatures.AsyncToolRegistration(tool, args -> handleGetRecentGames(args));
114 |   }
115 | 
116 |   private static Mono<CallToolResult> handleGetRecentGames(Map<String, Object> args) {
117 |     return Mono.fromCallable(
118 |         () -> {
119 |           var steamGames = new SteamGames(STEAM_API_KEY);
120 |           var games = steamGames.getRecentlyGames(STEAM_ID);
121 | 
122 |           var json =
123 |               new JSONObject()
124 |                   .put("owner", STEAM_ID)
125 |                   .put("description", "Recently played games by the given steam id")
126 |                   .put("recent_games", new JSONArray(games));
127 | 
128 |           return new CallToolResult(List.of(new TextContent(json.toString())), false);
129 |         });
130 |   }
131 | }
132 | 
```