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
21 changes: 20 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [v1.0.0] - 2025-01-01

### Added
- Full test coverage for all 10 endpoint modules (36 tools total)
- Added `test_steam_user.py` (6 tools)
- Added `test_user_stats.py` (6 tools)
- Added `test_steam_news.py` (1 tool)

### Changed
- Updated version to 1.0.0 across all files
- Changed development status from "Alpha" to "Production/Stable"

### Fixed
- Removed duplicate `_resolve_steam_id` methods in endpoint modules
- Consolidated to use base class implementation
- Affected files: steam_user.py, user_stats.py, player_service.py, steam_trading.py
- Cleaned up unused imports after code consolidation

## [v0.9.0] - 2025-12-12

### Added
Expand Down Expand Up @@ -237,7 +255,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Docker Compose configuration
- Comprehensive README with usage instructions

[Unreleased]: https://github.com/CodeKeanu/steam-mcp/compare/v0.9.0...HEAD
[Unreleased]: https://github.com/CodeKeanu/steam-mcp/compare/v1.0.0...HEAD
[v1.0.0]: https://github.com/CodeKeanu/steam-mcp/compare/v0.9.0...v1.0.0
[v0.9.0]: https://github.com/CodeKeanu/steam-mcp/compare/v0.8.0...v0.9.0
[v0.8.0]: https://github.com/CodeKeanu/steam-mcp/compare/v0.7.1...v0.8.0
[v0.7.1]: https://github.com/CodeKeanu/steam-mcp/compare/v0.7.0...v0.7.1
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "steam-mcp"
version = "0.8.0"
version = "1.0.0"
description = "Steam API integration via Model Context Protocol (MCP)"
readme = "README.md"
license = "MIT"
Expand All @@ -14,7 +14,7 @@ authors = [
]
keywords = ["steam", "mcp", "api", "model-context-protocol", "claude"]
classifiers = [
"Development Status :: 3 - Alpha",
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
Expand Down
23 changes: 0 additions & 23 deletions src/steam_mcp/endpoints/player_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from typing import Any

from steam_mcp.endpoints.base import BaseEndpoint, endpoint
from steam_mcp.utils.steam_id import normalize_steam_id, SteamIDError


# Default games to display in detailed output
Expand Down Expand Up @@ -318,28 +317,6 @@ async def get_steam_level(self, steam_id: str) -> str:

return f"Steam Level for {normalized_id}: {level} ({tier})"

async def _resolve_steam_id(self, steam_id: str) -> str:
"""
Resolve steam_id, handling 'me'/'my' shortcuts.

Returns:
Normalized SteamID64 or error message starting with "Error"
"""
# Handle "me" / "my" shortcuts
steam_id_lower = steam_id.strip().lower()
if steam_id_lower in ("me", "my", "myself", "mine"):
if not self.client.owner_steam_id:
return (
"Error: No owner Steam ID configured. "
"Set STEAM_USER_ID environment variable to use 'me'/'my' shortcuts."
)
return self.client.owner_steam_id

try:
return await normalize_steam_id(steam_id, self.client)
except SteamIDError as e:
return f"Error resolving Steam ID: {e}"

async def _fetch_games_raw(
self, steam_id: str, include_free: bool = True
) -> list[dict[str, Any]]:
Expand Down
22 changes: 0 additions & 22 deletions src/steam_mcp/endpoints/steam_trading.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,11 @@
from typing import Any

from steam_mcp.endpoints.base import BaseEndpoint, endpoint
from steam_mcp.utils.steam_id import normalize_steam_id, SteamIDError


class IEconService(BaseEndpoint):
"""Steam Economy Service API endpoints for trading and market data."""

async def _resolve_steam_id(self, steam_id: str) -> str:
"""
Resolve steam_id, handling 'me'/'my' shortcuts.

Returns:
Normalized SteamID64 or error message starting with "Error"
"""
steam_id_lower = steam_id.strip().lower()
if steam_id_lower in ("me", "my", "myself", "mine"):
if not self.client.owner_steam_id:
return (
"Error: No owner Steam ID configured. "
"Set STEAM_USER_ID environment variable to use 'me'/'my' shortcuts."
)
return self.client.owner_steam_id

try:
return await normalize_steam_id(steam_id, self.client)
except SteamIDError as e:
return f"Error resolving Steam ID: {e}"

@endpoint(
name="get_trade_offers",
description=(
Expand Down
21 changes: 0 additions & 21 deletions src/steam_mcp/endpoints/steam_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,6 @@
class ISteamUser(BaseEndpoint):
"""ISteamUser API endpoints for player identity and profile data."""

async def _resolve_steam_id(self, steam_id: str) -> str:
"""
Resolve steam_id, handling 'me'/'my' shortcuts.

Returns:
Normalized SteamID64 or error message starting with "Error"
"""
steam_id_lower = steam_id.strip().lower()
if steam_id_lower in ("me", "my", "myself", "mine"):
if not self.client.owner_steam_id:
return (
"Error: No owner Steam ID configured. "
"Set STEAM_USER_ID environment variable to use 'me'/'my' shortcuts."
)
return self.client.owner_steam_id

try:
return await normalize_steam_id(steam_id, self.client)
except SteamIDError as e:
return f"Error resolving Steam ID: {e}"

@endpoint(
name="get_my_steam_id",
description=(
Expand Down
17 changes: 0 additions & 17 deletions src/steam_mcp/endpoints/user_stats.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from typing import Any

from steam_mcp.endpoints.base import BaseEndpoint, endpoint
from steam_mcp.utils.steam_id import normalize_steam_id, SteamIDError


# Maximum achievements to display in detailed output
Expand Down Expand Up @@ -588,19 +587,3 @@ async def get_global_stats_for_game(
output.append(f" {stat_name}: {total_str}")

return "\n".join(output)

async def _resolve_steam_id(self, steam_id: str) -> str:
"""Resolve steam_id, handling 'me'/'my' shortcuts."""
steam_id_lower = steam_id.strip().lower()
if steam_id_lower in ("me", "my", "myself", "mine"):
if not self.client.owner_steam_id:
return (
"Error: No owner Steam ID configured. "
"Set STEAM_USER_ID environment variable to use 'me'/'my' shortcuts."
)
return self.client.owner_steam_id

try:
return await normalize_steam_id(steam_id, self.client)
except SteamIDError as e:
return f"Error resolving Steam ID: {e}"
2 changes: 1 addition & 1 deletion src/steam_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ async def run_server() -> None:
write_stream,
InitializationOptions(
server_name="steam-mcp-server",
server_version="0.1.0",
server_version="1.0.0",
capabilities=server.get_capabilities(
notification_options=NotificationOptions(),
experimental_capabilities={},
Expand Down
16 changes: 8 additions & 8 deletions tests/test_player_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ async def test_self_comparison_rejected(self, player_service, mock_client):
"""Friend ID matching user ID should be rejected."""
# Both resolve to same ID
with patch(
"steam_mcp.endpoints.player_service.normalize_steam_id",
"steam_mcp.endpoints.base.normalize_steam_id",
new_callable=AsyncMock,
return_value="76561198000000001",
):
Expand All @@ -58,7 +58,7 @@ async def test_self_comparison_rejected(self, player_service, mock_client):
async def test_invalid_steam_id_returns_error(self, player_service):
"""Invalid Steam ID should return error."""
with patch(
"steam_mcp.endpoints.player_service.normalize_steam_id",
"steam_mcp.endpoints.base.normalize_steam_id",
new_callable=AsyncMock,
side_effect=SteamIDError("Invalid Steam ID"),
):
Expand Down Expand Up @@ -90,7 +90,7 @@ async def test_finds_shared_unplayed_games(self, player_service, mock_client):
]

with patch(
"steam_mcp.endpoints.player_service.normalize_steam_id",
"steam_mcp.endpoints.base.normalize_steam_id",
new_callable=AsyncMock,
side_effect=["76561198000000001", "76561198000000002"],
):
Expand All @@ -115,7 +115,7 @@ async def test_no_unplayed_games_message(self, player_service, mock_client):
]

with patch(
"steam_mcp.endpoints.player_service.normalize_steam_id",
"steam_mcp.endpoints.base.normalize_steam_id",
new_callable=AsyncMock,
side_effect=["76561198000000001", "76561198000000002"],
):
Expand All @@ -141,7 +141,7 @@ async def test_private_profile_handled_gracefully(self, player_service, mock_cli
]

with patch(
"steam_mcp.endpoints.player_service.normalize_steam_id",
"steam_mcp.endpoints.base.normalize_steam_id",
new_callable=AsyncMock,
side_effect=["76561198000000001", "76561198000000002", "76561198000000003"],
):
Expand Down Expand Up @@ -176,7 +176,7 @@ async def test_multiple_friends_all_must_own(self, player_service, mock_client):
]

with patch(
"steam_mcp.endpoints.player_service.normalize_steam_id",
"steam_mcp.endpoints.base.normalize_steam_id",
new_callable=AsyncMock,
side_effect=["76561198000000001", "76561198000000002", "76561198000000003"],
):
Expand All @@ -203,7 +203,7 @@ async def test_all_friends_must_have_zero_playtime(self, player_service, mock_cl
]

with patch(
"steam_mcp.endpoints.player_service.normalize_steam_id",
"steam_mcp.endpoints.base.normalize_steam_id",
new_callable=AsyncMock,
side_effect=["76561198000000001", "76561198000000002", "76561198000000003"],
):
Expand All @@ -223,7 +223,7 @@ async def test_my_profile_private_returns_error(self, player_service, mock_clien
]

with patch(
"steam_mcp.endpoints.player_service.normalize_steam_id",
"steam_mcp.endpoints.base.normalize_steam_id",
new_callable=AsyncMock,
side_effect=["76561198000000001", "76561198000000002"],
):
Expand Down
Loading